Labelling Operator#

Build an operator that periodically reconciles all Deployments and adds a label to any with a certain annotation.

Warning

While you can build operators with kr8s we would recommend folks look at using kopf for building anything more complex than the below example.

Controller#

First we need to create a Python script called controller.py containing the controller code that uses kr8s.

This script runs a reconciliation loop that periodically lists all Deployments with kr8s.get(). It then checks the Deployment.annotations property and if it has the annotation pykube-test-operator it adds the label foo=bar using Deployment.label().

# controller.py
import time
import kr8s

def run():
    while True:
        for deploy in kr8s.get("deployments", namespace=kr8s.ALL):
            if 'pykube-test-operator' in deploy.annotations:
                deploy.label(foo="bar")
        time.sleep(15)

if __name__ == "__main__":
    run()
# controller.py
import asyncio
import kr8s

async def run():
    while True:
        for deploy in await kr8s.asyncio.get("deployments", namespace=kr8s.ALL):
            if 'pykube-test-operator' in deploy.annotations:
                await deploy.label(foo="bar")
        await asyncio.sleep(15)

if __name__ == "__main__":
    asyncio.run(run())

Packaging#

Now we can package our controller code in a container image.

# Dockerfile
FROM python:3.11

WORKDIR /usr/local/src

RUN pip install kr8s

COPY controller.py /usr/local/src/

CMD ["python3", "/usr/local/src/controller.py"]
$ docker build -t foo/labelling-operator:latest .
$ docker push foo/labelling-operator:latest

Deploying#

Then to deploy our operator we need to create a ServiceAccount with a ClusterRole for our controller to use to communicate with the Kubernetes API.

# rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: labelling-operator
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: labelling-operator
rules:
- apiGroups:
  - apps
  resources:
  - deployments
  verbs:
  - get
  - watch
  - list
  - update
  - patch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: labelling-operator
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: labelling-operator
subjects:
- kind: ServiceAccount
  name: labelling-operator
  namespace: default
$ kubectl apply -f rbac.yaml

Then create a deployment to run our controller container.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: labelling-operator
spec:
  replicas: 1
  selector:
    matchLabels:
      app: labelling-operator
  template:
    metadata:
      labels:
        app: labelling-operator
    spec:
      serviceAccountName: labelling-operator
      containers:
      - name: operator
        image: foo/labelling-operator:latest
$ kubectl apply -f deployment.yaml