Kubernetes Shell Operator by Flant demo
Flant have anounced pretty cool shell-operator project
How it works
Imagine that you have script (any bash, pwsh, python, whatever) that will be called on events you are looking for (aka pod created, namespace deleted, etc)
To do so, your script should return wanted events when ran with --config
argument
And whenever event will occur, script will be called with json passed as an argument
PowerShell-Operator
Here is full example for RBAC based cluster
First of all create namespace
apiVersion: v1
kind: Namespace
metadata:
name: oper
Create custom resource definition (suppose we want to create our own redis operator)
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
# name must match the spec fields below, and be in the form: <plural>.<group>
name: redis.redis.io
namespace: oper
spec:
# group name to use for REST API: /apis/<group>/<version>
group: redis.io
# list of versions supported by this CustomResourceDefinition
versions:
- name: v1
# Each version can be enabled/disabled by Served flag.
served: true
# One and only one version must be marked as the storage version.
storage: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
memory:
type: integer
replicas:
type: integer
# either Namespaced or Cluster
scope: Namespaced
names:
# plural name to be used in the URL: /apis/<group>/<version>/<plural>
plural: redis
# singular name to be used as an alias on the CLI and for display
singular: redis
# kind is normally the CamelCased singular type. Your resource manifests use this.
kind: Redis
Apply all RBAC related stuff
apiVersion: v1
kind: ServiceAccount
metadata:
name: oper
namespace: oper
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: oper
namespace: oper
rules:
- apiGroups:
- redis.io
resources:
- redis
verbs:
- get
- watch
- list
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oper
namespace: oper
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: oper
subjects:
- kind: ServiceAccount
name: oper
namespace: oper
Config map with our script
apiVersion: v1
kind: ConfigMap
metadata:
name: oper
namespace: oper
data:
entrypoint.sh: |
#!/usr/bin/env bash
# https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-linux?view=powershell-7.1#installation-via-direct-download---alpine-39-and-310
apk add --no-cache ca-certificates less ncurses-terminfo-base krb5-libs libgcc libintl libssl1.1 libstdc++ tzdata userspace-rcu zlib icu-libs curl
apk -X https://dl-cdn.alpinelinux.org/alpine/edge/main add --no-cache lttng-ust
curl -s -L https://github.com/PowerShell/PowerShell/releases/download/v7.1.3/powershell-7.1.3-linux-alpine-x64.tar.gz -o /tmp/powershell.tar.gz
mkdir -p /opt/microsoft/powershell/7
tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7
chmod +x /opt/microsoft/powershell/7/pwsh
ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh
# https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl
chmod +x kubectl
mv kubectl /usr/bin/
# https://github.com/flant/shell-operator/blob/master/Dockerfile#L40
exec /sbin/tini -- /shell-operator start
oper.ps1: |
#!/usr/bin/env pwsh
if ($args[0] -eq '--config') {
Write-Host '
configVersion: v1
kubernetes:
- apiVersion: redis.io/v1
kind: Redis
executeHookOnEvent:
- Added
- Modified
- Deleted
'
} else {
$items = Get-Content $env:BINDING_CONTEXT_PATH | ConvertFrom-Json
foreach($item in $items) {
$event = $item.watchEvent
$kind = $item.object.kind
$name = $item.object.metadata.name
$namespace = $item.object.metadata.namespace
$replicas = $item.object.spec.replicas
$memory = $item.object.spec.memory
Write-Host "$event $kind $name $namespace $replicas $memory"
if ($kind -eq "Redis") {
if ($event -eq "Deleted") {
kubectl -n $namespace delete deployment $name
}
if ($event -eq "Added") {
kubectl -n $namespace create deployment $name --image=redis
}
if ($event -eq "Modified") {
kubectl -n $namespace scale deployment $name --replicas=$replicas
}
}
}
}
Note that we are replacing entrypoint, installing powershell and using it instead of bash
Deploy shell operator itself
apiVersion: apps/v1
kind: Deployment
metadata:
name: oper
namespace: oper
labels:
app: oper
spec:
replicas: 1
selector:
matchLabels:
app: oper
template:
metadata:
labels:
app: oper
spec:
containers:
- name: oper
# image: oper
# imagePullPolicy: Never
image: flant/shell-operator:latest
command:
- /entrypoint.sh
imagePullPolicy: IfNotPresent
volumeMounts:
- name: oper
subPath: oper.ps1
mountPath: /hooks/oper.ps1
- name: oper
subPath: entrypoint.sh
mountPath: /entrypoint.sh
volumes:
- name: oper
configMap:
name: oper
defaultMode: 0755
And at the very end try to create our demo
apiVersion: redis.io/v1
kind: Redis
metadata:
name: demo
namespace: oper
spec:
replicas: 1
memory: 200
And if we look at our deployment logs we will see desired messages, all is left is to write the operator itself