概述
RC再扭、Deployment氧苍、DaemonSet都是面向無狀態(tài)的服務(wù),它們所管理的Pod的IP霍衫、名字候引,啟停順序等都是隨機(jī)的,而StatefulSet是什么敦跌?顧名思義,有狀態(tài)的集合逛揩,管理所有有狀態(tài)的服務(wù)柠傍,比如MySQL、MongoDB集群等辩稽。
StatefulSet本質(zhì)上是Deployment的一種變體惧笛,在v1.9版本中已成為GA版本,它為了解決有狀態(tài)服務(wù)的問題逞泄,它所管理的Pod擁有固定的Pod名稱患整,啟停順序拜效,在StatefulSet中,Pod名字稱為網(wǎng)絡(luò)標(biāo)識(hostname)各谚,還必須要用到共享存儲紧憾。
在Deployment中,與之對應(yīng)的服務(wù)是service昌渤,而在StatefulSet中與之對應(yīng)的headless service赴穗,headless service,即無頭服務(wù)膀息,與service的區(qū)別就是它沒有Cluster IP般眉,解析它的名稱時將返回該Headless Service對應(yīng)的全部Pod的Endpoint列表。
除此之外潜支,StatefulSet在Headless Service的基礎(chǔ)上又為StatefulSet控制的每個Pod副本創(chuàng)建了一個DNS域名甸赃,這個域名的格式為:
$(podname).(headless server name)
FQDN: $(podname).(headless server name).namespace.svc.cluster.local
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:
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的三個組成部分:
- Headless Service:名為nginx已烤,用來定義Pod網(wǎng)絡(luò)標(biāo)識( DNS domain)鸠窗。
- StatefulSet:定義具體應(yīng)用,名為Nginx胯究,有三個Pod副本稍计,并為每個Pod定義了一個域名。
- volumeClaimTemplates: 存儲卷申請模板裕循,創(chuàng)建PVC臣嚣,指定pvc名稱大小,將自動創(chuàng)建pvc剥哑,且pvc必須由存儲類供應(yīng)硅则。
為什么需要 headless service 無頭服務(wù)?
在用Deployment時株婴,每一個Pod名稱是沒有順序的怎虫,是隨機(jī)字符串,因此是Pod名稱是無序的困介,但是在statefulset中要求必須是有序 大审,每一個pod不能被隨意取代,pod重建后pod名稱還是一樣的座哩。而pod IP是變化的徒扶,所以是以Pod名稱來識別。pod名稱是pod唯一性的標(biāo)識符根穷,必須持久穩(wěn)定有效姜骡。這時候要用到無頭服務(wù)导坟,它可以給每個Pod一個唯一的名稱 。
為什么需要volumeClaimTemplate圈澈?
對于有狀態(tài)的副本集都會用到持久存儲惫周,對于分布式系統(tǒng)來講,它的最大特點(diǎn)是數(shù)據(jù)是不一樣的士败,所以各個節(jié)點(diǎn)不能使用同一存儲卷闯两,每個節(jié)點(diǎn)有自已的專用存儲,但是如果在Deployment中的Pod template里定義的存儲卷谅将,是所有副本集共用一個存儲卷漾狼,數(shù)據(jù)是相同的,因?yàn)槭腔谀0鍋淼?饥臂,而statefulset中每個Pod都要自已的專有存儲卷逊躁,所以statefulset的存儲卷就不能再用Pod模板來創(chuàng)建了,于是statefulSet使用volumeClaimTemplate隅熙,稱為卷申請模板稽煤,它會為每個Pod生成不同的pvc,并綁定pv囚戚, 從而實(shí)現(xiàn)各pod有專用存儲酵熙。這就是為什么要用volumeClaimTemplate的原因。
創(chuàng)建:
$ kubectl create -f nginx.yaml
service "nginx" created
statefulset "web" created
看下這三個Pod創(chuàng)建過程:
#第一個是創(chuàng)建web-0
$ kubectl get pod
web-0 1/1 ContainerCreating 0 51s
#待web-0 running且ready時驰坊,創(chuàng)建web-1
$ kubectl get pod
web-0 1/1 Running 0 51s
web-1 0/1 ContainerCreating 0 42s
#待web-1 running且ready時匾二,創(chuàng)建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
根據(jù)volumeClaimTemplates自動創(chuàng)建的PVC
$ 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的動態(tài)供應(yīng)PVC的機(jī)制,也可以提前手動創(chuàng)建多個PV拳芙、PVC察藐,手動創(chuàng)建的PVC名稱必須符合之后創(chuàng)建的StatefulSet命名規(guī)則:(volumeClaimTemplates.name)-(pod_name)
Statefulset名稱為web 三個Pod副本: web-0,web-1,web-2舟扎,volumeClaimTemplates名稱為:www分飞,那么自動創(chuàng)建出來的PVC名稱為www-web[0-2],為每個Pod創(chuàng)建一個PVC睹限。
規(guī)律總結(jié):
匹配Pod name(網(wǎng)絡(luò)標(biāo)識)的模式為:(序號)譬猫,比如上面的示例:web-0,web-1邦泄,web-2删窒。
StatefulSet為每個Pod副本創(chuàng)建了一個DNS域名,這個域名的格式為: $(podname).(headless server name)顺囊,也就意味著服務(wù)間是通過Pod域名來通信而非Pod IP,因?yàn)楫?dāng)Pod所在Node發(fā)生故障時蕉拢,Pod會被飄移到其它Node上特碳,Pod IP會發(fā)生變化诚亚,但是Pod域名不會有變化。
StatefulSet使用Headless服務(wù)來控制Pod的域名午乓,這個域名的FQDN為:(namespace).svc.cluster.local站宗,其中,“cluster.local”指的是集群的域名益愈。
根據(jù)volumeClaimTemplates梢灭,為每個Pod創(chuàng)建一個pvc,pvc的命名規(guī)則匹配模式:(volumeClaimTemplates.name)-(pod_name)蒸其,比如上面的volumeMounts.name=www敏释, Pod name=web-[0-2],因此創(chuàng)建出來的PVC是www-web-0摸袁、www-web-1钥顽、www-web-2。
刪除Pod不會刪除其pvc靠汁,手動刪除pvc將自動釋放pv蜂大。
關(guān)于Cluster Domain、headless service名稱蝶怔、StatefulSet 名稱如何影響StatefulSet的Pod的DNS域名的示例:
Statefulset的啟停順序:
有序部署:部署StatefulSet時奶浦,如果有多個Pod副本,它們會被順序地創(chuàng)建(從0到N-1)并且踢星,在下一個Pod運(yùn)行之前所有之前的Pod必須都是Running和Ready狀態(tài)澳叉。
有序刪除:當(dāng)Pod被刪除時,它們被終止的順序是從N-1到0斩狱。
有序擴(kuò)展:當(dāng)對Pod執(zhí)行擴(kuò)展操作時耳高,與部署一樣,它前面的Pod必須都處于Running和Ready狀態(tài)
Statefulset Pod管理策略:
在v1.7以后所踊,通過允許修改Pod排序策略泌枪,同時通過.spec.podManagementPolicy字段確保其身份的唯一性。
- OrderedReady:上述的啟停順序秕岛,默認(rèn)設(shè)置碌燕。
- Parallel:告訴StatefulSet控制器并行啟動或終止所有Pod,并且在啟動或終止另一個Pod之前不等待前一個Pod變?yōu)镽unning and Ready或完全終止继薛。
StatefulSet使用場景:
- 穩(wěn)定的持久化存儲修壕,即Pod重新調(diào)度后還是能訪問到相同的持久化數(shù)據(jù),基于PVC來實(shí)現(xiàn)遏考。
- 穩(wěn)定的網(wǎng)絡(luò)標(biāo)識符慈鸠,即Pod重新調(diào)度后其PodName和HostName不變。
- 有序部署灌具,有序擴(kuò)展青团,基于init containers來實(shí)現(xiàn)譬巫。
- 有序收縮。
更新策略
在Kubernetes 1.7及更高版本中督笆,通過.spec.updateStrategy字段允許配置或禁用Pod芦昔、labels、source request/limits娃肿、annotations自動滾動更新功能咕缎。
OnDelete:通過.spec.updateStrategy.type 字段設(shè)置為OnDelete,StatefulSet控制器不會自動更新StatefulSet中的Pod料扰。用戶必須手動刪除Pod凭豪,以使控制器創(chuàng)建新的Pod。
RollingUpdate:通過.spec.updateStrategy.type 字段設(shè)置為RollingUpdate记罚,實(shí)現(xiàn)了Pod的自動滾動更新墅诡,如果.spec.updateStrategy未指定,則此為默認(rèn)策略桐智。
StatefulSet控制器將刪除并重新創(chuàng)建StatefulSet中的每個Pod末早。它將以Pod終止(從最大序數(shù)到最小序數(shù))的順序進(jìn)行,一次更新每個Pod说庭。在更新下一個Pod之前然磷,必須等待這個Pod Running and Ready。
- Partitions:通過指定 .spec.updateStrategy.rollingUpdate.partition 來對 RollingUpdate 更新策略進(jìn)行分區(qū)刊驴,如果指定了分區(qū)姿搜,則當(dāng) StatefulSet 的 .spec.template 更新時,具有大于或等于分區(qū)序數(shù)的所有 Pod 將被更新捆憎。
具有小于分區(qū)的序數(shù)的所有 Pod 將不會被更新舅柜,即使刪除它們也將被重新創(chuàng)建。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于其 .spec.replicas躲惰,則其 .spec.template 的更新將不會傳播到 Pod致份。在大多數(shù)情況下,不需要使用分區(qū)础拨。