有狀態副本集
StatefulSet 是用於管理有狀態應用程式的工作負載 API 物件。
管理一組 Pod 的部署和擴縮,**並提供這些 Pod 的排序和唯一性保證**。
與 Deployment 類似,StatefulSet 管理基於相同容器規約的 Pod。與 Deployment 不同,StatefulSet 為每個 Pod 維護一個固定的身份。這些 Pod 從相同的規約建立,但不能互換:每個 Pod 都有一個持久識別符號,即使重新排程,它也會保持該識別符號。
如果你想使用儲存卷為工作負載提供永續性,可以將 StatefulSet 作為解決方案的一部分。儘管 StatefulSet 中的單個 Pod 可能會發生故障,但持久的 Pod 識別符號使得將現有卷與替代任何已失敗 Pod 的新 Pod 進行匹配變得更容易。
使用 StatefulSet
StatefulSet 對於需要以下一個或多個功能的應用程式很有價值。
- 穩定的、唯一的網路識別符號。
- 穩定的、持久的儲存。
- 有序、優雅的部署和擴縮。
- 有序、自動的滾動更新。
在上述內容中,穩定意味著在 Pod (重新)排程期間的永續性。如果應用程式不需要任何穩定識別符號或有序部署、刪除或擴縮,則應使用提供一組無狀態副本的工作負載物件來部署應用程式。Deployment 或 ReplicaSet 可能更適合你的無狀態需求。
限制
- 給定 Pod 的儲存必須由 PersistentVolume Provisioner (此處提供示例)根據請求的 **Storage Class** 進行提供,或由管理員預先提供。
- 刪除和/或縮減 StatefulSet 不會刪除與 StatefulSet 關聯的卷。這樣做是為了確保資料安全,這通常比自動清除所有相關的 StatefulSet 資源更有價值。
- StatefulSet 當前需要一個 Headless Service 來負責 Pod 的網路身份。你負責建立此 Service。
- 當 StatefulSet 被刪除時,StatefulSet 不提供任何 Pod 終止的保證。為了實現 StatefulSet 中 Pod 的有序且優雅終止,可以在刪除之前將 StatefulSet 縮減到 0。
- 當使用預設的 Pod 管理策略(`OrderedReady`)進行滾動更新時,可能會進入需要手動干預才能修復的損壞狀態。
元件
以下示例演示了 StatefulSet 的元件。
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
minReadySeconds: 10 # by default is 0
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.24
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
注意
為簡單起見,此示例使用 `ReadWriteOnce` 訪問模式。對於生產用途,Kubernetes 專案建議改用 `ReadWriteOncePod` 訪問模式。在上面的示例中
- 一個名為 `nginx` 的 Headless Service 用於控制網路域。
- 名為 `web` 的 StatefulSet 有一個 Spec,它指示將在唯一的 Pod 中啟動 3 個 nginx 容器副本。
- `volumeClaimTemplates` 將使用 PersistentVolume Provisioner 提供的 PersistentVolume 提供穩定的儲存。
StatefulSet 物件的名稱必須是有效的 DNS 標籤。
Pod 選擇器
你必須將 StatefulSet 的 `。spec.selector` 欄位設定為與其 `。spec.template.metadata.labels` 的標籤匹配。如果未指定匹配的 Pod 選擇器,則在建立 StatefulSet 期間將導致驗證錯誤。
卷宣告模板
您可以設定 `。spec.volumeClaimTemplates` 欄位來建立 PersistentVolumeClaim。這將為 StatefulSet 提供穩定的儲存,如果
- 卷宣告指定的 StorageClass 配置為使用 動態供應,或者
- 叢集中已經包含一個具有正確 StorageClass 和足夠可用儲存空間的 PersistentVolume。
最小就緒秒數
Kubernetes v1.25 [穩定]
`。spec.minReadySeconds` 是一個可選欄位,指定新建立的 Pod 在其任何容器未崩潰的情況下應執行並就緒的最小秒數,以便將其視為可用。這用於在使用 滾動更新 策略時檢查 rollout 的進展。此欄位預設為 0(Pod 在就緒後立即被視為可用)。要了解 Pod 何時被視為就緒,請參閱 容器探針。
Pod 身份
StatefulSet Pod 具有唯一的身份,包括序號、穩定的網路身份和穩定的儲存。無論 Pod 重新排程到哪個節點,該身份都保持不變。
序號索引
對於具有 N 個副本的 StatefulSet,StatefulSet 中的每個 Pod 都將分配一個整數序號,該序號在整個集合中是唯一的。預設情況下,Pod 將被分配從 0 到 N-1 的序號。StatefulSet 控制器還將新增一個帶有此索引的 Pod 標籤:`apps.kubernetes.io/pod-index`。
起始序號
Kubernetes v1.31 [stable]
(預設啟用:true)`。spec.ordinals` 是一個可選欄位,允許您配置分配給每個 Pod 的整數序號。它預設為 nil。在該欄位中,您可以配置以下選項
- `。spec.ordinals.start`:如果設定了 `。spec.ordinals.start` 欄位,則 Pod 將被分配從 `。spec.ordinals.start` 到 `。spec.ordinals.start + 。spec.replicas - 1` 的序號。
穩定的網路 ID
StatefulSet 中的每個 Pod 都從 StatefulSet 的名稱和 Pod 的序號派生其主機名。構造的主機名模式為 `$(statefulset name)-$(ordinal)`。上面的示例將建立三個名為 `web-0,web-1,web-2` 的 Pod。StatefulSet 可以使用 Headless Service 來控制其 Pod 的域。此 Service 管理的域的形式為:`$(service name).$(namespace).svc.cluster.local`,其中 "cluster.local" 是叢集域。每個 Pod 建立時,都會獲得一個匹配的 DNS 子域,其形式為:`$(podname).$(governing service domain)`,其中管理服務由 StatefulSet 上的 `serviceName` 欄位定義。
根據叢集中 DNS 的配置方式,你可能無法立即查詢新執行的 Pod 的 DNS 名稱。當叢集中的其他客戶端在 Pod 建立之前已經發送了針對 Pod 主機名的查詢時,可能會發生此行為。負快取(在 DNS 中正常)意味著即使在 Pod 執行後,也會記住並重用以前失敗的查詢結果,至少持續幾秒鐘。
如果你需要在 Pod 建立後立即發現它們,有以下幾種選擇
- 直接查詢 Kubernetes API(例如,使用 watch),而不是依賴 DNS 查詢。
- 減少 Kubernetes DNS 提供商的快取時間(通常這意味著編輯 CoreDNS 的配置對映,CoreDNS 當前快取 30 秒)。
正如限制部分所述,你負責建立負責 Pod 網路身份的無頭服務。
以下是一些叢集域、服務名稱、StatefulSet 名稱及其如何影響 StatefulSet Pod 的 DNS 名稱的示例。
叢集域 | 服務 (名稱空間/名稱) | StatefulSet (名稱空間/名稱) | StatefulSet 域 | Pod DNS | Pod 主機名 |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
注意
除非另行配置,否則叢集域將設定為`cluster.local`。穩定的儲存
對於 StatefulSet 中定義的每個 VolumeClaimTemplate 條目,每個 Pod 都會收到一個 PersistentVolumeClaim。在上面的 nginx 示例中,每個 Pod 都會收到一個 PersistentVolume,其 StorageClass 為 `my-storage-class`,並提供 1 GiB 的儲存空間。如果未指定 StorageClass,則將使用預設的 StorageClass。當 Pod (重新)排程到節點時,其 `volumeMounts` 將掛載與其 PersistentVolume Claims 關聯的 PersistentVolume。請注意,當 Pod 或 StatefulSet 被刪除時,與 Pod 的 PersistentVolume Claims 關聯的 PersistentVolume 不會被刪除。這必須手動完成。
Pod 名稱標籤
當 StatefulSet 控制器建立 Pod 時,它會新增一個標籤 `statefulset.kubernetes.io/pod-name`,該標籤設定為 Pod 的名稱。此標籤允許您將 Service 附加到 StatefulSet 中的特定 Pod。
Pod 索引標籤
Kubernetes v1.32 [stable]
(預設啟用:true)當 StatefulSet 控制器建立 Pod 時,新 Pod 會被標記為 `apps.kubernetes.io/pod-index`。此標籤的值是 Pod 的序數索引。此標籤允許您將流量路由到特定的 Pod 索引,使用 Pod 索引標籤篩選日誌/指標等。請注意,對於此功能,功能門 `PodIndexLabel` 預設啟用並鎖定,為了停用它,使用者必須使用伺服器模擬版本 v1.31。
部署和擴縮保證
- 對於具有 N 個副本的 StatefulSet,當 Pod 部署時,它們按 {0..N-1} 的順序依次建立。
- 當 Pod 被刪除時,它們按 {N-1..0} 的逆序終止。
- 在對 Pod 應用擴縮操作之前,其所有前驅必須處於執行和就緒狀態。
- 在 Pod 終止之前,其所有後繼必須完全關閉。
StatefulSet 不應將 `pod.Spec.TerminationGracePeriodSeconds` 設定為 0。這種做法不安全,強烈不建議。有關更多解釋,請參閱 強制刪除 StatefulSet Pod。
當建立上述 nginx 示例時,將按 web-0、web-1、web-2 的順序部署三個 Pod。web-1 在 web-0 執行並就緒 之前不會部署,web-2 在 web-1 執行並就緒之前不會部署。如果 web-0 在 web-1 執行並就緒之後但在 web-2 啟動之前失敗,則 web-2 不會啟動,直到 web-0 成功重新啟動並變為執行並就緒。
如果使用者透過修補 StatefulSet 將 `replicas=1` 來擴縮已部署的示例,則 web-2 將首先終止。web-1 在 web-2 完全關閉並刪除之前不會終止。如果 web-0 在 web-2 已終止並完全關閉之後但在 web-1 終止之前失敗,則 web-1 不會在 web-0 執行並就緒之前終止。
Pod 管理策略
StatefulSet 允許您透過其 `。spec.podManagementPolicy` 欄位放寬其排序保證,同時保留其唯一性和身份保證。
有序就緒 Pod 管理
`OrderedReady` Pod 管理是 StatefulSet 的預設設定。它實現了上述行為。
並行 Pod 管理
`Parallel` Pod 管理指示 StatefulSet 控制器並行啟動或終止所有 Pod,並且在啟動或終止另一個 Pod 之前,不等待 Pod 變為 Running 和 Ready 或完全終止。此選項僅影響擴縮操作的行為。更新不受影響。
更新策略
StatefulSet 的 `。spec.updateStrategy` 欄位允許您配置和停用 StatefulSet 中 Pod 的容器、標籤、資源請求/限制和註解的自動滾動更新。有兩個可能的值
OnDelete
- 當 StatefulSet 的 `。spec.updateStrategy.type` 設定為 `OnDelete` 時,StatefulSet 控制器不會自動更新 StatefulSet 中的 Pod。使用者必須手動刪除 Pod,才能使控制器建立反映 StatefulSet 的 `。spec.template` 所做修改的新 Pod。
RollingUpdate
- `RollingUpdate` 更新策略實現 StatefulSet 中 Pod 的自動化滾動更新。這是預設的更新策略。
滾動更新
當 StatefulSet 的 `。spec.updateStrategy.type` 設定為 `RollingUpdate` 時,StatefulSet 控制器將刪除並重新建立 StatefulSet 中的每個 Pod。它將按照 Pod 終止的相同順序(從最大序數到最小序數)進行,每次更新一個 Pod。
Kubernetes 控制平面在更新其前身之前,會等待已更新的 Pod 變為 Running 和 Ready。如果你已設定 `。spec.minReadySeconds`(參見最小就緒秒數),控制平面會在 Pod 變為就緒後額外等待該時間量,然後才會繼續進行。
分割槽滾動更新
透過指定 `。spec.updateStrategy.rollingUpdate.partition`,可以對 `RollingUpdate` 更新策略進行分割槽。如果指定了分割槽,則當 StatefulSet 的 `。spec.template` 更新時,所有序數大於或等於分割槽的 Pod 都將更新。所有序數小於分割槽的 Pod 將不會更新,並且即使它們被刪除,也將以以前的版本重新建立。如果 StatefulSet 的 `。spec.updateStrategy.rollingUpdate.partition` 大於其 `。spec.replicas`,則對其 `。spec.template` 的更新將不會傳播到其 Pod。在大多數情況下,你不需要使用分割槽,但如果你想分階段更新、推出金絲雀版本或執行分階段推出,它們會很有用。
最大不可用 Pod 數
你可以透過指定 `。spec.updateStrategy.rollingUpdate.maxUnavailable` 欄位來控制更新期間最大不可用的 Pod 數量。該值可以是絕對數字(例如,`5`)或所需 Pod 數量的百分比(例如,`10%`)。絕對數字由百分比值向上取整計算。此欄位不能為 0。預設設定為 1。
此欄位適用於 `0` 到 `replicas - 1` 範圍內的所有 Pod。如果 `0` 到 `replicas - 1` 範圍內有任何不可用的 Pod,它將被計入 `maxUnavailable`。
注意
`maxUnavailable` 欄位處於 Alpha 階段,只有在啟用 `MaxUnavailableStatefulSet` 功能門 的 API 伺服器上執行的 API 伺服器才會遵守它。強制回滾
當使用預設的 Pod 管理策略(`OrderedReady`)進行滾動更新時,可能會進入需要手動干預才能修復的損壞狀態。
如果你將 Pod 模板更新到永遠不會變為 Running 和 Ready 的配置(例如,由於糟糕的二進位制檔案或應用程式級配置錯誤),StatefulSet 將停止推出並等待。
在這種狀態下,僅僅將 Pod 模板恢復到良好配置是不夠的。由於存在一個已知問題,StatefulSet 將繼續等待損壞的 Pod 變為 Ready(這永遠不會發生),然後才會嘗試將其恢復到工作配置。
恢復模板後,您還必須刪除 StatefulSet 已嘗試使用錯誤配置執行的任何 Pod。然後,StatefulSet 將開始使用恢復的模板重新建立 Pod。
PersistentVolumeClaim 保留
Kubernetes v1.32 [stable]
(預設啟用:true)可選的 `。spec.persistentVolumeClaimRetentionPolicy` 欄位控制在 StatefulSet 的生命週期中是否以及如何刪除 PVC。您必須在 API 伺服器和控制器管理器上啟用 `StatefulSetAutoDeletePVC` 功能門 才能使用此欄位。啟用後,您可以為每個 StatefulSet 配置兩種策略
whenDeleted
- 配置當 StatefulSet 被刪除時應用的卷保留行為
whenScaled
- 配置當 StatefulSet 的副本計數減少時應用的卷保留行為;例如,當縮減集合時。
對於您可以配置的每個策略,您可以將值設定為 `Delete` 或 `Retain`。
刪除
- 從 StatefulSet `volumeClaimTemplate` 建立的 PVC 會因受策略影響的每個 Pod 而被刪除。使用 `whenDeleted` 策略,所有來自 `volumeClaimTemplate` 的 PVC 在其 Pod 被刪除後都會被刪除。使用 `whenScaled` 策略,只有與正在縮減的 Pod 副本對應的 PVC 在其 Pod 被刪除後才會被刪除。
- `Retain`(預設)
- 從 `volumeClaimTemplate` 建立的 PVC 在其 Pod 被刪除時不受影響。這是此新功能之前的行為。
請記住,這些策略**僅**適用於因 StatefulSet 被刪除或縮減而移除 Pod 時。例如,如果與 StatefulSet 關聯的 Pod 因節點故障而失敗,並且控制平面建立了一個替換 Pod,則 StatefulSet 會保留現有的 PVC。現有卷不受影響,叢集會將其附加到新 Pod 即將啟動的節點上。
策略的預設值為 `Retain`,與此新功能之前的 StatefulSet 行為一致。
這是一個示例策略。
apiVersion: apps/v1
kind: StatefulSet
...
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Delete
...
StatefulSet 控制器為其 PVC 新增所有者引用,然後這些 PVC 在 Pod 終止後由垃圾收集器刪除。這使得 Pod 可以在 PVC 刪除(以及根據保留策略,在底層 PV 和卷刪除之前)之前乾淨地解除安裝所有卷。當您將 `whenDeleted` 策略設定為 `Delete` 時,與該 StatefulSet 關聯的所有 PVC 都會放置對 StatefulSet 例項的所有者引用。
`whenScaled` 策略必須僅在 Pod 縮減時刪除 PVC,而不是在 Pod 因其他原因刪除時刪除。在協調時,StatefulSet 控制器會將其期望的副本計數與叢集中實際存在的 Pod 進行比較。任何 ID 大於副本計數的 StatefulSet Pod 都將被譴責並標記為刪除。如果 `whenScaled` 策略是 `Delete`,則被譴責的 Pod 在刪除之前,首先會被設定為關聯的 StatefulSet 模板 PVC 的所有者。這會導致 PVC 在僅被譴責的 Pod 終止後被垃圾回收。
這意味著如果控制器崩潰並重啟,在所有者引用根據策略得到適當更新之前,任何 Pod 都不會被刪除。如果一個被譴責的 Pod 在控制器停機期間被強制刪除,所有者引用可能已經設定,也可能尚未設定,具體取決於控制器何時崩潰。更新所有者引用可能需要幾個協調迴圈,因此一些被譴責的 Pod 可能已經設定了所有者引用,而另一些則可能沒有。因此,我們建議等待控制器恢復,這將驗證所有者引用,然後再終止 Pod。如果不可能,操作員應驗證 PVC 上的所有者引用,以確保在強制刪除 Pod 時刪除預期物件。
副本
`。spec.replicas` 是一個可選欄位,指定所需的 Pod 數量。它預設為 1。
如果您手動擴縮部署,例如透過 `kubectl scale statefulset statefulset --replicas=X`,然後根據清單更新該 StatefulSet(例如:透過執行 `kubectl apply -f statefulset.yaml`),則應用該清單將覆蓋您之前手動執行的擴縮操作。
如果 HorizontalPodAutoscaler(或任何類似的水平擴縮 API)正在管理 StatefulSet 的擴縮,請不要設定 `。spec.replicas`。相反,讓 Kubernetes 控制平面 自動管理 `。spec.replicas` 欄位。
下一步
- 瞭解Pod。
- 瞭解如何使用 StatefulSet
- 遵循部署有狀態應用程式的示例。
- 遵循使用 StatefulSet 部署 Cassandra 的示例。
- 遵循運行復制有狀態應用程式的示例。
- 瞭解如何擴縮 StatefulSet。
- 瞭解刪除 StatefulSet 時涉及哪些內容。
- 瞭解如何配置 Pod 以使用捲進行儲存。
- 瞭解如何配置 Pod 以使用 PersistentVolume 進行儲存。
- `StatefulSet` 是 Kubernetes REST API 中的一個頂級資源。閱讀 StatefulSet 物件定義以瞭解有狀態集合的 API。
- 瞭解 PodDisruptionBudget 以及如何使用它來管理中斷期間的應用程式可用性。