1. 程式人生 > 其它 >【Kubernetes系列十】Kubernetes StatefulSet控制器

【Kubernetes系列十】Kubernetes StatefulSet控制器

1. StatefulSet介紹

前面使用Deployment建立的Pod是無狀態的,當掛載了volume之後,如果該Pod掛了,Replication Controller會再啟動一個Pod來保證可用性,但是由於Pod是無狀態的,pod掛了就會和之前的Volume的關係斷開,新建立的Pod無法找到之前的Pod。但是對於使用者來說,他們對底層的Pod掛了是沒有感知的,但是當Pod掛了之後就無法再使用之前掛載的儲存卷。為了解決這一問題,就引入了StatefulSet用於保留Pod的狀態資訊。

StatefulSet是Pod資源控制器的一種實現,用於部署和擴充套件有狀態應用的Pod資源,確保它們的執行順序及每個Pod資源的唯一性。其應用場景包括:
穩定的持久化儲存,即Pod重新排程後還是能訪問到相同的持久化資料,基於PVC來實現。
穩定的網路標識,即Pod重新排程後其PodName和HostName不變,基於Headless Service(即沒有Cluster IP的Service)來實現
有序部署,有序擴充套件,即Pod是有順序的,在部署或者擴充套件的時候要依據定義的順序依次進行(即從0到N-1,在下一個Pod執行之前的所有之前的Pod必須都是Running和Ready狀態),基於init Containers來實現
有序收縮,有序刪除(即從N-1到0)

StatefulSet由以下幾個部分組成:
用於定義網路標誌(DNS domain)和Headless Service
用於建立PersistentVolumes和VolumeClaimTemplates
定義具體應用的StatefulSet

StatefulSet中的每個Pod的DNS格式為statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local,其中
serviceName:為Headless Service的名字
0..N-1:為Pod所在的序號,從0開始到N-1
statefulSetName:為StatefulSet的名字
namespace:為服務所在的namaspace,Headless Service和StatefulSet必須在相同的namespace
.cluster.local:為Cluster Domain

疑問解惑:
(1) 為什麼要有headless?
在Deployment中,每一個pod是沒有名稱,是隨機字串,是無序的。而statefulSet中是要求有序的,每一個Pod的名稱必須是固定的。當節點掛了,重建之後的識別符號是不變的,每一個節點的節點名稱是不會改變的。Pod名稱是作為Pod識別的唯一識別符號,必須保證其識別符號的穩定並且唯一。
為了實現識別符號的穩定,這時候就需要一個headless service解析直達到Pod,還需要給Pod配置一個唯一的名稱。

(2) 為什麼要有volumeClainTemplate?
大部分有狀態副本集都會用到持久儲存,比如分散式系統來說,由於資料是不一樣的,每個節點都需要自己專用的儲存節點。而在Deployment中Pod模板中建立的儲存卷是一個共享的儲存卷,多個Pod使用同一個儲存卷,而statefulSet定義中的每一個Pod都不能使用同一個儲存卷,由此基於Pod模板建立Pod是不適應的,這就需要引入volumeClainTemplate,當在使用StatefulSet建立Pod時,會自動生成一個PVC,從而請求繫結一個PV,從而有自己專用的儲存卷。

2. StatefulSet定義

在建立StatefulSet之前需要準備的東西,建立順序非常關鍵,如下

a、Volume
b、Persistent Volume
b、Persistent Volume Clain
d、Service
e、StatefulSet
Volume可以有很多中型別,比如nfs、gluster等,下面使用nfs

statefulSet欄位說明:

~]# kubectl explain statefulset.spec
podManagementPolicy    <string>    #Pod管理策略
replicas    <integer>    #Pod副本數量
revisionHistoryLimit    <integer>    #歷史版本限制
selector    <Object> -required-    #標籤選擇器,根據標籤選擇管理的Pod資源;必選欄位
serviceName    <string> -required-    #服務名稱,必選欄位
template    <Object> -required-    #模板,定義pod資源,必選欄位
updateStrategy    <Object>    #更新策略
volumeClaimTemplates    <[]Object>    #儲存卷申請模板,列表物件形式

示列:清單定義StatefulSet
通過上面的描述,下面示例定義StatefulSet資源,在定義之前首先得準備PV資源物件。這裡同樣使用NFS作為後端儲存。
(1) 準備NFS

~]# mkdir /data/volumes/v{1..5} -p
~]# vim /etc/exports
/data/volumes/v1  10.157.27.0/24(rw,no_root_squash)
/data/volumes/v2  10.157.27.0/24(rw,no_root_squash)
/data/volumes/v3  10.157.27.0/24(rw,no_root_squash)
/data/volumes/v4  10.157.27.0/24(rw,no_root_squash)
/data/volumes/v5  10.157.27.0/24(rw,no_root_squash)
~]# exportfs -arv
exporting 10.157.27.0/24:/data/volumes/v5
exporting 10.157.27.0/24:/data/volumes/v4
exporting 10.157.27.0/24:/data/volumes/v3
exporting 10.157.27.0/24:/data/volumes/v2
exporting 10.157.27.0/24:/data/volumes/v1

~]# showmount -e
Export list for storage:
/data/volumes/v5 10.157.27.0/24
/data/volumes/v4 10.157.27.0/24
/data/volumes/v3 10.157.27.0/24
/data/volumes/v2 10.157.27.0/24
/data/volumes/v1 10.157.27.0/24

(2) 建立pv
這裡建立5個PV,儲存大小各不相等,是否可讀也不相同,這裡新建立一個目錄用於存放statefulset所有的資源清單檔案等

~]# mkdir statefulset && cd statefulset
~]# vim pv-nfs.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-001
  labels:
    name: pv001
spec:
  nfs:
    path: /data/volumes/v1
    server: 10.157.27.114
    readOnly: false 
  accessModes: ["ReadWriteOnce","ReadWriteMany"]
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-002
  labels:
    name: pv002
spec:
  nfs:
    path: /data/volumes/v2
    server: 10.157.27.114
    readOnly: false 
  accessModes: ["ReadWriteOnce"]
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-003
  labels:
    name: pv003
spec:
  nfs:
    path: /data/volumes/v3
    server: 10.157.27.114
    readOnly: false 
  accessModes: ["ReadWriteOnce","ReadWriteMany"]
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-004
  labels:
    name: pv004
spec:
  nfs:
    path: /data/volumes/v4
    server: 10.157.27.114
    readOnly: false 
  accessModes: ["ReadWriteOnce","ReadWriteMany"]
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-005
  labels:
    name: pv005
spec:
  nfs:
    path: /data/volumes/v5
    server: 10.157.27.114
    readOnly: false 
  accessModes: ["ReadWriteOnce","ReadWriteMany"]
  capacity:
    storage: 5Gi
  persistentVolumeReclaimPolicy: Retain
~]# kubectl apply -f pv-nfs.yaml
persistentvolume/pv-nfs-001 created
persistentvolume/pv-nfs-002 created
persistentvolume/pv-nfs-003 created
persistentvolume/pv-nfs-004 created
persistentvolume/pv-nfs-005 created 

(3) 編寫定義StatefulSet的資源清單
首先我們要定義一個Headless Service,這裡headless Service和StatefulSet寫在一個檔案。

~]# vim statefulset-demo.yaml
#定義一個Headless Service
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
  labels:
    app: nginx-svc
spec:
  ports:
  - name: http
    port: 80
  clusterIP: None
  selector:
    app: nginx-pod
---
#定義StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nginx-statefulset
spec:
  serviceName: nginx-svc    #指定service,和上面定義的service對應
  replicas: 5    #指定副本數量
  selector:    #指定標籤選擇器,和後面的pod的標籤對應
    matchLabels:
      app: nginx-pod
  template:    #定義後端Pod的模板
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.12
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80
        volumeMounts:
        - name: nginxdata
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:    #定義儲存卷申請模板
  - metadata: 
      name: nginxdata
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 5Gi
#---
#解析上面的資源清單:由於StatefulSet資源依賴於一個事先存在的Service資源,所以需要先定義一個名為nginx-svc的Headless Service資源,用於關聯到每個Pod資源建立DNS資源記錄。接著定義了一個名為nginx-statefulset的StatefulSet資源,它通過Pod模板建立了5個Pod資源副本,並基於volumeClaiTemplate向前面建立的PV進行了請求大小為5Gi的專用儲存卷。

(4) 建立StatefulSet資源

~]# kubectl apply -f statefulset-demo.yaml
service/nginx-svc created
statefulset.apps/nginx-statefulset created
~]# kubectl get svc   #檢視建立的無頭服務nginx-svc
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
nginx-svc            ClusterIP   None             <none>        80/TCP         8s
~]# kubectl get pv     #檢視PV繫結
NAME         CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM                                   STORAGECLASS   REASON   AGE
pv-nfs-001   5Gi        RWO,RWX        Retain           Available                                                                   3m25s
pv-nfs-002   5Gi        RWO            Retain           Bound       default/nginxdata-nginx-statefulset-0                           3m25s
pv-nfs-003   5Gi        RWO,RWX        Retain           Available                                                                   3m25s
pv-nfs-004   5Gi        RWO,RWX        Retain           Available                                                                   3m25s
pv-nfs-005   5Gi        RWO,RWX        Retain           Available 
~]# kubectl get pvc     #檢視PVC繫結
NAME                            STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nginxdata-nginx-statefulset-0   Bound    pv-nfs-002   5Gi        RWO                           17s
~]# kubectl get statefulset    #檢視StatefulSet
NAME                DESIRED   CURRENT   AGE
nginx-statefulset   5         2         72s
~]# kubectl get pods    #檢視Pod資訊

(5) 刪除測試

~]# kubectl delete -f statefulset-demo.yaml 
service "nginx-svc" deleted
statefulset.apps "nginx-statefulset" deleted

~]# kubectl get pods -w     #動態檢視刪除過程,可以也是按照順序刪除,逆向關閉。
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          18m
nginx-statefulset-1   1/1     Running   0          18m
nginx-statefulset-2   1/1     Running   0          18m
nginx-statefulset-2   1/1     Terminating   0          18m
nginx-statefulset-0   1/1     Terminating   0          18m
nginx-statefulset-1   1/1     Terminating   0          18m
nginx-statefulset-2   0/1     Terminating   0          18m
nginx-statefulset-0   0/1     Terminating   0          18m
nginx-statefulset-1   0/1     Terminating   0          18m
nginx-statefulset-2   0/1     Terminating   0          18m
nginx-statefulset-2   0/1     Terminating   0          18m
nginx-statefulset-2   0/1     Terminating   0          18m
nginx-statefulset-1   0/1     Terminating   0          18m
nginx-statefulset-1   0/1     Terminating   0          18m
nginx-statefulset-0   0/1     Terminating   0          18m
nginx-statefulset-0   0/1     Terminating   0          18m


此時PVC依舊存在的,再重新建立pod時,依舊會重新去繫結原來的PVC
~]# kubectl apply -f statefulset-demo.yaml 
service/nginx-svc created
statefulset.apps/nginx-statefulset created

~]# kubectl get pvc     #檢視PVC繫結
NAME                            STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
nginxdata-nginx-statefulset-0   Bound    pv-nfs-002   5Gi        RWO                           30m
nginxdata-nginx-statefulset-1   Bound    pv-nfs-003   5Gi        RWO,RWX                       30m
nginxdata-nginx-statefulset-2   Bound    pv-nfs-004   5Gi        RWO,RWX                       30m

(6) 名稱解析

~]# kubectl get pods -o wide
NAME                            READY   STATUS    RESTARTS   AGE     IP             NODE               NOMINATED NODE
nginx-statefulset-0             1/1     Running   0          10m     10.244.1.196   node01.ilinux.io   <none>
nginx-statefulset-1             1/1     Running   0          9m13s   10.244.2.167   node02.ilinux.io   <none>
nginx-statefulset-2             1/1     Running   0          2m9s    10.244.3.42    node03.ilinux.io   <none>
nginx-statefulset-3             1/1     Running   0          2m6s    10.244.0.28    master.ilinux.io   <none>
nginx-statefulset-4             1/1     Running   0          54s     10.244.3.43    node03.ilinux.io   <none>

~]# dig -t A nginx-statefulset-0.nginx-svc.default.svc.cluster.local @10.96.0.10
nginx-statefulset-0.nginx-svc.default.svc.cluster.local. 5 IN A	10.244.1.196

3.StatefulSet資源擴縮容

StatefulSet資源的擴縮容與Deployment資源相似,即通過修改資源的副本數來改動其目標Pod資源數量。對StatefulSet資源來說,kubectl scale和kubectl patch命令均可以實現此功能,也可以使用kubectl edit命令直接修改其副本數,或者修改資源清單檔案,由kubectl apply命令重新宣告

(1) 通過scale將nginx-statefulset資源副本數量擴容為4個

~]# kubectl scale statefulset/nginx-statefulset --replicas=4   #擴容副本增加到4個
~]# kubectl get pods     #檢視pod資訊
NAME                            READY   STATUS        RESTARTS   AGE
nginx-statefulset-0             1/1     Running       0          13m
nginx-statefulset-1             1/1     Running       0          12m
nginx-statefulset-2             1/1     Running       0          5m30s
nginx-statefulset-3             1/1     Running       0          5m27s
~]# kubectl get pv   #檢視pv繫結

(2) 通過patch將nginx-statefulset資源副本數量縮容為3個

~]# kubectl patch sts/nginx-statefulset -p '{"spec":{"replicas":2}}'    #通過patch打補丁方式縮容
~]# kubectl get pods -w    #動態檢視縮容過程
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          17m
nginx-statefulset-1   1/1     Running   0          17m
nginx-statefulset-2   1/1     Running   0          17m
nginx-statefulset-3   1/1     Running   0          1m
nginx-statefulset-3   1/1     Terminating   0          20s
nginx-statefulset-3   0/1     Terminating   0          20s
nginx-statefulset-3   0/1     Terminating   0          22s
nginx-statefulset-3   0/1     Terminating   0          22s
nginx-statefulset-2   1/1     Terminating   0          24s
nginx-statefulset-2   0/1     Terminating   0          24s
nginx-statefulset-2   0/1     Terminating   0          36s
nginx-statefulset-2   0/1     Terminating   0          36s

4.更新策略

StatefulSet的預設更新策略為滾動更新,也可以暫停更新

(1) 滾動更新示例:

~]# kubectl patch sts/nginx-statefulset -p '{"spec":{"replicas":4}}'    #這裡先將副本擴容到4個。方便測試
~]# kubectl set image statefulset nginx-statefulset nginx=nginx:1.14    #更新映象版本
statefulset.apps/nginx-statefulset image updated
~]# kubectl get pods -w    #動態檢視更新
NAME                  READY   STATUS    RESTARTS   AGE
nginx-statefulset-0   1/1     Running   0          18m
nginx-statefulset-1   1/1     Running   0          18m
nginx-statefulset-2   1/1     Running   0          13m
nginx-statefulset-3   1/1     Running   0          13m
nginx-statefulset-3   1/1     Terminating   0          13m
nginx-statefulset-3   0/1     Terminating   0          13m
nginx-statefulset-3   0/1     Terminating   0          13m
nginx-statefulset-3   0/1     Terminating   0          13m
nginx-statefulset-3   0/1     Pending       0          0s
nginx-statefulset-3   0/1     Pending       0          0s
nginx-statefulset-3   0/1     ContainerCreating   0          0s
nginx-statefulset-3   1/1     Running             0          2s
nginx-statefulset-2   1/1     Terminating         0          13m
nginx-statefulset-2   0/1     Terminating         0          13m
nginx-statefulset-2   0/1     Terminating         0          14m
nginx-statefulset-2   0/1     Terminating         0          14m
nginx-statefulset-2   0/1     Pending             0          0s
nginx-statefulset-2   0/1     Pending             0          0s
nginx-statefulset-2   0/1     ContainerCreating   0          0s
nginx-statefulset-2   1/1     Running             0          1s
nginx-statefulset-1   1/1     Terminating         0          18m
nginx-statefulset-1   0/1     Terminating         0          18m
nginx-statefulset-1   0/1     Terminating         0          18m
nginx-statefulset-1   0/1     Terminating         0          18m
nginx-statefulset-1   0/1     Pending             0          0s
nginx-statefulset-1   0/1     Pending             0          0s
nginx-statefulset-1   0/1     ContainerCreating   0          0s
nginx-statefulset-1   1/1     Running             0          2s
nginx-statefulset-0   1/1     Terminating         0          18m
nginx-statefulset-0   0/1     Terminating         0          18m
nginx-statefulset-0   0/1     Terminating         0          18m
nginx-statefulset-0   0/1     Terminating         0          18m
nginx-statefulset-0   0/1     Pending             0          0s
nginx-statefulset-0   0/1     Pending             0          0s
nginx-statefulset-0   0/1     ContainerCreating   0          0s
nginx-statefulset-0   1/1     Running             0          2s

~]# kubectl get pods -l app=nginx-pod -o custom-columns=NAME:metadata.name,IMAGE:spec.containers[0].image    #檢視更新完成後的映象版本
NAME                  IMAGE
nginx-statefulset-0   nginx:1.14
nginx-statefulset-1   nginx:1.14
nginx-statefulset-2   nginx:1.14
nginx-statefulset-3   nginx:1.14   

通過上面示例可以看出,預設為滾動更新,倒序更新,更新完成一個接著更新下一個。

(2) 暫停更新示例:
有時候設定了一個更新操作,但是又不希望一次性全部更新完成,想先更新幾個,觀察其是否穩定,然後再更新所有的。這時候只需要將.spec.spec.updateStrategy.rollingUpdate.partition欄位的值進行修改即可。(預設值為0,所以我們看到了更新效果為上面那樣,全部更新)。該欄位表示如果設定為2,那麼只有當編號大於等於2的才會進行更新。類似於金絲雀的釋出方式。示例如下:

~]# kubectl patch sts/nginx-statefulset -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":2}}}}}'     #將更新值partition設定為2
~]# kubectl set image statefulset nginx-statefulset nginx=nginx:1.12    #更新映象版本
statefulset.apps/nginx-statefulset image updated
~]# kubectl get pods -w
~]# kubectl get pods -l app=nginx-pod -o custom-columns=NAME:metadata.name,IMAGE:spec.containers[0].image    #檢視更新完成後的映象版本,可以發現只有當編號大於等於2的進行了更新。
NAME                  IMAGE
nginx-statefulset-0   nginx:1.14
nginx-statefulset-1   nginx:1.14
nginx-statefulset-2   nginx:1.12
nginx-statefulset-3   nginx:1.12

將剩餘的也全部更新,只需要將更新策略的partition的值改為0即可,如下:
~]# kubectl patch sts/nginx-statefulset -p '{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}}'    #將更新值partition設定為0
statefulset.apps/nginx-statefulset patche
~]# kubectl get pods -w    #動態檢視更新