Post

Enhance Developer Experience with Kyverno Rules

01-kyverno

I want to share some notes on how Kyverno can make things easier for developers.

Think about your developer teams apply YAML files to your Kubernetes clusters every day. Sometimes, you need to add or change a few things in those files automatically. Like:

  • Adding labels to all deployments
  • Injecting environment variables into containers
  • Adding annotations for monitoring

Developers don’t have to worry about every little detail. Some of this can be handled automatically, which makes their job simpler and reduces mistakes.

With Kyverno mutate rules, you can automatically add or update missing info when resources are deployed. It helps keep things correct without extra work for you or your team.

If you’re not very familiar with Kyverno, I highly recommend reading How Kyverno Works. It gives a clear overview of how Kyverno operates and helps you understand what’s happening behind the scenes.

Installing Kyverno

First, let’s deploy Kyverno to our Kubernetes cluster.

You can find detailed installation methods on the Install Kyverno using Helm.

Since I want to try it locally, I’ll use the standalone installation method. This is simple for testing on your local cluster.

Here is the command to install Kyverno standalone:

1
2
3
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno --create-namespace

Apply the Kyverno Policy

To start, let’s create and apply a Kyverno ClusterPolicy that will automatically add a label to every Pod created in the cluster.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
kubectl apply -f - <<EOF
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-hello-label
spec:
  rules:
    - name: add-label-to-pods
      match:
        resources:
          kinds:
            - Pod
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              hello: kyverno
EOF

This policy ensures that every Pod deployed will automatically receive the label hello=kyverno without requiring any manual edits to the Pod manifests.

Next, let’s create a basic Nginx Pod named example-pod Pod to test this policy.

1
2
3
4
5
6
7
8
9
10
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  containers:
    - name: nginx
      image: nginx
EOF

Finally, to confirm that the label was added successfully, run this command to check the labels on your Pod:

1
2
3
4
kubectl get pod example-pod --show-labels
                                                                                                                                               
NAME          READY   STATUS    RESTARTS   AGE    LABELS
example-pod   1/1     Running   0          4m5s   hello=kyverno

You should see the label hello=kyverno listed among the Pod’s labels. This demonstrates how Kyverno’s mutate rules help you automate configuration changes and keep your Kubernetes resources consistent without extra manual work.

A More Practical Example

In many teams, developers don’t need to know all the low-level details about infrastructure. For example, let’s say you have deployed Ceph-CSI to your Kubernetes cluster. Developers might want to create static PersistentVolumes on their own.

Here’s a sample PV that they might write:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: v1
kind: PersistentVolume
metadata:
  name: myvolume
spec:
  accessModes:
    - ReadWriteMany
  capacity:
    storage: 10Gi
  csi:
    driver: cephfs.csi.ceph.com
    nodeStageSecretRef:
      name: csi-cephfs-secret
      namespace: csi-system
    volumeAttributes:
      fsName: multi-zone
      staticVolume: "true"
      monitors: 10.10.10.1
      clusterID: abc123456
      rootPath: /volumes/myvolume/id-001
    volumeHandle: /volumes/myvolume/id-001
  persistentVolumeReclaimPolicy: Retain
  volumeMode: Filesystem

There are a lot of parameters here. What should we put for monitors and clusterID ? Honestly, they shouldn’t need to. These are values that can be added automatically.

With Kyverno, we can create a policy that adds these values when:

  • The CSI driver is cephfs.csi.ceph.com
  • The staticVolume field is set to “true”

Here is a Kyverno ClusterPolicy that does this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: enrich-cephfs-pv
spec:
  rules:
    - name: enrich-static-cephfs-pv
      match:
        resources:
          kinds:
            - PersistentVolume
      preconditions:
        all:
          - key: "{{ request.object.spec.csi.driver }}"
            operator: Equals
            value: cephfs.csi.ceph.com
          - key: "{{ request.object.spec.csi.volumeAttributes.staticVolume }}"
            operator: Equals
            value: "true"
      mutate:
        patchStrategicMerge:
          spec:
            csi:
              volumeAttributes:
                +(monitors): 10.30.40.50
                +(clusterID): def789000
                +(fsName): multi-zone
                +(rootPath): "{{ request.object.spec.csi.volumeHandle }}"
              +(nodeStageSecretRef):
                name: csi-cephfs-secret
                namespace: csi-system
            +(persistentVolumeReclaimPolicy): Retain
            +(volumeMode): Filesystem

Let’s explain it simply:

  • This policy only works for PersistentVolume resources.
  • It checks two things:
    • The driver must be cephfs.csi.ceph.com
    • The volume must be marked as static ( staticVolume: "true" )
  • If both are true, Kyverno will automatically add the parameters like monitors and clusterID values — but only if they are not already set.

In Kyverno, you can use +(key): value syntax to add a field only if it doesn’t already exist. This is useful when you want to set default values without overwriting what the developer may have already defined.

At the end of the day, when a developer applies a PersistentVolume like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: PersistentVolume
metadata:
  name: myvolume
spec:
  accessModes:
    - ReadWriteMany
  capacity:
    storage: 10Gi
  csi:
    driver: cephfs.csi.ceph.com
    volumeAttributes:
      staticVolume: "true"
    volumeHandle: /volumes/myvolume/id-001

The Kyverno policy will fill in the missing parts and make sure the resource is created like the example above. All needed fields will be added automatically. This way, developers don’t have to fill in extra fields and can focus on their tasks, and the platform will handle everything else.