1. 程式人生 > 其它 >statefulset有狀態應用管理

statefulset有狀態應用管理

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