This section provides information about deploying workloads with WaitForFirstConsumer
mode in a topology-aware environment for block volumes.
Prerequisites
Procedure
- Create a
StorageClass
with thevolumeBindingMode
parameter set toWaitForFirstConsumer
.kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: topology-aware-standard provisioner: csi.vsphere.vmware.com volumeBindingMode: WaitForFirstConsumer
- Create an application to use the
StorageClass
created previously.Instead of creating a volume immediately, the
WaitForFirstConsumer
setting instructs the volume provisioner to wait until a pod using the associated PVC runs through scheduling. In contrast with theImmediate
volume binding mode, when theWaitForFirstConsumer
setting is used, the Kubernetes scheduler drives the decision of which failure domain to use for volume provisioning using the pod policies.apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: replicas: 2 selector: matchLabels: app: nginx serviceName: nginx template: metadata: labels: app: nginx spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: topology.csi.vmware.com/k8s-zone operator: In values: - zone-a - zone-b podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - nginx topologyKey: topology.csi.vmware.com/k8s-zone containers: - name: nginx image: gcr.io/google_containers/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html - name: logs mountPath: /logs volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] storageClassName: topology-aware-standard resources: requests: storage: 2Gi - metadata: name: logs spec: accessModes: [ "ReadWriteOnce" ] storageClassName: topology-aware-standard resources: requests: storage: 1Gi
- Verify that the statefulset is in the Running state and check that the pods are evenly distributed among the
zone-a
andzone-b
.$ kubectl get statefulset NAME READY AGE web 2/2 3m51s $ kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 4m40s 10.244.3.21 k8s-node-2 <none> <none> web-1 1/1 Running 0 4m12s 10.244.4.25 k8s-node-1 <none> <none> $ kubectl get node k8s-node-1 k8s-node-2 --show-labels NAME STATUS ROLES AGE VERSION LABELS k8s-node-1 Ready <none> 2d v1.21.1 topology.csi.vmware.com/k8s-region=region-1,topology.csi.vmware.com/k8s-zone=zone-a k8s-node-2 Ready <none> 2d v1.21.1 topology.csi.vmware.com/k8s-region=region-1,topology.csi.vmware.com/k8s-zone=zone-b
Notice that the PV node affinity rules include at least one domain withinzone-a
orzone-b
depending on whether the selected datastore is local or shared across zones.$ kubectl get pv -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.claimRef.name}{"\t"}{.spec.nodeAffinity}{"\n"}{end}' pvc-2253dc52-a9ed-11e9-b26e-005056a04307 www-web-0 map[required:map[nodeSelectorTerms:[map[matchExpressions:[map[key:topology.csi.vmware.com/k8s-region operator:In values:[region-1]] map[operator:In values:[zone-b] key:topology.csi.vmware.com/k8s-zone]]]]]] pvc-22575240-a9ed-11e9-b26e-005056a04307 logs-web-0 map[required:map[nodeSelectorTerms:[map[matchExpressions:[map[key:topology.csi.vmware.com/k8s-zone operator:In values:[zone-b]] map[key:topology.csi.vmware.com/k8s-region operator:In values:[region-1]]]]]]] pvc-3c963150-a9ed-11e9-b26e-005056a04307 www-web-1 map[required:map[nodeSelectorTerms:[map[matchExpressions:[map[key:topology.csi.vmware.com/k8s-zone operator:In values:[zone-a]] map[operator:In values:[region-1] key:topology.csi.vmware.com/k8s-region]]]]]] pvc-3c98978f-a9ed-11e9-b26e-005056a04307 logs-web-1 map[required:map[nodeSelectorTerms:[map[matchExpressions:[map[key:topology.csi.vmware.com/k8s-zone operator:In values:[zone-a]] map[key:topology.csi.vmware.com/k8s-region operator:In values:[region-1]]]]]]]