statefulset詳解及為何結合headless service部署有狀態應用

渡邊灬發表於2023-02-10

1.1 有狀態應用管理statefulset

StatefulSet(有狀態集,縮寫為sts)常用於部署有狀態的且需要有序啟動的應用程式,比如在進行SpringCloud專案容器化時,Eureka的部署是比較適合用StatefulSet部署方式的,可以給每個Eureka例項建立一個唯一且固定的識別符號,並且每個Eureka例項無需配置多餘的Service,其餘Spring Boot應用可以直接透過Eureka的Headless Service即可進行註冊。

1.1.1 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的角色
image
image

1.1.2 StatefulSet注意事項

一般StatefulSet用於有以下一個或者多個需求的應用程式:

  1. 需要穩定的獨一無二的網路識別符號。
    
  2. 需要持久化資料。
    
  3. 需要有序的、優雅的部署和擴充套件。
    
  4. 需要有序的自動滾動更新。
    
    如果應用程式不需要任何穩定的識別符號或者有序的部署、刪除或者擴充套件,應該使用無狀態的控制器部署應用程式,比如Deployment或者ReplicaSet。
    StatefulSet是Kubernetes 1.9版本之前的beta資源,在1.5版本之前的任何Kubernetes版本都沒有。
    Pod所用的儲存必須由PersistentVolume Provisioner(持久化卷配置器)根據請求配置StorageClass,或者由管理員預先配置,當然也可以不配置儲存。
    為了確保資料安全,刪除和縮放StatefulSet不會刪除與StatefulSet關聯的卷,可以手動選擇性地刪除PVC和PV)。
    StatefulSet目前使用Headless Service(無頭服務)負責Pod的網路身份和通訊,需要提前建立此服務。
    刪除一個StatefulSet時,不保證對Pod的終止,要在StatefulSet中實現Pod的有序和正常終止,可以在刪除之前將StatefulSet的副本縮減為0

為什麼要用headless service+statefulSet部署有狀態應用?

Headless Services介紹

Headless Services是一種特殊的service,其spec:clusterIP表示為None,這樣在實際執行時就不會被分配ClusterIP。也被稱為無頭服務。

1、headless Service和普通Service的區別

headless不分配clusterIP

headless service可以透過解析service的DNS,返回所有Pod的地址和域名(statefulSet部署的Pod才有域名)

headless service會為關聯的Pod分配一個域:
service-name.namespace-name.svc.cluster.local

普通的service,只能透過解析service的DNS返回service的ClusterIP

2、statefulSet和Deployment控制器的區別

statefulSet下的Pod有DNS地址,透過解析Pod的DNS可以返回Pod的IP

StatefulSet會為關聯的Pod保持一個不變的Pod Name
statefulset中Pod的hostname格式為
statefulsetname-(pod序號)

而deployment下的Pod沒有具體的域名,想訪問Pod都是透過普通service來負載均衡到後端pod,無法指定訪問具體哪個Pod

3、普通Service解析service的DNS結果

Service的ClusterIP工作原理:一個service可能對應一組endpoints(所有pod的地址+埠),client訪問ClusterIP,透過iptables或者ipvs轉發到Real Server(Pod)。

StatefulSet+headless service會為關聯的每個Pod都分配一個具體的域名:
Pod-Name.service-name.namespace-name.svc.cluster.local

實操部分

1.1.3 定義一個StatefulSet資原始檔

image

點選檢視程式碼
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

image

image
建立個Busybox演示如何透過無頭service名字來進行網路訪問
image
image
透過nslookup來解析web-0.nginx(sts名稱+無頭service名稱),可以得到這個Pod的ip地址:10.244.32.152
image
image
進入叢集busybox容器對web-0.nginx進行Ping和wget發現都是通的
image
image
1.1.4 縮容擴容StatefulSet
檢視sts lable標籤
image
透過標籤對指定sts進行監聽
image
對sts進行擴容操作
image
可以看到sts啟動的順序是嚴格按照序號來進行啟動的,同理縮容的話會嚴格按照pod的序號倒序進行刪除
image
image

1.1.4 StatefulSet更新策略

1. RollingUpdate 更新策略

  • 預設更新策略RollingUpdate
點選檢視程式碼
updateStrategy:
     rollingUpdate:
       partition: 0       #不更新小於 N 的副本
     type: RollingUpdate
更新映象版本

image

image

開啟另一個視窗,觀察Pod更新過程,可以看到他的更新順序是倒序的
image

2. Ondelete 更新策略 [適用於灰度釋出]

更改更新策略為Ondelete

點選檢視程式碼
 修改:
   updateStrategy:
    rollingUpdate:
      partition: 0
    type: RollingUpdate
 
 改為:
   updateStrategy:
    type: OnDelete   #修改為OnDelete更新模式並儲存,該更新策略是,刪除時才會進行更新

image
更新映象版本,並觀察更新過程
image
只有進行delete pod刪除操作的時候,pod才會被更新
image
image

相關文章