StatefulSet 的擴充套件和縮減操作與 Deployment 不同,Pod 會按照預定的順序建立和終止,確保應用程式狀態的一致性。擴充套件時,新 Pod 會依次建立,直到所有前置 Pod 處於 Running 和 Ready 狀態。縮減時,Pod 也會依次終止,同樣需要等待所有前置 Pod 完全終止。此外,podManagementPolicy 引數控制 Pod 的管理策略,OrderedReady 保證順序執行,而 Parallel 則允許平行操作。更新 StatefulSet 時,RollingUpdate 策略會按照逆序逐一更新 Pod,確保應用程式在更新過程中保持可用性。OnDelete 策略則需要手動刪除 Pod 以觸發更新,適用於需要額外驗證或手動干預的場景。在刪除 StatefulSet 時,可以選擇是否保留 Pod,但 PVC 和 PV 預設不會被刪除,需要手動清理以釋放儲存資源。
擴充套件 StatefulSet
在 StatefulSet 的情況下,您可以透過更改規格中的副本數量或使用 kubectl scale 命令來執行與 Deployment 物件類別似的擴充套件操作。新的 Pod 將自動被發現為 Service 的新端點(Endpoints),當您擴充套件時,或者在縮減時自動從端點列表中移除。
然而,與 Deployment 物件相比,有一些差異:
- 當您佈署一個具有 N 個副本的 StatefulSet 物件時,在佈署期間,Pod 會按照順序從 0 到 N-1 建立。在我們的例子中,在建立具有三個副本的 StatefulSet 物件期間,首先建立
mysql-stateful-0Pod,然後是mysql-stateful-1,最後是mysql-stateful-2。 - 當您擴充套件 StatefulSet 時,新的 Pod 也會按照順序建立。
- 當您縮減 StatefulSet 時,Pod 會按照相反的順序,從 N-1 到 0 終止。在我們的例子中,當縮減 StatefulSet 物件到零個副本時,
mysql-stateful-2Pod 首先被終止,然後是mysql-stateful-1,最後是mysql-stateful-0。 - 在擴充套件 StatefulSet 物件期間,在建立下一個 Pod 之前,它的所有前驅 Pod 必須正在執行並且就緒。
- 在縮減 StatefulSet 物件期間,在終止下一個 Pod 之前,它的所有前驅 Pod 必須完全終止並刪除。
- 此外,通常在對 StatefulSet 物件中的 Pod 執行任何擴充套件操作之前,它的所有前驅 Pod 必須正在執行並且就緒。這意味著,如果在從四個副本縮減到一個副本的過程中,
mysql-stateful-0Pod 突然失敗,則不會對mysql-stateful-1、mysql-stateful-2和mysql-stateful-3Pod 執行進一步的擴充套件操作。當mysql-stateful-0Pod 再次就緒時,擴充套件操作將還原。
具備了這些知識後,讓我們透過命令列示範如何快速擴充套件我們的 StatefulSet:
- 使用以下命令擴充套件 StatefulSet:
$ kubectl scale statefulset -n mysql mysql-stateful --replicas 4
statefulset.apps/mysql-stateful scaled
這種順序的擴充套件行為可以透過更改規格中的 .spec.podManagementPolicy 欄位來放鬆。預設值是 OrderedReady。如果將其更改為 Parallel,則擴充套件操作將與 Deployment 物件類別似地平行執行。
- 如果您現在使用
kubectl get pods命令檢查 Pod,您將看到新的 Pod 按順序建立:
$ kubectl get pod -n mysql
NAME READY STATUS RESTARTS AGE
k8sutils 1/1 Running 0 56m
mysql-stateful-0 1/1 Running 0 9m13s
mysql-stateful-1 1/1 Running 0 9m12s
mysql-stateful-2 1/1 Running 0 9m10s
mysql-stateful-3 1/1 Running 0 4s
同樣,如果您檢查 StatefulSet 物件的 kubectl describe 命令輸出,您將在事件中看到以下內容:
$ kubectl describe sts -n mysql mysql-stateful
Name: mysql-stateful
Namespace: mysql
...<removed for brevity>...
Events:
Type Reason Age From Message
---
-
---
---
---
-
---
-
---
-
---
Normal SuccessfulCreate 23m (x2 over 75m) statefulset-controller create Pod mysql-stateful-0 in StatefulSet mysql-stateful successful
Normal RecreatingTerminatedPod 11m (x13 over 23m) statefulset-controller StatefulSet mysql/mysql-stateful is recreating terminated Pod mysql-stateful-0
Normal SuccessfulDelete 11m (x13 over 23m) statefulset-controller delete Pod mysql-stateful-0 in StatefulSet mysql-stateful successful
Normal SuccessfulCreate 2m28s statefulset-controller create Claim mysql-data-mysql-stateful-3 Pod mysql-stateful-3 in StatefulSet mysql-stateful success
Normal SuccessfulCreate 2m28s statefulset-controller create Pod mysql-stateful-3 in StatefulSet mysql-stateful successful
讓我們使用命令列示範如何縮減我們的 StatefulSet 物件並檢查 Pod:
$ kubectl scale statefulset -n mysql mysql-stateful --replicas 2
statefulset.apps/mysql-stateful scaled
您可以看到,最後兩個 Pod – mysql-stateful-3 和 mysql-stateful-2 – 按順序被刪除。現在,讓我們檢查 StatefulSet 中的 Pod:
$ kubectl get pod -n mysql
NAME READY STATUS RESTARTS AGE
k8sutils 1/1 Running 0 61m
mysql-stateful-0 1/1 Running 0 15m
mysql-stateful-1 1/1 Running 0 15m
- 現在檢查 PVC,您將看到 PVC 仍然存在。這是 StatefulSet 的預期行為,正如我們之前所學到的:
$ kubectl get pvc -n mysql
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
mysql-data-mysql-stateful-0 Bound pvc-453dbfee-6076-48b9-8878-e7ac6f79d271 1Gi RWO standard <unset> 79m
mysql-data-mysql-stateful-1 Bound pvc-36494153-3829-42aa-be6d-4dc63163ea38 1Gi RWO standard <unset> 79m
mysql-data-mysql-stateful-2 Bound pvc-6730af33-f0b6-445d-841b-4fbad5732cde 1Gi RWO standard <unset> 79m
mysql-data-mysql-stateful-3 Bound pvc-6ec1ee2a-5be3-4bf9-84e5-4f5aee566c11 1Gi RWO standard <unset> 7m4s
程式碼解密:
上述範例展示瞭如何使用 kubectl scale 命令來擴充套件和縮減 StatefulSet。以下是關鍵步驟的解析:
- 使用
kubectl scale命令更改 StatefulSet 的副本數量,以擴充套件或縮減 Pod 的數量。 - StatefulSet 中的 Pod 是按順序建立或終止的,這與 Deployment 物件不同。
- 當擴充套件或縮減 StatefulSet 時,需要考慮
.spec.podManagementPolicy的設定,以決定是否按順序或平行執行操作。
刪除 StatefulSet
要刪除 StatefulSet,有兩種可能性:
- 刪除 StatefulSet 以及它擁有的 Pod。
- 刪除 StatefulSet 但保留 Pod 不變。
在這兩種情況下,預設情況下不會刪除使用 volumeClaimTemplates 為 Pod 建立的 PVC 和 PV。這樣可以確保狀態資料不會意外丟失,除非您明確清理 PVC 和 PV。
程式碼解密:
刪除 StatefulSet 時需要注意以下事項:
- 可以選擇是否刪除 StatefulSet 所擁有的 Pod。
- PVC 和 PV 不會被自動刪除,以保留狀態資料。
- 在使用 Horizontal Pod Autoscaler(HPA)等水平擴充套件工具管理 StatefulSet 時,應避免在 manifest 中指定
.spec.replicas的值,而應讓 Kubernetes 控制平面根據資源需求動態調整副本數量。
StatefulSet:佈署有狀態應用程式
在 Kubernetes 中,StatefulSet 是一種用於管理有狀態應用程式的控制器。與 Deployment 不同,StatefulSet 能夠保證 Pod 的身份和順序,這對於需要持久化儲存和有序佈署的應用程式來說至關重要。
管理 StatefulSet 的生命週期
在最新的 Kubernetes 版本(從 v1.27 開始),可以使用 .spec.persistentVolumeClaimRetentionPolicy 欄位來控制 StatefulSet 生命週期中 PVC 的刪除。
apiVersion: apps/v1
kind: StatefulSet
...
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Delete
...
內容解密:
apiVersion和kind定義了 Kubernetes 資源的版本和型別。.spec.persistentVolumeClaimRetentionPolicy控制 PVC 在 StatefulSet 被刪除或縮減時的行為。whenDeleted: Retain表示當 StatefulSet 被刪除時,保留 PVC。whenScaled: Delete表示當 StatefulSet 縮減時,刪除多餘的 PVC。
要刪除 StatefulSet 物件及其 Pod,可以使用 kubectl delete 命令:
$ kubectl delete sts -n mysql mysql-stateful
statefulset.apps "mysql-stateful" deleted
這將首先終止 Pod,然後刪除 StatefulSet 物件。
刪除 StatefulSet 物件但保留 Pod
如果只想刪除 StatefulSet 物件而保留 Pod,可以使用 --cascade=orphan 選項:
$ kubectl delete sts -n mysql mysql-stateful --cascade=orphan
執行此命令後,Pod 將繼續存在,但不再受 StatefulSet 管理。
清理 PVC 和 PV
刪除 StatefulSet 物件後,需要手動清理 PVC 和 PV:
$ kubectl delete -n mysql pvc mysql-data-mysql-stateful-0 mysql-data-mysql-stateful-1 mysql-data-mysql-stateful-2 mysql-data-mysql-stateful-3
persistentvolumeclaim "mysql-data-mysql-stateful-0" deleted
persistentvolumeclaim "mysql-data-mysql-stateful-1" deleted
persistentvolumeclaim "mysql-data-mysql-stateful-2" deleted
persistentvolumeclaim "mysql-data-mysql-stateful-3" deleted
內容解密:
- 此命令刪除了與 StatefulSet 關聯的 PVC,從而進一步清理了相關的 PV。
發布新版本的 StatefulSet 應用程式
StatefulSet 支援兩種更新策略:RollingUpdate 和 OnDelete。
RollingUpdate 策略
RollingUpdate 是預設的更新策略,它允許以可控的方式推出新版本的應用程式。與 Deployment 的 RollingUpdate 不同,StatefulSet 的 RollingUpdate 將按照逆序(例如,從最大的索引到最小的索引)逐一終止並重新建立 Pod。
OnDelete 策略
OnDelete 策略要求手動刪除 Pod 以觸發更新。這在需要對更新進行額外驗證或手動干預時非常有用。
使用 RollingUpdate 策略更新 StatefulSet
當使用 RollingUpdate 策略時,StatefulSet 將按照逆序更新 Pod,並且會等待每個 Pod 處於 Ready 狀態後再繼續更新下一個 Pod。
spec:
updateStrategy:
type: RollingUpdate
內容解密:
.spec.updateStrategy.type定義了更新策略。RollingUpdate將按照逆序更新 Pod,並確保每個 Pod 在繼續之前處於 Ready 狀態。
請注意,在進行新版本滾動更新後,如果需要驗證狀態永續性,請不要刪除 PVC,以避免丟失儲存在 PV 中的資料。
StatefulSet – 佈署有狀態應用程式
StatefulSet 是 Kubernetes 中用於管理有狀態應用程式的重要資源物件。與 Deployment 不同,StatefulSet 能夠保證 Pod 的身份識別和順序,並且支援有序的佈署、擴充套件和更新。
RollingUpdate 策略
在 StatefulSet 中,RollingUpdate 策略允許您逐步更新 Pod 的版本,同時保持應用程式的可用性和一致性。如果更新過程中出現錯誤,StatefulSet 控制器將嘗試還原失敗的 Pod 至其當前版本。這意味著已經成功更新的 Pod 將保持當前版本,而尚未更新的 Pod 將保持在先前的版本。
然而,這種機制也可能導致更新過程被中斷。如果其中一個 Pod 副本無法變為 Running 和 Ready 狀態,StatefulSet 將停止更新並等待手動干預。簡單地將 StatefulSet 的範本更新為先前的版本是不夠的,因為 StatefulSet 將等待失敗的 Pod 變為 Ready 狀態。唯一的解決方案是手動刪除失敗的 Pod,然後讓 StatefulSet 套用先前的 Pod 範本版本。
此外,RollingUpdate 策略還提供了一個 partition 欄位,用於控制更新的粒度。透過設定 partition 的值,您可以指定哪些 Pod 應該被更新,而哪些應該保持在先前的版本。例如,如果 partition 設定為 1,則只有序號大於或等於 1 的 Pod 會被更新,而序號小於 1 的 Pod 將保持不變。
更新 StatefulSet
現在,我們將示範如何使用 RollingUpdate 策略來更新 StatefulSet 中的 MySQL 容器映像版本。
首先,複製先前的 YAML 清單檔:
$ cp mysql-statefulset.yaml mysql-statefulset-rolling-update.yaml
然後,確保 YAML 清單檔中的 updateStrategy 設定為 RollingUpdate,並且 partition 設定為 0:
# mysql-statefulset-rolling-update.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-stateful
labels:
app: mysql
namespace: mysql
spec:
serviceName: mysql-headless
podManagementPolicy: OrderedReady
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 0
replicas: 3
...
接下來,將清單檔套用到叢集中:
$ kubectl apply -f mysql-statefulset-rolling-update.yaml
statefulset.apps/mysql-stateful created
等待 Pod 變為 Running 狀態後,我們可以驗證 StatefulSet 中的 Pod:
$ kubectl get pods -n mysql
NAME READY STATUS RESTARTS AGE
k8sutils 1/1 Running 1 23h
mysql-stateful-0 1/1 Running 0 65s
mysql-stateful-1 1/1 Running 0 62s
mysql-stateful-2 1/1 Running 0 58s
建立新資料函式庫
在 StatefulSet 中建立一個新的資料函式庫:
$ kubectl exec -it -n mysql k8sutils -- /bin/bash
root@k8sutils:/# mysql -u root -p -h mysql-stateful-0.mysql-headless
Enter password: <mysqlroot>
Welcome to the MariaDB monitor. Commands end with ; or \g.
...
MySQL [(none)]> create database stsrolling;
Query OK, 1 row affected (0.027 sec)
MySQL [(none)]> exit;
更新 MySQL 容器映像版本
現在,我們將更新 MySQL 容器映像版本至 mysql:8.3.0:
# mysql-statefulset-rolling-update.yaml
...
spec:
containers:
- name: mysql
image: mysql:8.3.0
...
將變更套用到叢集中:
$ kubectl apply -f mysql-statefulset-rolling-update.yaml
statefulset.apps/mysql-stateful configured
使用 kubectl rollout status 命令觀察更新進度:
$ kubectl rollout status statefulset -n mysql
Waiting for 1 pods to be ready...
Waiting for 1 pods to be ready...
...
partitioned roll out complete: 3 new pods have been updated...
檢視更新事件
使用 kubectl describe 命令檢視 StatefulSet 的事件:
$ kubectl describe sts -n mysql mysql-stateful
Name: mysql-stateful
...
程式碼解密:
以上範例程式碼演示瞭如何使用 RollingUpdate 策略來更新 StatefulSet 中的 MySQL 容器映像版本。其中,updateStrategy 設定為 RollingUpdate,並且 partition 設定為 0,以確保所有 Pod 都會被更新。接下來,我們建立了一個新的資料函式庫,並將 MySQL 容器映像版本更新至 mysql:8.3.0。最後,我們使用 kubectl rollout status 命令觀察更新進度,並使用 kubectl describe 命令檢視 StatefulSet 的事件。
此圖示顯示了 StatefulSet 更新過程中的事件順序:
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title Kubernetes StatefulSet 擴充套件與更新策略詳解
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml
此圖示說明瞭 StatefulSet 更新過程中的事件順序,包括檢查 partition 值、更新 Pod 和等待 Pod 變為 Ready 狀態等步驟。