K8s statefulset控制器

概述

RC、Deployment、DaemonSet都是面向無狀態的服務,它們所管理的Pod的IP、名字,啓停順序等都是隨機的,而StatefulSet是什麼?顧名思義,有狀態的集合,管理所有有狀態的服務,比如MySQL、MongoDB集羣等。
StatefulSet本質上是Deployment的一種變體,在v1.9版本中已成爲GA版本,它爲了解決有狀態服務的問題,它所管理的Pod擁有固定的Pod名稱,啓停順序,在StatefulSet中,Pod名字稱爲網絡標識(hostname),還必須要用到共享存儲。
在Deployment中,與之對應的服務是service,而在StatefulSet中與之對應的headless service,headless service,即無頭服務,與service的區別就是它沒有Cluster IP,解析它的名稱時將返回該Headless Service對應的全部Pod的Endpoint列表。
除此之外,StatefulSet在Headless Service的基礎上又爲StatefulSet控制的每個Pod副本創建了一個DNS域名,這個域名的格式爲:

$(podname).(headless server name)   
FQDN: $(podname).(headless server name).namespace.svc.cluster.local

statefulset特點

  • 穩定且唯一的網絡標識符

如: Redis集羣, 在Redis集羣中,它是通過槽位來存儲數據的,假如:第一個節點是0~1000,第二個節點是1001~2000,第三個節點2001~3000...等等,這就使得Redis集羣中每個節點要通過ID來標識自己,如: 第二個節點宕機了,重建後它必須還叫第二個節點,或者說第二個節點叫R2,它必須還叫R2,這樣在獲取1001~2000槽位的數據時,才能找到數據,否則Redis集羣將無法找到這段數據。

  • 穩定且持久的存儲
  • 有序、平滑的部署和擴展

如 MySQL集羣,要先啓動主節點, 若從節點沒有要求,則可一起啓動,若從節點有啓動順序要求,可先啓動第一個從節點,接着第二從節點等;這個過程就是有順序,平滑安全的啓動。

  • 有序、平滑的終止和刪除

即: 我們先終止從節點,若從節點是有啓動順序的,那麼關閉時,也要按照逆序終止,即啓動時是從S1~S4以此啓動,則關閉時,則是先關閉S4,然後時S3,依次關閉,最後在關閉主節點。

  • 有序的滾動更新

MySQL在更新時,應該先更新從節點,全部的從節點都更新完了,最後在更新主節點,因爲新版本一般可兼容老版本,但是一定要注意,若新版本不兼容老版本就很很麻煩

statefulset組成 

  • Headless Service 用來定義Pod網絡標識( DNS domain)
  • volumeClaimTemplates 存儲卷申請模板,創建PVC,指定pvc名稱大小,將自動創建pvc,且pvc必須由存儲類供應
  • StatefulSet 用於定義具體應用

爲什麼需要 headless service 無頭服務?
在用Deployment時,每一個Pod名稱是沒有順序的,是隨機字符串,因此是Pod名稱是無序的,但是在statefulset中要求必須是有序 ,每一個pod不能被隨意取代,pod重建後pod名稱還是一樣的。而pod IP是變化的,所以是以Pod名稱來識別。pod名稱是pod唯一性的標識符,必須持久穩定有效。這時候要用到無頭服務,它可以給每個Pod一個唯一的名稱 。
爲什麼需要volumeClaimTemplate?
對於有狀態的副本集都會用到持久存儲,對於分佈式系統來講,它的最大特點是數據是不一樣的,所以各個節點不能使用同一存儲卷,每個節點有自已的專用存儲,但是如果在Deployment中的Pod template裏定義的存儲卷,是所有副本集共用一個存儲卷,數據是相同的,因爲是基於模板來的 ,而statefulset中每個Pod都要自已的專有存儲卷,所以statefulset的存儲卷就不能再用Pod模板來創建了,於是statefulSet使用volumeClaimTemplate,稱爲卷申請模板,它會爲每個Pod生成不同的pvc,並綁定pv, 從而實現各pod有專用存儲。這就是爲什麼要用volumeClaimTemplate的原因。

StatefulSet示例

創建之前需要有定義好的PV,參考Kubernetes存儲卷PV及PVC

 

接下來看一些示例,演示下上面所說的特性,以加深理解。

[root@s1 statefulset]# vim sts-demo.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-sts
  labels:
    app: myapp-sts
spec:
  ports:
  - port: 80
    name: web
  clusterIP: "None"
  selector:
    app: myapp-pod
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: myapp
spec:
  serviceName: myapp-sts #聲明它屬於哪個Headless Service.
  replicas: 2
  selector:
    matchLabels:
      app: myapp-pod
  template:
    metadata:
      labels:
        app: myapp-pod
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: myappdata
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: myappdata
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi
[root@s1 statefulset]# kubectl apply -f sts-demo.yaml 
service/myapp-sts created
statefulset.apps/myapp created
[root@s1 statefulset]# kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP            NODE   NOMINATED NODE   READINESS GATES
myapp-0   1/1     Running   0          2m47s   10.244.2.31   n2     <none>           <none>
myapp-1   1/1     Running   0          2m45s   10.244.1.33   n1     <none>           <none>
[root@s1 statefulset]# kubectl get pvc
NAME                STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myappdata-myapp-0   Bound    pv004    15Gi       RWO,ROX                       26m
myappdata-myapp-1   Bound    pv005    10Gi       RWO,ROX,RWX                   26m

如果集羣中沒有StorageClass的動態供應PVC的機制,也可以提前手動創建多個PV、PVC,手動創建的PVC名稱必須符合之後創建的StatefulSet命名規則:(volumeClaimTemplates.name)-(pod_name)

可以正確解析到ip

[root@s1 statefulset]# nslookup myapp-1.myapp-sts.default.svc.cluster.local 10.96.0.10
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   myapp-1.myapp-sts.default.svc.cluster.local
Address: 10.244.1.33

[root@s1 statefulset]# nslookup myapp-0.myapp-sts.default.svc.cluster.local 10.96.0.10
Server:         10.96.0.10
Address:        10.96.0.10#53

Name:   myapp-0.myapp-sts.default.svc.cluster.local
Address: 10.244.2.31


規律總結:

  • 匹配Pod name(網絡標識)的模式爲:$(statefulset名稱)-$(序號),比如上面的示例:myapp-0,myap-1。
  • StatefulSet爲每個Pod副本創建了一個DNS域名,這個域名的格式爲: $(podname).(headless server name),也就意味着服務間是通過Pod域名來通信而非Pod IP,因爲當Pod所在Node發生故障時,Pod會被飄移到其它Node上,Pod IP會發生變化,但是Pod域名不會有變化。
  • StatefulSet使用Headless服務來控制Pod的域名,這個域名的FQDN爲:$(service name).$(namespace).svc.cluster.local,其中,“cluster.local”指的是集羣的域名。
  • 根據volumeClaimTemplates,爲每個Pod創建一個pvc,pvc的命名規則匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的volumeMounts.name=myappdata, Pod name=myapp-[0-1],因此創建出來的PVC是myappdata-myapp-0,myappdata-myapp-1。
  • 刪除Pod不會刪除其pvc,手動刪除pvc將自動釋放pv。

擴容和縮容

#擴容至三個pod
kubectl scale sts myapp --replicas=3
#然後重新縮容至兩個pod
kubectl scale sts myapp --replicas=2

擴容當中出現了一個問題, 新創建的pod處於pending狀態, describe看到是mount的問題, 原來是之前pod掛載的pv變成了released狀態, 並沒有變成available狀態, 經過查找發現最關鍵的是PV的spec.claimRef字段,該字段記錄着原來PVC的綁定信息,刪除綁定信息,即可重新釋放PV從而達到Available。

statefulset管理pod的啓停順序

  • 有序部署:部署StatefulSet時,如果有多個Pod副本,它們會被順序地創建(從0到N-1)並且,在下一個Pod運行之前所有之前的Pod必須都是Running和Ready狀態。
  • 有序刪除:當Pod被刪除時,它們被終止的順序是從N-1到0。
  • 有序擴展:當對Pod執行擴展操作時,與部署一樣,它前面的Pod必須都處於Running和Ready狀態

statefulset管理策略

  • OrderedReady:上述的啓停順序,默認設置。

    spec:
      podManagementPolicy: OrderedReady
  • Parallel:告訴StatefulSet控制器並行啓動或終止所有Pod,並且在啓動或終止另一個Pod之前不等待前一個Pod變爲Running and Ready或完全終止。

    spec:
      podManagementPolicy: Parallel

 更新策略

在Kubernetes 1.7及更高版本中,通過.spec.updateStrategy字段允許配置或禁用Pod、labels、source request/limits、annotations自動滾動更新功能。
OnDelete:通過.spec.updateStrategy.type 字段設置爲OnDelete,StatefulSet控制器不會自動更新StatefulSet中的Pod。用戶必須手動刪除Pod,以使控制器創建新的Pod。
RollingUpdate:通過.spec.updateStrategy.type 字段設置爲RollingUpdate,實現了Pod的自動滾動更新,如果.spec.updateStrategy未指定,則此爲默認策略。
StatefulSet控制器將刪除並重新創建StatefulSet中的每個Pod。它將以Pod終止(從最大序數到最小序數)的順序進行,一次更新每個Pod。在更新下一個Pod之前,必須等待這個Pod Running and Ready。
Partitions:通過指定 .spec.updateStrategy.rollingUpdate.partition 來對 RollingUpdate 更新策略進行分區,如果指定了分區,則當 StatefulSet 的 .spec.template 更新時,具有大於或等於分區序數的所有 Pod 將被更新。
具有小於分區的序數的所有 Pod 將不會被更新,即使刪除它們也將被重新創建。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大於其 .spec.replicas,則其 .spec.template 的更新將不會傳播到 Pod。在大多數情況下,不需要使用分區。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章