k8s (十四) --- Kubernetes 存儲 之 StatefulSet 控制器

這篇博客主要探討StatefulSet如何通過Headless Service維持Pod的拓撲狀態

一、StatefulSets

StatefulSet 是用來管理有狀態應用的工作負載 API 對象。
StatefulSet 用來管理 Deployment 和擴展一組 Pod,並且能爲這些 Pod 提供序號和唯一性保證

和 Deployment 相同的是,StatefulSet 管理了基於相同容器定義的一組 Pod。但和 Deployment 不同的是,StatefulSet 爲它們的每個 Pod 維護了一個固定的 ID。這些 Pod 是基於相同的聲明來創建的,但是不能相互替換:無論怎麼調度,每個 Pod 都有一個永久不變的 ID。

StatefulSet 和其他控制器使用相同的工作模式。你在 StatefulSet 對象 中定義你期望的狀態,然後 StatefulSet 的 控制器 就會通過各種更新來達到那種你想要的狀態。

StatefulSets 對於需要滿足以下一個或多個需求的應用程序很有價值:

  • 穩定的、唯一的網絡標識符。
  • 穩定的、持久的存儲。
  • 有序的、優雅的部署和縮放。
  • 有序的、自動的滾動更新。

在上面,穩定意味着 Pod 調度或重調度的整個過程是有持久性的。如果應用程序不需要任何穩定的標識符或有序的部署、刪除或伸縮,則應該使用由一組無狀態的副本控制器提供的工作負載來部署應用程序,比如 Deployment 或者 ReplicaSet 可能更適用於您的無狀態應用部署需要。

給定 Pod 的存儲必須由 PersistentVolume 驅動 基於所請求的 storage class 來提供,或者由管理員預先提供。

刪除或者收縮 StatefulSet 並不會刪除它關聯的存儲卷。這樣做是爲了保證數據安全,它通常比自動清除 StatefulSet 所有相關的資源更有價值。

StatefulSet 當前需要 headless 服務 來負責 Pod 的網絡標識。您需要負責創建此服務。

當刪除 StatefulSets 時,StatefulSet 不提供任何終止 Pod 的保證。爲了實現 StatefulSet 中的 Pod 可以有序和優雅的終止,可以在刪除之前將 StatefulSet 縮放爲 0。
在默認 Pod 管理策略(OrderedReady) 時使用 滾動更新,可能進入需要 人工干預 才能修復的損壞狀態。

StatefulSet將應用狀態抽象成了兩種情況:

  • 拓撲狀態:應用實例必須按照某種順序啓動。新創建的Pod必須和原來Pod的網絡標識一樣
  • 存儲狀態:應用的多個實例分別綁定了不同存儲數據。

StatefulSet給所有的Pod進行了編號,編號規則是:$(statefulset名稱)-$(序號),從0開始。

Pod被刪除後重建,重建Pod的網絡標識也不會改變,Pod的拓撲狀態按照Pod的“名字+編號”的方式固定下來,並且爲每個Pod提供了一個固定且唯一的訪問入口,即Pod對應的DNS記錄,同時重建時保證每個pod掛載到原來的捲上。

二、StatefulSet通過Headless Service維持Pod的拓撲狀態

首先創建Headless service:

[root@server1 pv]# mkdir statefulset
[root@server1 pv]# cd statefulset/
[root@server1 statefulset]# vim service.yaml
[root@server1 statefulset]# cat service.yaml 
apiVersion: v1
kind: Service
metadata:
 name: nginx
 labels:
  app: nginx
spec:
 ports:
 - port: 80
   name: web
 clusterIP: None
 selector:
  app: nginx

創建服務:

[root@server1 statefulset]# kubectl apply -f service.yaml 
service/nginx created
[root@server1 statefulset]# kubectl get service
NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP   19d
myservice    ClusterIP   10.101.31.155   <none>        80/TCP    14d
nginx        ClusterIP   None            <none>        80/TCP    13s

可以看出創建了一個名爲nginx的無頭服務(Headless service)。

創建使用StatefulSet控制器的pod:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: web
spec:
 serviceName: "nginx"
 replicas: 2
 selector:
  matchLabels:
   app: nginx
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: nginx
     ports:
     - containerPort: 80
       name: web

[root@server1 statefulset]# 
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web created
[root@server1 statefulset]# 
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          31m
web-0                                     1/1     Running   0          29s
web-1                                     1/1     Running   0          26s

可以看出創建了兩個名爲web-0web-1的pod,service也會由兩個endpoint:
在這裏插入圖片描述
其中pod名稱中的0和1是pod的唯一標識。

當我們將replicas改爲1時,web-1將會被回收:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
更改:
 replicas: 1

[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          33m
web-0                                     1/1     Running   0          2m12s

而當我們將replicas改爲0時,所有pod將會被回收:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
更改:
 replicas: 0
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          34m

之後再次重建pod:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
更改:
 replicas: 2
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS              RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running             0          35m
web-0                                     0/1     ContainerCreating   0          2s
[root@server1 statefulset]# kubectl get pod
^[[3~NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          35m
web-0                                     1/1     Running   0          9s
web-1                                     1/1     Running   0          7s
[root@server1 statefulset]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          35m   10.244.1.81   server2   <none>           <none>
web-0                                     1/1     Running   0          28s   10.244.2.72   server3   <none>           <none>
web-1                                     1/1     Running   0          26s   10.244.1.83   server2   <none>           <none>

可以看出重新創建時是先創建web-0再創建web-1,而刪除的時候是先刪除web-1,再刪除web-0.

但是需要注意的是雖然名稱標識沒有變,但是pod的ip發生了變化。

現在可以直接訪問服務:

在這裏插入圖片描述

也可以解析地址:
在這裏插入圖片描述也可以使用nslookup web-1.nginx的方式解析和訪問到:

在這裏插入圖片描述
PV和PVC的設計,使得StatefulSet對存儲狀態的管理成爲了可能,現在爲以上這些pod添加存儲:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: web
spec:
 serviceName: "nginx"
 replicas: 2
 selector:
  matchLabels:
   app: nginx
 template:
  metadata:
   labels:
    app: nginx
  spec:
   containers:
   - name: nginx
     image: nginx
     ports:
     - containerPort: 80
       name: web
     volumeMounts:
       - name: www
         mountPath: /usr/share/nginx/html
 volumeClaimTemplates:
  - metadata:
     name: www
    spec:
     storageClassName: managed-nfs-storage			#分配器名稱
     accessModes:
     - ReadWriteOnce
     resources:
      requests:
       storage: 100Mi

[root@server1 statefulset]# 
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web created
[root@server1 statefulset]# kubectl get pod -o wide
NAME                                      READY   STATUS    RESTARTS   AGE   IP            NODE      NOMINATED NODE   READINESS GATES
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          85m   10.244.1.81   server2   <none>           <none>
test                                      1/1     Running   2          48m   10.244.2.73   server3   <none>           <none>
web-0                                     1/1     Running   0          25s   10.244.1.84   server2   <none>           <none>
web-1                                     1/1     Running   0          20s   10.244.2.74   server3   <none>           <none>

StatefulSet還會爲每一個Pod分配並創建一個同樣編號的PVC。這樣,kubernetes就可以通過Persistent Volume機制爲這個PVC綁定對應的PV,從而保證每一個Pod都擁有一個獨立的Volume:

[root@server1 statefulset]# kubectl get pvc
NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS          AGE
www-web-0   Bound    pvc-41713073-508d-4dcf-897e-dd0575a0945a   100Mi      RWO            managed-nfs-storage   2m30s
www-web-1   Bound    pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6   100Mi      RWO            managed-nfs-storage   2m25s
[root@server1 statefulset]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS          REASON   AGE
pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6   100Mi      RWO            Delete           Bound    default/www-web-1   managed-nfs-storage            2m28s
pvc-41713073-508d-4dcf-897e-dd0575a0945a   100Mi      RWO            Delete           Bound    default/www-web-0   managed-nfs-storage            2m33s

可以往這些存儲裏寫入測試頁面:

[root@server1 statefulset]# ls /nfs/
default-www-web-0-pvc-41713073-508d-4dcf-897e-dd0575a0945a
default-www-web-1-pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6
[root@server1 statefulset]# 
[root@server1 statefulset]# echo web-0 > /nfs/default-www-web-0-pvc-41713073-508d-4dcf-897e-dd0575a0945a/index.html
[root@server1 statefulset]# echo web-1 > /nfs/default-www-web-1-pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6/index.html

測試解析和訪問:

[root@server1 statefulset]# kubectl attach test -it

/ # nslookup nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      nginx
Address 1: 10.244.1.84 web-0.nginx.default.svc.cluster.local
Address 2: 10.244.2.74 10-244-2-74.myservice.default.svc.cluster.local

可以看出解析成功。

/ # curl nginx
web-1
/ # curl nginx
web-1
/ # curl nginx
web-0
/ # curl nginx
web-0
/ # curl nginx
web-1
/ # curl nginx
web-0
/ # curl web-1.nginx			#訪問特定pod
web-1
/ # curl web-0.nginx
web-0

訪問可以成功負載。

接下來我們將replicas設置爲3,再次進行測試:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
修改:
 replicas: 3

[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          90m
test                                      1/1     Running   4          53m
web-0                                     1/1     Running   0          5m14s
web-1                                     1/1     Running   0          5m9s
web-2                                     1/1     Running   0          9s

service由三個endpoint:
在這裏插入圖片描述對應的pv當然也會創建:

[root@server1 statefulset]# kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM               STORAGECLASS          REASON   AGE
pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6   100Mi      RWO            Delete           Bound    default/www-web-1   managed-nfs-storage            5m35s
pvc-41713073-508d-4dcf-897e-dd0575a0945a   100Mi      RWO            Delete           Bound    default/www-web-0   managed-nfs-storage            5m40s
pvc-c6e2ee56-02d8-4540-9dc7-b91926d34274   100Mi      RWO            Delete           Bound    default/www-web-2   managed-nfs-storage            35s

寫入測試頁面並訪問:

[root@server1 statefulset]# ls /nfs/
default-www-web-0-pvc-41713073-508d-4dcf-897e-dd0575a0945a
default-www-web-1-pvc-36ac2b3c-2f82-452e-ad39-bab2a51e67c6
default-www-web-2-pvc-c6e2ee56-02d8-4540-9dc7-b91926d34274
[root@server1 statefulset]# echo web-2 > /nfs/default-www-web-2-pvc-c6e2ee56-02d8-4540-9dc7-b91926d34274/index.html
[root@server1 statefulset]# 
[root@server1 statefulset]# kubectl attach test -it
Defaulting container name to test.
Use 'kubectl describe pod/test -n default' to see all of the containers in this pod.
If you don't see a command prompt, try pressing enter.
/ # nslookup web-2.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-2.nginx
Address 1: 10.244.1.85 web-2.nginx.default.svc.cluster.local
/ # curl web-2.nginx
web-2
/ # curl web-1.nginx
web-1
/ # curl web-0.nginx
web-0

可以看出成功解析且訪問。

而當我們將replicas改爲0後,StatefulSet會將pod從2 ,1,0有序刪除。

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
更改:
 replicas: 0
 
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured
[root@server1 statefulset]# kubectl get pod
NAME                                      READY   STATUS    RESTARTS   AGE
nfs-client-provisioner-6b66ddf664-zjvtv   1/1     Running   0          94m
test                                      1/1     Running   5          57m

刪除後進行pod的重建:

[root@server1 statefulset]# vim deployment.yaml 
[root@server1 statefulset]# cat deployment.yaml 
更改:
 replicas: 3
 
[root@server1 statefulset]# kubectl apply -f deployment.yaml 
statefulset.apps/web configured

重鍵後測試訪問:

[root@server1 statefulset]# kubectl attach test -it

/ # curl web-0.nginx
web-0
/ # curl web-1.nginx
web-1
/ # curl web-2.nginx
web-2

依然可以成功訪問,這就驗證了StatefulSet 控制器的特性。

最後將replicas改爲0刪除所有pod。

以上的一系列操作都是通過更改部署文件實現的,當然也可以使用命令的方式:

  • kubectl 彈縮

首先,想要彈縮的StatefulSet. 需先清楚是否能彈縮該應用.

$ kubectl get statefulsets <stateful-set-name>
  • 改變StatefulSet副本數量:
$ kubectl scale statefulsets <stateful-set-name> --replicas=<new-replicas>
  • 如果StatefulSet開始由 kubectl apply 或 kubectl create --save-config
    創建,更新StatefulSet manifests中的 .spec.replicas, 然後執行命令 kubectl apply:
$ kubectl apply -f <stateful-set-file-updated>
  • 也可以通過命令 kubectl edit 編輯該字段:
$ kubectl edit statefulsets <stateful-set-name>
  • 使用 kubectl patch:
$ kubectl patch statefulsets <stateful-set-name> -p '{"spec":{"replicas":<new-replicas>}}'
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章