A member of a Slack channel I frequent recently asked how you go about patching existing Kubernetes resources. After responding, I thought that others might benefit from the same information. In today’s example, we’re going to resize a PersistentVolumeClaim
(PVC).
We’ll start off with a dynamic StorageClass
.
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: dynamic
provisioner: kubernetes.io/no-provisioner
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
Next, we’ll create a PersistentVolume
(PV) based on the new StorageClass
.
apiVersion: v1
kind: PersistentVolume
metadata:
name: dynamic-pv
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 500Mi
local:
path: /tmp/vol1
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- kind-control-plane
storageClassName: dynamic
volumeMode: Filesystem
And finally, we’ll create a PersistentVolumeClaim
(PVC) that our Pod can use to claim some storage.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dynamic-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Mi
storageClassName: dynamic
volumeMode: Filesystem
If we check the status of our three new resources we should see that all three have been created and the PVC is in a Pending state.
$ k get sc NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE dynamic kubernetes.io/no-provisioner Retain WaitForFirstConsumer true 83s standard (default) rancher.io/local-path Delete WaitForFirstConsumer false 64d $ k get pv NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE dynamic-pv 500Mi RWO Retain Available dynamic 12s $ k get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE dynamic-pvc Pending dynamic 10s
The PVC is Pending because our binding mode is WaitForFirstConsumer
and we haven’t created a Pod yet to consume it. Let’s fix that.
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
volumeMounts:
- name: data
mountPath: /var/www/html
volumes:
- name: data
persistentVolumeClaim:
claimName: dynamic-pvc
dnsPolicy: ClusterFirst
restartPolicy: Always
Now if we check the status of our PVC we can see that it’s bound to our PV.
k get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE dynamic-pvc Bound dynamic-pv 500Mi RWO dynamic 3m25s
Now we can finally get to the meat of this post. In order to patch a resource, you need to pass the JSON path. It can look a little ugly, but this one isn’t so bad. First, let’s take a look at the PVC in JSON format.
Since 1.19 include the new managedFields
section which, imho, provides a lot of bloat that the average user doesn’t need to know or care about, I’m excluding that from this snippet.
"spec": {
"accessModes": [
"ReadWriteOnce"
],
"resources": {
"requests": {
"storage": "100Mi"
}
},
"storageClassName": "dynamic",
"volumeMode": "Filesystem",
"volumeName": "dynamic-pv"
},
"status": {
"accessModes": [
"ReadWriteOnce"
],
"capacity": {
"storage": "500Mi"
},
"phase": "Bound"
}
If we further strip everything else we don’t need, this is what we’re left with.
"spec": {
"resources": {
"requests": {
"storage": "100Mi"
}
},
},
So we really only have four steps to our data. The patch command is:
k patch <resource> -p <command>
For our example, that will start off as k patch pvc dynamic-pvc
.
Now we turn the JSON path into a one-line command with our new value.
'{"spec":{"resources":{"requests":{"storage":"200Mi"}}}}'
The full command is:
$ k patch pvc dynamic-pvc -p '{"spec":{"resources":{"requests":{"storage":"200Mi"}}}}'
persistentvolumeclaim/dynamic-pvc patched
If you do a describe on the PVC you won’t notice anything special.
$ k describe pvc dynamic-pvc Name: dynamic-pvc Namespace: dynamic StorageClass: dynamic Status: Bound Volume: dynamic-pv Labels: <none> Annotations: pv.kubernetes.io/bind-completed: yes pv.kubernetes.io/bound-by-controller: yes Finalizers: [kubernetes.io/pvc-protection] Capacity: 500Mi Access Modes: RWO VolumeMode: Filesystem Mounted By: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal WaitForFirstConsumer 2m33s (x9 over 4m30s) persistentvolume-controller waiting for first consumer to be created before binding
Just like a Deployment
, you can also record the actions taken by your patches. Use the same --record
flag. Let’s patch the PVC again.
$ k patch pvc dynamic-pvc -p '{"spec":{"resources":{"requests":{"storage":"275Mi"}}}}' --record persistentvolumeclaim/dynamic-pvc patched
Now we see an annotation has been added noting our action.
$ k describe pvc dynamic-pvc Name: dynamic-pvc Namespace: dynamic StorageClass: dynamic Status: Bound Volume: dynamic-pv Labels: <none> Annotations: kubernetes.io/change-cause: kubectl patch pvc dynamic-pvc --patch={"spec":{"resources":{"requests":{"storage":"275Mi"}}}} --record=true
There you go. Now you can use the same pattern to patch other resources throughout your Kubernetes clusters.