statefulset有狀態應用管理
阿新 • • 發佈:2022-06-01
statefulset介紹
StatefulSet(有狀態集,縮寫為sts)常用於部署有狀態的且需要有序啟動的應用程式,比如在進行SpringCloud專案容器化時,Eureka的部署是比較適合用StatefulSet部署方式的,可以給每個Eureka例項建立一個唯一且固定的識別符號,並且每個Eureka例項無需配置多餘的Service,其餘Spring Boot應用可以直接通過Eureka的Headless Service即可進行註冊。
Eureka的statefulset的資源名稱是eureka,
eureka-0 eureka-1 eureka-2
Service:headless service,沒有ClusterIP eureka-svc
Eureka-0.eureka-svc.NAMESPACE_NAME eureka-1.eureka-svc
StatefulSet的基本概念
StatefulSet主要用於管理有狀態應用程式的工作負載API物件。比如在生產環境中,可以部署ElasticSearch叢集、MongoDB叢集或者需要持久化的RabbitMQ叢集、Redis叢集、Kafka叢集和ZooKeeper叢集等。 和Deployment類似,一個StatefulSet也同樣管理著基於相同容器規範的Pod。不同的是,StatefulSet為每個Pod維護了一個粘性標識。這些Pod是根據相同的規範建立的,但是不可互換,每個Pod都有一個持久的識別符號,在重新排程時也會保留,一般格式為StatefulSetName-Number。比如定義一個名字是Redis-Sentinel的StatefulSet,指定建立三個Pod,那麼創建出來的Pod名字就為Redis-Sentinel-0、Redis-Sentinel-1、Redis-Sentinel-2。而StatefulSet建立的Pod一般使用Headless Service(無頭服務)進行通訊,和普通的Service的區別在於Headless Service沒有ClusterIP,它使用的是Endpoint進行互相通訊,Headless一般的格式為: statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local。 說明: serviceName為Headless Service的名字,建立StatefulSet時,必須指定Headless Service名稱; 0..N-1為Pod所在的序號,從0開始到N-1; statefulSetName為StatefulSet的名字; namespace為服務所在的名稱空間; .cluster.local為Cluster Domain(叢集域) 假如公司某個專案需要在Kubernetes中部署一個主從模式的Redis,此時使用StatefulSet部署就極為合適,因為StatefulSet啟動時,只有當前一個容器完全啟動時,後一個容器才會被排程,並且每個容器的識別符號是固定的,那麼就可以通過識別符號來斷定當前Pod的角色
StatefulSet注意事項
一般StatefulSet用於有以下一個或者多個需求的應用程式: 需要穩定的獨一無二的網路識別符號。 需要持久化資料。 需要有序的、優雅的部署和擴充套件。 需要有序的自動滾動更新。 如果應用程式不需要任何穩定的識別符號或者有序的部署、刪除或者擴充套件,應該使用無狀態的控制器部署應用程式,比如Deployment或者ReplicaSet。 StatefulSet是Kubernetes 1.9版本之前的beta資源,在1.5版本之前的任何Kubernetes版本都沒有。 Pod所用的儲存必須由PersistentVolume Provisioner(持久化卷配置器)根據請求配置StorageClass,或者由管理員預先配置,當然也可以不配置儲存。 為了確保資料安全,刪除和縮放StatefulSet不會刪除與StatefulSet關聯的卷,可以手動選擇性地刪除PVC和PV(關於PV和PVC請參考2.2.12節)。 StatefulSet目前使用Headless Service(無頭服務)負責Pod的網路身份和通訊,需要提前建立此服務。 刪除一個StatefulSet時,不保證對Pod的終止,要在StatefulSet中實現Pod的有序和正常終止,可以在刪除之前將StatefulSet的副本縮減為0
定義一個StatefulSet資原始檔
定義一個簡單的StatefulSet的示例如下:
Cat StatefulSet-1.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None #這裡可以有IP,也可以無IP,推薦無IP,也就是 無頭service
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.15.2
ports:
- containerPort: 80
name: web
此示例沒有新增儲存配置,後面的章節會單獨講解儲存相關的知識點
建立一個StatefulSet
# 建立一個sts
[root@k8s-m01 yaml-file]# kubectl create -f StatefulSet-1.yaml #-n namespace_name
service/nginx created
statefulset.apps/web created
[root@k8s-m01 yaml-file]# kubectl get sts
NAME READY AGE
web 2/2 3s
[root@k8s-m01 yaml-file]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 73s
web-1 1/1 Running 0 72s
[root@k8s-m01 yaml-file]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 192.168.0.1 <none> 443/TCP 37d
nginx ClusterIP None <none> 80/TCP 2m59s
# <<--沒有ClusterIP的service 簡稱 '無頭service'
無頭service
演示案例:
cat sts-busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
#啟動進入 busybox ping StatefulSet的web節點,理解如何解析無頭service
[root@k8s-m01 yaml-file]# kubectl exec -it busybox -- sh
#nslookup解析
/ # nslookup web-0.nginx
Server: 192.168.0.10
Address 1: 192.168.0.10 kube-dns.kube-system.svc.cluster.local
Name: web-0.nginx
Address 1: 172.16.57.124 web-0.nginx.default.svc.cluster.local
可以看到 可以直接ping通 service的名字
縮容擴容StatefulSet
[root@k8s-m01 yaml-file]# kubectl scale --replicas=3 sts web
statefulset.apps/web scaled
[root@k8s-m01 yaml-file]# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-55bbd66bbd-6n9jl 1/1 Running 0 9h
nginx-55bbd66bbd-9gxhf 1/1 Running 0 9h
web-0 1/1 Running 0 6m54s
web-1 1/1 Running 0 6m53s
web-2 1/1 Running 0 4s
[root@k8s-m01 yaml-file]# kubectl scale --replicas=2 sts web
statefulset.apps/web scaled
[root@k8s-m01 yaml-file]# kubectl get pod -o wide|grep web
web-0 1/1 Running 0 29m 172.16.57.124 k8s-n02
web-1 1/1 Running 0 29m 172.17.207.167 k8s-n01
#檢視sts標籤
[root@k8s-m01 yaml-file]# kubectl get pod --show-labels |grep web
web-0 1/1 Running 0 30m app=nginx,controller-revision-hash=web-6949d64dc8,statefulset.kubernetes.io/pod-name=web-0
web-1 1/1 Running 0 30m app=nginx,controller-revision-hash=web-6949d64dc8,statefulset.kubernetes.io/pod-name=web-1
#過濾標籤,找到屬於某個標籤下的pod
[root@k8s-m01 yaml-file]# kubectl get pod -l controller-revision-hash=web-6949d64dc8
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 32m
web-1 1/1 Running 0 32m
#檢視擴縮容過程:
#執行命令監視pod變化動態
kubectl get pod -l controller-revision-hash=web-6949d64dc8 -w
#擴容sts
[root@k8s-m01 ~]# kubectl scale --replicas=5 statefulset web
statefulset.apps/web scaled
#檢查狀態:
[root@k8s-m01 yaml-file]# kubectl get pod -l controller-revision-hash=web-6949d64dc8 -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 42m
web-1 1/1 Running 0 42m
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 2s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 1/1 Running 0 1s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 1/1 Running 0 1s
可以看到擴容時,web序號呈現遞增更新
#縮容sts
[root@k8s-m01 ~]# kubectl scale --replicas=3 statefulset web
statefulset.apps/web scaled
[root@k8s-m01 yaml-file]# kubectl get pod -l controller-revision-hash=web-6949d64dc8 -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 46m
web-1 1/1 Running 0 46m
web-2 1/1 Running 0 115s
web-3 1/1 Running 0 113s
web-4 1/1 Running 0 112s
web-4 1/1 Terminating 0 2m1s
web-4 0/1 Terminating 0 2m1s
web-4 0/1 Terminating 0 2m2s
web-4 0/1 Terminating 0 2m2s
web-3 1/1 Terminating 0 2m3s
web-3 0/1 Terminating 0 2m4s
web-3 0/1 Terminating 0 2m4s
web-3 0/1 Terminating 0 2m4s
可以看到縮容時,web序號呈現倒序縮減
#刪除sts檢視流程
[root@k8s-m01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 53m
web-1 1/1 Running 0 53m
web-2 1/1 Running 0 21s
[root@k8s-m01 ~]# kubectl delete pod web-2
pod "web-2" deleted
[root@k8s-m01 yaml-file]# kubectl get pod -l controller-revision-hash=web-6949d64dc8 -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 53m
web-1 1/1 Running 0 53m
web-2 1/1 Running 0 13s
web-2 1/1 Terminating 0 31s
web-2 0/1 Terminating 0 31s
web-2 0/1 Terminating 0 31s
web-2 0/1 Terminating 0 31s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 2s
# sts 會保證副本數達到期望值,刪除一個副本後會重新拉起一個新的副本
[root@k8s-m01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 55m
web-1 1/1 Running 0 55m
web-2 1/1 Running 0 87s #<=== 重新拉起的新副本
注意:
StatefulSet的pod也會被命名為 pod的name
[root@k8s-m01 ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 55m
web-1 1/1 Running 0 55m
web-2 1/1 Running 0 87s
[root@k8s-m01 ~]# kubectl exec -it web-0 -- sh
# hostname
web-0
StatefulSet更新策略
1. RollingUpdate 更新策略
#監視 statefulset
kubectl get pod -l statefulset.kubernetes.io/pod-name
#修改nginx版本讓它進行滾動更新
[root@k8s-m01 ~]# kubectl edit sts web
#預設更新策略RollingUpdate
updateStrategy:
rollingUpdate:
partition: 0 #不更新小於 N 的副本
type: RollingUpdate
#檢查更新過程
[root@k8s-m01 ~]# kubectl get pod -l statefulset.kubernetes.io/pod-name -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8m44s
web-1 1/1 Running 0 8m47s
web-2 1/1 Running 0 8m50s
web-2 1/1 Terminating 0 9m26s
web-2 0/1 Terminating 0 9m27s
web-2 0/1 Terminating 0 9m27s
web-2 0/1 Terminating 0 9m27s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 2s
web-1 1/1 Terminating 0 9m26s
web-1 0/1 Terminating 0 9m27s
web-1 0/1 Terminating 0 9m27s
web-1 0/1 Terminating 0 9m27s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 0s
web-1 1/1 Running 0 1s
web-0 1/1 Terminating 0 9m25s
web-0 0/1 Terminating 0 9m26s
web-0 0/1 Terminating 0 9m26s
web-0 0/1 Terminating 0 9m26s
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 1s
#可以看到更新過程會先從 web-2 更新,倒序更新
2. Ondelete 更新策略 [適用於灰度釋出]
[root@k8s-m01 yaml-file]# kubectl edit sts web
修改:
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
改為:
updateStrategy:
type: OnDelete #修改為OnDelete更新模式並儲存,該更新策略是,刪除時才會進行更新
#檢查策略更新過程
開啟一個視窗進行監視pod過程
# kubectl get pod -l statefulset.kubernetes.io/pod-name -w
#此時修改一個映象版本,看是否像之前一樣會被更新
- image: nginx:1.15.1 修改為 - image: nginx:1.15.3
#這時候另一個視窗完全沒有變化
#刪除pod檢視視窗變化:
[root@k8s-m01 yaml-file]# kubectl get pod -l statefulset.kubernetes.io/pod-name
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 8m31s
web-1 1/1 Running 0 8m34s
web-2 1/1 Running 0 8m37s
[root@k8s-m01 yaml-file]# kubectl delete pod web-0
pod "web-0" deleted
#另一個視窗的變化:
[root@k8s-m01 ~]# kubectl get pod -l statefulset.kubernetes.io/pod-name -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 11m
web-1 1/1 Running 0 11m
web-2 1/1 Running 0 12m
web-0 1/1 Terminating 0 15m
web-0 0/1 Terminating 0 15m
web-0 0/1 Terminating 0 15m
web-0 0/1 Terminating 0 15m
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 1s
可以看到這時候web-0已經開始發生變化,這時候檢查該pod版本是否被更新:
[root@k8s-m01 yaml-file]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-0 1/1 Running 0 64s 172.17.207.176 k8s-n01 <none> <none> #<--被更新
web-1 1/1 Running 0 16m 172.17.207.174 k8s-n01 <none> <none>
web-2 1/1 Running 0 16m 172.16.57.68 k8s-n02 <none> <none>
#檢查版本:
kubectl get pod web-0 -o yaml|grep image #也可以用這個命令來看版本
[root@k8s-m01 yaml-file]# kubectl exec -it web-0 -- sh
# nginx -v
nginx version: nginx/1.15.3
[root@k8s-m01 yaml-file]# kubectl exec -it web-1 -- sh
# nginx -v
nginx version: nginx/1.15.2
[root@k8s-m01 yaml-file]# kubectl exec -it web-2 -- sh
# nginx -v
nginx version: nginx/1.15.2
結論:
OnDelete策略只會在pod被刪除時進行更新版本,該方法在進行灰度釋出時效果較好
yaml中引數 partition含義 #分段更新配置
updateStrategy
rollingUpdate:
partition: 2 #副本大於設定的值時進行更新,也就是分段更新,這裡是大於2的副本更新
type: RollingUpdate
partition: 2 #不更新小於 N 的副本
案例:
修改當前statefulset配置新增更新引數:
kubectl edit sts web
找到:
updateStrategy:
type: OnDelete
修改為:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
順便修改映象版本:
- image: nginx:1.15.3 修改為 - image: nginx:1.15.2
新開視窗監視變化
kubectl get pod -l statefulset.kubernetes.io/pod-name -w
修改映象版本進行更新測試 partition: 2 引數
#這裡可以看到 保留2個副本後,只有web-2進行了自動更新,web-0 web-1都沒有更新
[root@k8s-m01 ~]# kubectl get pod -l statefulset.kubernetes.io/pod-name -w
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 21m
web-1 1/1 Running 0 22m
web-2 1/1 Running 0 21m
web-2 1/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Terminating 0 22m
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 1s
#檢查更新結果:
[root@k8s-m01 yaml-file]# kubectl get pod -l statefulset.kubernetes.io/pod-name -o yaml|grep image
- image: nginx:1.15.3
imagePullPolicy: IfNotPresent
image: nginx:1.15.3
imageID: docker-pullable://nginx@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
- image: nginx:1.15.3
imagePullPolicy: IfNotPresent
image: nginx:1.15.3
imageID: docker-pullable://nginx@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
- image: nginx:1.15.2
imagePullPolicy: IfNotPresent
image: nginx:1.15.2
imageID: docker-pullable://nginx@sha256:d85914d547a6c92faa39ce7058bd7529baacab7e0cd4255442b04577c4d1f424
可以看到只有一個nginx進行了更新到了 1.15.2
這就是 partition 引數的作用,在進行灰度釋出時作用較大
statefulset刪除
statefulset刪除分為兩種
級聯刪除[預設]
刪除statefulset時刪除pod
非級聯刪除
刪除statefulset時不刪除pod
級聯刪除:
預設級聯刪除時 會刪除statefulset和pod
[root@k8s-m01 yaml-file]# kubectl delete sts web
statefulset.apps "web" deleted
[root@k8s-m01 yaml-file]# kubectl delete svc nginx
service "nginx" deleted
[root@k8s-m01 yaml-file]# kubectl get pod -l statefulset.kubernetes.io/pod-name
No resources found in default namespace. #刪除後 web這個sts pod 已經沒了
非級聯刪除:
[root@k8s-m01 yaml-file]# kubectl create -f StatefulSet-1.yaml
service/nginx created
statefulset.apps/web created
[root@k8s-m01 yaml-file]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 7m
web-1 1/1 Running 0 5m
非級聯刪除:
[root@k8s-m01 yaml-file]# kubectl delete sts web --cascade=orphan
statefulset.apps "web" deleted
檢查:
[root@k8s-m01 yaml-file]# kubectl get sts
No resources found in default namespace.
#此時這些pod會變成 孤兒pod,可以刪除並不會被重建
[root@k8s-m01 yaml-file]# kubectl get pod
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 7m
web-1 1/1 Running 0 5m
非級聯刪除時,刪除了sts 但是pod並未被刪除
#kubernetes 1.24以前版本引數 --cascade=false
#kubernetes 1.24以後版本引數 --cascade=orphan