--- tags: k8s-fundamentals --- # StatefulSets https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ ## What are the differences between StatefulSets and Deployments https://www.magalix.com/blog/kubernetes-statefulsets-101-state-of-the-pods https://stackoverflow.com/questions/41583672/kubernetes-deployments-vs-statefulsets https://medium.com/stakater/k8s-deployments-vs-statefulsets-vs-daemonsets-60582f0c62d4 In short, every replica of a stateful set will have its own state, and each of the pods will be creating its own PVC(Persistent Volume Claim). So a statefulset with 3 replicas will create 3 pods, each having its own Volume, so total 3 PVCs. ## StatefulSet try-out on vShpere ### Create storageclass if needed To make things easier, I am using dynamci provisioning for my volumes. So, I have my storageclass created before hand. ### (Optional) Create ServiceAccount and Rolebinding ``` apiVersion: v1 kind: ServiceAccount metadata: name: statefulset-sa --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: statefulset-cr rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - vmware-system-privileged --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: statefulset-rb roleRef: kind: ClusterRole name: statefulset-cr apiGroup: rbac.authorization.k8s.io subjects: # Authorize specific service accounts: - kind: ServiceAccount name: statefulset-sa namespace: default ``` ### Create sample nginx statefulset ``` apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: serviceAccountName: statefulset-sa containers: - name: nginx image: k8s.gcr.io/nginx-slim:0.8 ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: storageClassName: statefulset-storageclass accessModes: [ "ReadWriteOnce" ] # vSphere only support ReadWriteOnce resources: requests: storage: 1Gi ``` After apply above yaml, you will see the output like this: ``` root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl get statefulset,pod,pv,pvc,storageclass NAME READY AGE statefulset.apps/web 2/2 13m NAME READY STATUS RESTARTS AGE pod/web-0 1/1 Running 0 13m pod/web-1 1/1 Running 0 13m NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO Delete Bound default/www-web-1 gc-storage-profile 13m persistentvolume/pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO Delete Bound default/www-web-0 gc-storage-profile 13m NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO gc-storage-profile 13m persistentvolumeclaim/www-web-1 Bound pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO gc-storage-profile 13m NAME PROVISIONER RECLAIMPOLICY VOLUMEBINDINGMODE ALLOWVOLUMEEXPANSION AGE storageclass.storage.k8s.io/gc-storage-profile csi.vsphere.vmware.com Delete Immediate false 10h ``` * Two pods, `web-0` and `web-1` got created in order. The creation order is mentioned in the [doc](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies) * Two PVs and PVCs got created for each individule pod ### Scale the statefulset #### Scale up to 3 replicas ``` root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl scale statefulset web --replicas=3 root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl get pods,pvc,pv NAME READY STATUS RESTARTS AGE pod/kuard 1/1 Running 0 26h pod/web-0 1/1 Running 0 17h pod/web-1 1/1 Running 0 17h pod/web-2 1/1 Running 0 50s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-1 Bound pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-2 Bound pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO gc-storage-profile 50s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO Delete Bound default/www-web-1 gc-storage-profile 17h persistentvolume/pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO Delete Bound default/www-web-2 gc-storage-profile 45s persistentvolume/pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO Delete Bound default/www-web-0 gc-storage-profile 17h ``` #### Scale down to 0 replicas ``` root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl scale statefulset web --replicas=0 root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl get pods,pvc,pv NAME READY STATUS RESTARTS AGE pod/kuard 1/1 Running 0 26h NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-1 Bound pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-2 Bound pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO gc-storage-profile 3m19s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO Delete Bound default/www-web-1 gc-storage-profile 17h persistentvolume/pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO Delete Bound default/www-web-2 gc-storage-profile 3m14s persistentvolume/pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO Delete Bound default/www-web-0 gc-storage-profile 17h ``` * The PVs and PVCs were not deleted automatically. This is also a expected behavior mentioned in [doc](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#limitations) #### Scale up to 3 replicas again ``` root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl scale statefulset web --replicas=3 root@42143908e772adcf6eec02e7bfc59758 [ ~/statefull-set-test ]# kubectl get pods,pvc,pv NAME READY STATUS RESTARTS AGE pod/kuard 1/1 Running 0 26h pod/web-0 1/1 Running 0 84s pod/web-1 1/1 Running 0 72s pod/web-2 1/1 Running 0 14s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-1 Bound pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO gc-storage-profile 17h persistentvolumeclaim/www-web-2 Bound pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO gc-storage-profile 7m13s NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-26f864a9-803f-47e2-ba06-c03426888bd7 1Gi RWO Delete Bound default/www-web-1 gc-storage-profile 17h persistentvolume/pvc-3588dcb0-ce2e-4e1f-9938-4d4b35d5252b 1Gi RWO Delete Bound default/www-web-2 gc-storage-profile 7m8s persistentvolume/pvc-a405fce4-81cc-4739-bf43-7aefb3831384 1Gi RWO Delete Bound default/www-web-0 gc-storage-profile 17h ``` * The PVs and PVCs will not be recreated, they are reused ### Access to the pods behind statefulset ``` root@42143908e772adcf6eec02e7bfc59758 [ ~ ]# kubectl get svc nginx -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx ClusterIP None <none> 80/TCP 17h app=nginx root@42143908e772adcf6eec02e7bfc59758 [ ~ ]# kubectl get endpoints nginx -o wide NAME ENDPOINTS AGE nginx 192.0.160.10:80,192.0.160.11:80,192.0.160.9:80 17h root@42143908e772adcf6eec02e7bfc59758 [ ~ ]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES web-0 1/1 Running 0 29m 192.0.160.9 test-cluster-e2e-script-workers-hzggp-79ff5fb574-2z8nd <none> <none> web-1 1/1 Running 0 29m 192.0.160.10 test-cluster-e2e-script-workers-hzggp-79ff5fb574-2z8nd <none> <none> web-2 1/1 Running 0 28m 192.0.160.11 test-cluster-e2e-script-workers-hzggp-79ff5fb574-2z8nd <none> <none> ``` #### Write some dummy data to volume ``` for i in 0 1 2; do kubectl exec web-$i -- sh -c 'echo $(hostname) > /usr/share/nginx/html/index.html'; done for i in 0 1 2; do kubectl exec -it web-$i -- curl localhost; done web-0 web-1 web-2 ``` #### Create a wrapper LB service to expose the statefulset https://itnext.io/exposing-statefulsets-in-kubernetes-698730fb92a1 ``` apiVersion: v1 kind: Service metadata: name: nginx-lb-svc labels: app: nginx spec: type: LoadBalancer ports: - port: 80 targetPort: 80 protocol: TCP selector: app: nginx ``` #### Try access the service from curl It will randomly send the traffic to the pod in statefulset, and each pod has different state ``` root@423ff1d6680e6d6469f3f001313a0791 [ ~/statefulset-test ]# curl http://192.168.123.3:80 web-0 root@423ff1d6680e6d6469f3f001313a0791 [ ~/statefulset-test ]# curl http://192.168.123.3:80 web-0 root@423ff1d6680e6d6469f3f001313a0791 [ ~/statefulset-test ]# curl http://192.168.123.3:80 web-1 root@423ff1d6680e6d6469f3f001313a0791 [ ~/statefulset-test ]# curl http://192.168.123.3:80 web-2 ```