Kubernetes有狀態(tài)應(yīng)用管理StatefulSet使用詳解
什么是有狀態(tài)應(yīng)用
我們在《Kubernetes工作負載管理》中主要介紹了無狀態(tài)應(yīng)用的管理,當(dāng)時也有提到有狀態(tài)應(yīng)用,但是由于那時候還沒有解釋數(shù)據(jù)如何持久化就沒有做深度的介紹,而在這章,我們會著重介紹如何進行有狀態(tài)應(yīng)用的管理。
實例之間的不等關(guān)系以及實例對外數(shù)據(jù)有依賴關(guān)系的應(yīng)用,就被稱為"有狀態(tài)應(yīng)用"。
所謂實例之間的不等關(guān)系即對分布式應(yīng)用來說,各實例,各應(yīng)用之間往往有比較大的依賴關(guān)系,比如某個應(yīng)用必須先于其他應(yīng)用啟動,否則其他應(yīng)用將不能啟動等。
對外數(shù)據(jù)有依賴關(guān)系的應(yīng)用,最顯著的就是數(shù)據(jù)庫應(yīng)用,對于數(shù)據(jù)庫應(yīng)用,我們是需要持久化保存其數(shù)據(jù)的,如果是無狀態(tài)應(yīng)用,在數(shù)據(jù)庫重啟數(shù)據(jù)和應(yīng)用就失去了聯(lián)系,這顯然是違背我們的初衷,不能投入生產(chǎn)的。
所以,為了解決Kubernetes中有狀態(tài)應(yīng)用的有效支持,Kubernetes使用StatefulSet來編排管理有狀態(tài)應(yīng)用。 StatefulSet類似于ReplicaSet,不同之處在于它可以控制Pod的啟動順序,它為每個Pod設(shè)置唯一的標識。其具有一下功能:
- 穩(wěn)定的,唯一的網(wǎng)絡(luò)標識符
- 穩(wěn)定的,持久化存儲
- 有序的,優(yōu)雅部署和縮放
- 有序的,自動滾動更新
StatefulSet的設(shè)計很容易理解,它把現(xiàn)實世界抽象為以下兩種情況:
(1)、拓撲狀態(tài)。這就意味著應(yīng)用之間是不對等關(guān)系,應(yīng)用要按某種順序啟動,即使應(yīng)用重啟,也必須按其規(guī)定的順序重啟,并且重啟后其網(wǎng)絡(luò)標識必須和原來的一樣,這樣才能保證原訪問者能通過同樣的方法訪問新的Pod;
(2)、存儲狀態(tài) 。這就意味著應(yīng)用綁定了存儲數(shù)據(jù),不論什么時候,不論什么情況,對應(yīng)用來說,只要存儲里的數(shù)據(jù)沒有變化,讀取到的數(shù)據(jù)應(yīng)該是同一份;
所以StatefulSet的核心功能就是以某種方式記錄Pod的狀態(tài),然后在Pod被重新創(chuàng)建時,通過某種方法恢復(fù)其狀態(tài)。
如何使用StatefulSet
在《Kubernetes應(yīng)用訪問管理》中,我們介紹了Service,它是為一組Pod提供外部訪問的一種方式。通常,我們使用 Service訪問Pod有一下兩種方式:
- (1)、通過Cluster IP,這個Clustre IP就相當(dāng)于VIP,我們訪問這個IP,就會將請求轉(zhuǎn)發(fā)到后端Pod上;
- (2)、通過DNS方式,通過這種方式首先得確保Kubernetes集群中有DNS服務(wù)。這個時候我們只要訪問"my-service.my-namespace.svc,cluster.local",就可以訪問到名為my-service的Service所代理的后端Pod;
而對于第二種方式,有下面兩種處理方法:
- (1)、Normal Service,即解析域名,得到的是Cluster IP,然后再按照方式一訪問;
- (2)、Headless Service,即解析域名,得到的是后端某個Pod的IP地址,這樣就可以直接訪問;
而在使用StatefulSet的時候,主要用到Headless Service,還記得Headless Service怎么定義的嗎?
我們只需要把ClusterIP設(shè)置為None即可,如下:
apiVersion: v1 kind: Service metadata: name: nginx-headless-service labels: name: nginx-headless-service spec: clusterIP: None selector: name: nginx ports: - port: 8000 targetPort: 80
了解了Headless Service,還需要了解PV、PVC是怎么使用的,如果忘記了,可以移步《Kubernetes數(shù)據(jù)持久化管理》回顧,這里就不再贅述了。
下面,我們開始使用StatefulSet。
首先,我們創(chuàng)建兩個個PV,因為準備為有狀態(tài)應(yīng)用創(chuàng)建兩個副本,如下:
apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv01 labels: storage: pv spec: accessModes: - ReadWriteOnce capacity: storage: 1Gi persistentVolumeReclaimPolicy: Recycle nfs: path: /data/k8s server: 192.168.205.128 --- apiVersion: v1 kind: PersistentVolume metadata: name: nginx-pv02 labels: storage: pv spec: accessModes: - ReadWriteOnce capacity: storage: 1Gi persistentVolumeReclaimPolicy: Recycle nfs: path: /data/k8s server: 192.168.205.128
StatefulSet需要YAML文件
apiVersion: v1 kind: Service metadata: name: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx role: stateful --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx role: stateful spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: [ "ReadWriteOnce" ] resources: requests: storage: 1Gi
注意上面的 YAML 文件中和volumeMounts進行關(guān)聯(lián)的是一個新的屬性:volumeClaimTemplates,該屬性會自動聲明一個 pvc 對象和 pv 進行管理,而serviceName: "nginx"表示在執(zhí)行控制循環(huán)的時候,用nginx這個Headless Service來保存Pod的可解析身份。
創(chuàng)建完成后,可以看到會起兩個Pod:
$ kubectl get pod | grep web web-0 1/1 Running 0 2m45s web-1 1/1 Running 0 2m41s
從這兩個Pod的命令可以看到,它們的名字不像Deployment那樣隨機生成的字符串,而是0,1這樣的序號。這是因為StatefulSet要保證每個Pod順序,確保每次重啟或者更新,每個Pod依然保持以前的數(shù)據(jù),不會錯亂。所以StatefulSet會以[statefulset-name]-[index]
規(guī)則進行命名,其中index從0開始。而且每個Pod的創(chuàng)建是有順序的,如上只有web-0進入running狀態(tài)后,web-1才創(chuàng)建。
當(dāng)兩個Pod都進入running狀態(tài)后,就可以查看其各自的網(wǎng)絡(luò)身份了,我們通過kubectl exec來查看,如下:
$ kubectl exec web-0 -- sh -c 'hostname' web-0 $ kubectl exec web-1 -- sh -c 'hostname' web-1
可以看到這兩個pod的hostname和pod的名字是一致的,都被分配為對應(yīng)的編號,接下來我們用DNS的方式來訪問Headless Service。
我們先啟動一個調(diào)試Pod,如下:
apiVersion: v1 kind: Pod metadata: name: dnsutils namespace: default spec: containers: - name: dnsutils image: lansible/dnstools command: - sleep - "3600" imagePullPolicy: IfNotPresent restartPolicy: Always
dnsutils容器解析
$ kubectl exec -it dnsutils -- /bin/sh / # nslookup web-0.nginx Server: 10.96.0.10 Address: 10.96.0.10#53 Name: web-0.nginx.default.svc.cluster.local Address: 172.16.51.247 / # nslookup web-1.nginx Server: 10.96.0.10 Address: 10.96.0.10#53 Name: web-1.nginx.default.svc.cluster.local Address: 172.16.51.251 / #
從nslookup的結(jié)果分析,在訪問web-0.nginx的時候解析的是web-0這個Pod的IP,另一個亦然。這表示,如果我們在應(yīng)用中配置web-0.nginx,則只會調(diào)用web-0這個Pod,在配置有狀態(tài)應(yīng)用,比如Zookeeper的時候,我們需要在配置文件里指定zkServer,這時候就可以指定類似:zk-0.zookeeper,zk-1.zookeeper。
如果我們現(xiàn)在更新StatefuleSet,起更新順序是怎么樣的呢?
首先,我們新開一個終端,輸入以下命令用以觀察:
$ kubectl get pods -w -l role=stateful NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 67m web-1 1/1 Running 0 67m
然后使用以下命令更新應(yīng)用的鏡像,如下:
$ kubectl set image statefulset/web nginx=nginx:1.8
然后觀察web應(yīng)用的更新順序,如下:
$ kubectl get pods -w -l role=stateful NAME READY STATUS RESTARTS AGE web-0 1/1 Running 0 67m web-1 1/1 Running 0 67m web-1 1/1 Terminating 0 68m web-1 1/1 Terminating 0 68m web-1 0/1 Terminating 0 68m web-1 0/1 Terminating 0 68m web-1 0/1 Terminating 0 68m web-1 0/1 Pending 0 0s web-1 0/1 Pending 0 0s web-1 0/1 ContainerCreating 0 0s web-1 0/1 ContainerCreating 0 1s web-1 1/1 Running 0 10s web-0 1/1 Terminating 0 69m web-0 1/1 Terminating 0 69m web-0 0/1 Terminating 0 69m web-0 0/1 Terminating 0 69m web-0 0/1 Terminating 0 69m web-0 0/1 Pending 0 0s web-0 0/1 Pending 0 0s web-0 0/1 ContainerCreating 0 0s web-0 0/1 ContainerCreating 0 1s web-0 1/1 Running 0 9s
從整個順序可以看到,起更新是從后往前進行更新的,也就是先更新web-1的pod,再更新web-0的pod。通過這種嚴格的對應(yīng)規(guī)則,StatefulSet就保證了Pod的網(wǎng)絡(luò)標識的穩(wěn)定性,通過這個方法,就可以把Pod的拓撲狀態(tài)按照Pod的名字+編號的方式固定起來。此外,Kubernetes還為每一個Pod提供了一個固定并且唯一的訪問入口,即這個Pod的DNS記錄。
由此,我們對StatefulSet梳理如下:
- (1)、StatefulSet直接管理的是Pod。這是因為StatefulSet里的Pod實例不像ReplicaSet中的Pod實例完全一樣,它們是有細微的區(qū)別,比如每個Pod的名字、hostname等是不同的,而且StatefulSet區(qū)分這些實例的方式就是為Pod加上編號;
- (2)、Kubernetes通過Headless Service為這個編號的Pod在DNS服務(wù)器中生成帶同樣編號的記錄。只要StatefulSet能保證這個Pod的編號不變,那么Service中類似于web-0.nginx.default.svc.cluster.local這樣的DNS記錄就不會變,而這條記錄所解析的Pod IP地址會隨著Pod的重新創(chuàng)建自動更新;
- (3)、StatefulSet還可以為每個Pod分配并創(chuàng)建一個和Pod同樣編號的PVC。這樣Kubernetes就可以通過Persitent Volume機制為這個PVC綁定對應(yīng)的PV,從而保證每一個Pod都擁有獨立的Volume。這種情況下即使Pod被刪除,它所對應(yīng)的PVC和PV依然會保留下來,所以當(dāng)這個Pod被重新創(chuàng)建出來過后,Kubernetes會為它找到同樣編號的PVC,掛載這個PVC對應(yīng)的Volume,從而獲取到以前Volume以前的數(shù)據(jù);
總結(jié)
StatefulSet這個控制器的主要作用之一,就是使用Pod模板創(chuàng)建Pod的時候,對它們進行編號,并且按照編號順序完成作業(yè),當(dāng)StatefulSet的控制循環(huán)發(fā)現(xiàn)Pod的實際狀態(tài)和期望狀態(tài)不一致的時候,也會按著順序?qū)od進行操作。
當(dāng)然 StatefulSet 還擁有其他特性,在實際的項目中,我們還是很少回去直接通過 StatefulSet 來部署我們的有狀態(tài)服務(wù)的,除非你自己能夠完全能夠 hold 住,對于一些特定的服務(wù),我們可能會使用更加高級的 Operator 來部署,比如 etcd-operator、prometheus-operator 等等,這些應(yīng)用都能夠很好的來管理有狀態(tài)的服務(wù),而不是單純的使用一個 StatefulSet 來部署一個 Pod就行,因為對于有狀態(tài)的應(yīng)用最重要的還是數(shù)據(jù)恢復(fù)、故障轉(zhuǎn)移等等。
以上就是Kubernetes有狀態(tài)應(yīng)用管理StatefulSet使用詳解的詳細內(nèi)容,更多關(guān)于Kubernetes StatefulSet應(yīng)用管理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
K8S-ConfigMap實現(xiàn)應(yīng)用和配置分離詳解
這篇文章主要為大家介紹了K8S-ConfigMap實現(xiàn)應(yīng)用和配置分離詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04淺析k8s中各組件和kube?apiserver通信時的認證和鑒權(quán)問題
這篇文章主要介紹了k8s中各組件和kube?apiserver通信時的認證和鑒權(quán),本文使用的k8s集群是用kubekey搭建,命令是./kk create cluster --with-kubernetes v1.21.5 --with-kubesphere v3.2.1,需要的朋友可以參考下2022-06-06