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域名,這個域名的格式爲:html
$(podname).(headless server name) FQDN: $(podname).(headless server name).namespace.svc.cluster.local
接下來看一些示例,演示下上面所說的特性,以加深理解。nginx
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: selector: matchLabels: app: nginx # has to match .spec.template.metadata.labels serviceName: "nginx" #聲明它屬於哪一個Headless Service. replicas: 3 # by default is 1 template: metadata: labels: app: nginx # has to match .spec.selector.matchLabels spec: terminationGracePeriodSeconds: 10 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: #可看做pvc的模板 - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "gluster-heketi" #存儲類名,改成集羣中已存在的 resources: requests: storage: 1Gi
經過該配置文件,可看出StatefulSet的三個組成部分:web
爲何須要 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的緣由。 api
建立:網絡
$ kubectl create -f nginx.yaml service "nginx" created statefulset "web" created
看下這三個Pod建立過程:app
#第一個是建立web-0 $ kubectl get pod web-0 1/1 ContainerCreating 0 51s #待web-0 running且ready時,建立web-1 $ kubectl get pod web-0 1/1 Running 0 51s web-1 0/1 ContainerCreating 0 42s #待web-1 running且ready時,建立web-2 $ kubectl get pod web-0 1/1 Running 0 1m web-1 1/1 Running 0 45s web-2 1/1 ContainerCreating 0 36s #最後三個Pod所有running且ready $ kubectl get pod NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 4m web-1 1/1 Running 0 3m web-2 1/1 Running 0 1m
根據volumeClaimTemplates自動建立的PVC less
$ kubectl get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE www-web-0 Bound pvc-ecf003f3-828d-11e8-8815-000c29774d39 2G RWO gluster-heketi 7m www-web-1 Bound pvc-0615e33e-828e-11e8-8815-000c29774d39 2G RWO gluster-heketi 6m www-web-2 Bound pvc-43a97acf-828e-11e8-8815-000c29774d39 2G RWO gluster-heketi 4m
若是集羣中沒有StorageClass的動態供應PVC的機制,也能夠提早手動建立多個PV、PVC,手動建立的PVC名稱必須符合以後建立的StatefulSet命名規則:(volumeClaimTemplates.name)-(pod_name)dom
Statefulset名稱爲web 三個Pod副本: web-0,web-1,web-2,volumeClaimTemplates名稱爲:www,那麼自動建立出來的PVC名稱爲www-web[0-2],爲每一個Pod建立一個PVC。
規律總結: 分佈式
Cluster Domain | Service (ns/name) | StatefulSet (ns/name) | StatefulSet Domain | Pod DNS | Pod Hostname |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
Statefulset的啓停順序:ide
Statefulset Pod管理策略:
在v1.7之後,經過容許修改Pod排序策略,同時經過.spec.podManagementPolicy字段確保其身份的惟一性。
StatefulSet使用場景:
在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。在大多數狀況下,不須要使用分區。