有狀態(tài)應(yīng)用管理 StatefulSet

StatefulSet (有狀態(tài)集,縮寫為sts) 常用于部署有狀態(tài)的且需要有序啟動的應(yīng)用程序畜疾,比如在進(jìn)行 SpringCloud 項(xiàng)目容器化時不翩,Eureka 的部署是比較適合用 StatefulSet 部署方式的茶没,可以給每個 Eureka 實(shí)例創(chuàng)建一個唯一且固定的標(biāo)識符筒占,并且每個Eureka 實(shí)例無需配置多余的 Service,其余Spring Boot 應(yīng)用可以直接通過 Eureka 的 Headless Service 即可進(jìn)行注冊蛛株。

StatefulSet 基本概念

StatefulSet 主要用于管理有狀態(tài)應(yīng)用程序的工作負(fù)載 API 對象团赁。比如在生產(chǎn)環(huán)境中,可以部署 ElasticSearch 集群谨履、MongoDB 集群或者需要持久化的 RabbitMQ 集群欢摄、Redis 集群、Kafka 集群和 ZooKeeper 集群等笋粟。

和 Deployment 類似怀挠,一個 StatefulSet 也同樣管理者基于相同容器規(guī)范的 Pod析蝴。不同的是, StatefulSet 為每個 Pod 維護(hù)一個粘性標(biāo)識绿淋。這些 Pod 是根據(jù)相同的規(guī)范創(chuàng)建的闷畸,但是不可互換,每個 Pod 都有一個持久的標(biāo)識符吞滞,在重新調(diào)度時也會保留佑菩,一般格式為 StatefulSetName-Number。比如定義了一個名字是 Redis-Sentinel-0裁赠、Redis-Sentinel-1殿漠、Redis-Sentinel-2。而 StatefulSet 創(chuàng)建的 Pod 一般使用 Headless Service (無頭服務(wù))進(jìn)行通信佩捞,和普通的 Service 的區(qū)別在于 Headless Service 沒有 ClusterIP绞幌,它使用的是 Endpoint 進(jìn)行互相通信,Headless 一般的格式為:

statefulSetName-{0..N-1}.serivceName.namespace.svc.cluster.local

說明:

  • serviceName:Headless Service 的名字一忱,創(chuàng)建 StatefulSet 時莲蜘,必須指定 Headless Service 名稱。
  • 0..N-1 : Pod 所在的序號帘营,從 0 開始到 N-1
  • statefulSetName:StatefulSet 的名字
  • namespace:服務(wù)所在的命名空間
  • .cluster.local : Cluster Domain (集群域)

假如公司某個項(xiàng)目需要再 Kubernetes 中部署一個主從模式的 Redis菇夸,此時使用 StatefulSet 部署就極為合適,因?yàn)?StatefulSet 啟動時仪吧,只有當(dāng)前一個容器完全啟動時,后一個容器才會被調(diào)度鞠眉,并且每個容器的標(biāo)識符是固定的薯鼠,那么就可以通過標(biāo)識符來斷定當(dāng)前 Pod 的角色。

比如用一個名為 redis-ms 的 StatefulSet 部署主從架構(gòu)的 Redis械蹋,第一個容器啟動時出皇,它的標(biāo)識符為 redis-ms-0,并且 Pod 內(nèi)主機(jī)名也為 redis-ms-0哗戈,此時就可以根據(jù)主機(jī)名來判斷郊艘,當(dāng)主機(jī)名為 redis-ms-0 的容器作為 Redis 的主節(jié)點(diǎn),其余從節(jié)點(diǎn)唯咬,那么 Slave 連接 Master 主機(jī)配置就可以使用不會更改的 Master 的 Headless Serivce纱注,此時 Redis 從節(jié)點(diǎn)(Slave)配置文件如下:

port 6379
slaveof redis-ms-0.redis-ms.public-service.svc.cluster.local 6379
tcp-backlog 511
timeout 0
tcp-keeplive 0
...

其中 redis-ms-0.redis-ms.public-service.svc.cluster.local 是 Redis Master 的 Headless Service,在同一命名空間下只需要寫 redis-ms-0.redis-ms 即可胆胰,后面的 public-service.svc.cluster.local 可以省略狞贱。

StatefulSet 注意事項(xiàng)

一般 StatefulSet 用于有以下一個或者多個需求的應(yīng)用程序:

  • 需要穩(wěn)定的獨(dú)一無二的網(wǎng)絡(luò)標(biāo)識符
  • 需要持久化數(shù)據(jù)
  • 需要有序的、優(yōu)雅的部署和擴(kuò)展
  • 需要有序的自動滾動更新

如果應(yīng)用程序不需要任何穩(wěn)定的標(biāo)識符或者有序的部署蜀涨、刪除或者擴(kuò)展瞎嬉,應(yīng)該使用無狀態(tài)的控制器部署應(yīng)用程序蝎毡,比如 Deployment 或者 ReplicaSet。

StatefulSet 是 Kubernetes 1.9 版本以前的 beta 資源氧枣,在1.5 版本之前的任何 Kubernetes 版本都沒有沐兵。

Pod 所用的存儲必須由 PersistenVolume Provisioner (持久化卷配置器)根據(jù)請求配置 StorageClass,或者由管理員預(yù)先配置便监,當(dāng)然也可以不配置存儲扎谎。
為了確保數(shù)據(jù)安全,刪除和縮放 StatefulSet 不會刪除與 StatefulSet 關(guān)聯(lián)的卷茬贵,可以手動選擇性地刪除 PVC 和PV簿透。

StatefulSet 目前使用 Headless Service (無頭服務(wù))負(fù)責(zé) Pod 的網(wǎng)絡(luò)身份和通信,需要提前創(chuàng)建此服務(wù)解藻。
刪除一個 StatefulSet 時老充,不保證對 Pod 的終止,要在 StatefulSet 中實(shí)現(xiàn) Pod 的有序和正常終止螟左,可以在刪除之前將 StatefulSet 的副本縮減為 0啡浊。

定義一個 StatefulSet 資源文件

定義一個簡單的 StatefulSet 的示例如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
    - port: 80
      name: web
  clusterIP: None
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80
              name: web

其中:

  • kind:Service 定義了一個名字為 Nginx 的 Headless Service,創(chuàng)建的 Service格式為 nginx-0.nginx.default.svc.cluster.local胶背,因?yàn)闆]有指定 Namespace (命名空間)巷嚣,所以默認(rèn)部署在 default。
  • kind:StatefulSet 定義了一個名字為 web 的StatefulSet钳吟,replicas 表示部署 Pod 的副本數(shù)廷粒,本實(shí)例為2。

在 StatefulSet 中必須設(shè)置 Pod 選擇器(.spec.selector)用來匹配其標(biāo)簽(.spec.template.metadata.labels)红且。在1.8版本之前坝茎,如果未配置該字段(.spec.selector),將被設(shè)置為默認(rèn)值暇番。在1.8版本之后嗤放,如果為指定匹配Pod Selector,則會導(dǎo)致 StatefulSet 創(chuàng)建錯誤壁酬。

當(dāng) StatefulSet 控制器創(chuàng)建 Pod 時次酌,它會添加一個標(biāo)簽 statefulset.kubernetes.io/pod-name , 該標(biāo)簽的值為 Pod的名稱舆乔,用于匹配 Service岳服。

使用 kubectl apply 創(chuàng)建

kubectl apply -f statefulset.yaml

創(chuàng)建 busybox 驗(yàn)證

apiVersion: v1
kind: Pod
metadata:
  name: busybox
spec:
  containers:
    - name: busybox
      image: busybox:1.28.4
      command:
        - sleep
        - "3600"
      resources:
        limits:
          memory: "128Mi"
          cpu: "500m"
  restartPolicy: Always
$ kubectl exec -it busybox -- sh
/ # nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.1.0.198 web-0.nginx.default.svc.cluster.local
/ # nslookup web-1.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.1.0.199 web-1.nginx.default.svc.cluster.local
$ kubectl get pods -o wide
NAME      READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
busybox   1/1     Running   0          6m52s   10.1.0.202   docker-desktop   <none>           <none>
web-0     1/1     Running   0          14m     10.1.0.198   docker-desktop   <none>           <none>
web-1     1/1     Running   0          14m     10.1.0.199   docker-desktop   <none>           <none>

nslookup 命令的輸出結(jié)果中,我們可以看到希俩,在訪問 web-0.nginx 的時候派阱,最后解析
到的,正是 web-0 這個 Pod 的 IP 地址斜纪;而當(dāng)訪問 web-1.nginx 的時候贫母,解析到的則是
web-1 的 IP 地址文兑。

如果你把這兩個 StatefulSet 的 Pod 刪除掉

$ kubectl delete pod -l app=nginx
pod "web-0" deleted
pod "web-1" deleted

再查看這兩個Pod 的狀態(tài)變化

$ kubectl get pod -w -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-0   0/1     ContainerCreating   0          0s
web-0   1/1     Running             0          5s
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          4s

當(dāng)我們刪除這兩個Pod 后,Kubernetes 會按照原來的編號的順序腺劣,創(chuàng)建出兩個新的Pod绿贞。依舊可以使用 web-0.nginxweb-1.nginx 訪問,StatefuleSet 保證了 Pod 網(wǎng)絡(luò)標(biāo)識的穩(wěn)定性

$ kubectl exec -it busybox -- sh
/ # nslookup web-0.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-0.nginx
Address 1: 10.1.0.209 web-0.nginx.default.svc.cluster.local
/ # nslookup web-1.nginx
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      web-1.nginx
Address 1: 10.1.0.210 web-1.nginx.default.svc.cluster.local

$ kubectl get pod -l app=nginx -owide
NAME    READY   STATUS    RESTARTS   AGE     IP           NODE             NOMINATED NODE   READINESS GATES
web-0   1/1     Running   0          7m21s   10.1.0.209   docker-desktop   <none>           <none>
web-1   1/1     Running   0          7m16s   10.1.0.210   docker-desktop   <none>           <none>

擴(kuò)容縮容

$ kubectl scale --replicas=3 sts web
statefulset.apps/web scaled
$ kubectl get pods
NAME      READY   STATUS    RESTARTS      AGE
web-0     1/1     Running   0             40m
web-1     1/1     Running   0             40m
web-2     1/1     Running   0             11m

$ kubectl scale --replicas=2 sts web
statefulset.apps/web scaled
$ kubectl get pods
NAME      READY   STATUS    RESTARTS      AGE
web-0     1/1     Running   0             41m
web-1     1/1     Running   0             41m

StatefulSet 更新策略

更新策略:

  • rollingUpdate: 當(dāng)updateStrategy的值被設(shè)置為RollingUpdate時橘原,StatefulSet Controller會刪除并創(chuàng)建StatefulSet相關(guān)的每個Pod對象籍铁,其處理順序與StatefulSet終止Pod的順序一致,即從序號最大的Pod開始重建趾断,每次更新一個Pod拒名。

  • onDeleted:當(dāng)updateStrategy的值被設(shè)置為OnDelete時,StatefulSet Controller并不會自動更新StatefulSet中的Pod實(shí)例芋酌,而是需要用戶手動刪除這些Pod并觸發(fā)StatefulSet Controller創(chuàng)建新的Pod實(shí)例來彌補(bǔ)增显,因此這其實(shí)是一種手動升級模式。

  • Partitioned : updateStrategy也支持特殊的分區(qū)升級策略(Partitioned)脐帝,在這種模式下同云,用戶指定一個序號,StatefulSet中序號大于等于此序號的Pod實(shí)例會全部被升級堵腹,小于此序號的Pod實(shí)例則保留舊版本不變炸站,即使這些Pod被刪除、重建疚顷,也仍然保持原來的舊版本旱易。這種分區(qū)升級策略通常用于按計劃分步驟的系統(tǒng)升級過程中。

灰度發(fā)布

灰度發(fā)布(又名金絲雀發(fā)布)是指在黑與白之間腿堤,能夠平滑過渡的一種發(fā)布方式咒唆。

Partitioned 可以用于灰度發(fā)布

$ kubectl edit sts web
updateStrategy:
    rollingUpdate:
      partition: 2
    type: RollingUpdate
    
#修改 yaml文件
image: nginx:1.23.1

$ kubectl apply -f nginx.yaml

$ kubectl get pod web-2 -oyaml | grep image
  - image: nginx:1.23.1
    imagePullPolicy: Always
    image: nginx:1.23.1
    imageID: docker-pullable://nginx@sha256:1761fb5661e4d77e107427d8012ad3a5955007d997e0f4a3d41acc9ff20467c7

$ kubectl get pod web-0 -oyaml | grep image
  - image: nginx
    imagePullPolicy: Always
    image: nginx:latest
    imageID: docker-pullable://nginx@sha256:1761fb5661e4d77e107427d8012ad3a5955007d997e0f4a3d41acc9ff20467c7

可以看出,序號大于等于2释液,都更新了,實(shí)現(xiàn)了灰度發(fā)布

級聯(lián)刪除和非級聯(lián)刪除

級聯(lián)刪除:刪除 StatefulSet 同時刪除 Pod
非級聯(lián)刪除:刪除 StatefulSet 不刪除 Pod

# 級聯(lián)刪除
$ kubectl delete sts web
statefulset.apps "web" deleted
# 非級聯(lián)刪除
$ kubectl delete sts web --cascade=false
warning: --cascade=false is deprecated (boolean value) and can be replaced with --cascade=orphan.
statefulset.apps "web" deleted

# 刪除 statefulset 后装处,再刪除pod误债,pod不會重新創(chuàng)建
$ kubectl delete pod web-0 web-1
pod "web-0" deleted
pod "web-1" deleted

$ kubectl get pods -l app=nginx
NAME    READY   STATUS    RESTARTS   AGE
web-2   1/1     Running   0          4m31s
web-3   1/1     Running   0          4m28s
web-4   1/1     Running   0          4m26s
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市妄迁,隨后出現(xiàn)的幾起案子寝蹈,更是在濱河造成了極大的恐慌,老刑警劉巖登淘,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箫老,死亡現(xiàn)場離奇詭異,居然都是意外死亡黔州,警方通過查閱死者的電腦和手機(jī)耍鬓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門阔籽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人牲蜀,你說我怎么就攤上這事笆制。” “怎么了涣达?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵在辆,是天一觀的道長。 經(jīng)常有香客問我度苔,道長匆篓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任寇窑,我火速辦了婚禮鸦概,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘疗认。我一直安慰自己完残,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布横漏。 她就那樣靜靜地躺著谨设,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缎浇。 梳的紋絲不亂的頭發(fā)上扎拣,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音素跺,去河邊找鬼二蓝。 笑死,一個胖子當(dāng)著我的面吹牛指厌,可吹牛的內(nèi)容都是我干的刊愚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼踩验,長吁一口氣:“原來是場噩夢啊……” “哼鸥诽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箕憾,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤牡借,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后袭异,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钠龙,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了碴里。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沈矿。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖并闲,靈堂內(nèi)的尸體忽然破棺而出细睡,到底是詐尸還是另有隱情,我是刑警寧澤帝火,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布溜徙,位于F島的核電站,受9級特大地震影響犀填,放射性物質(zhì)發(fā)生泄漏蠢壹。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一九巡、第九天 我趴在偏房一處隱蔽的房頂上張望图贸。 院中可真熱鬧,春花似錦冕广、人聲如沸疏日。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沟优。三九已至,卻和暖如春睬辐,著一層夾襖步出監(jiān)牢的瞬間挠阁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工溯饵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侵俗,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓丰刊,卻偏偏與公主長得像隘谣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子啄巧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容