StatefulSets
StatefulSet 是用於管理有狀態應用程式的工作負載 API 物件。
管理一組 Pod 的部署和擴展,並提供關於這些 Pod 排序和唯一性的保證。
如同 Deployment,StatefulSet 管理基於相同容器規格的 Pod。與 Deployment 不同的是,StatefulSet 為其每個 Pod 維護黏性身分。這些 Pod 從相同的規格建立,但不可互換:每個 Pod 都有一個持久性識別符,在任何重新排程中都會維護該識別符。
如果您想要使用儲存磁碟區為您的工作負載提供持久性,則可以使用 StatefulSet 作為解決方案的一部分。儘管 StatefulSet 中的個別 Pod 容易發生故障,但持久性 Pod 識別符使得將現有磁碟區與替換任何已故障 Pod 的新 Pod 相匹配變得更容易。
使用 StatefulSets
StatefulSets 對於需要以下一或多項的應用程式很有價值。
- 穩定、唯一的網路識別符。
- 穩定、持久性儲存。
- 有序、優雅的部署和擴展。
- 有序、自動滾動更新。
在以上內容中,穩定與 Pod(重新)排程中的持久性同義。如果應用程式不需要任何穩定識別符或有序部署、刪除或擴展,則您應該使用提供一組無狀態複本的工作負載物件來部署您的應用程式。Deployment 或 ReplicaSet 可能更適合您的無狀態需求。
限制
- 給定 Pod 的儲存必須由 PersistentVolume Provisioner(此處範例)根據請求的儲存類別配置,或由管理員預先配置。
- 刪除和/或縮減 StatefulSet 將不會刪除與 StatefulSet 關聯的磁碟區。這樣做是為了確保資料安全,這通常比自動清除所有相關的 StatefulSet 資源更有價值。
- StatefulSets 目前需要 Headless Service 負責 Pod 的網路身分。您有責任建立此服務。
- 當刪除 StatefulSet 時,StatefulSets 不會對 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 配置的 PersistentVolumes 提供穩定儲存。
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 應該在沒有任何容器崩潰的情況下執行並就緒的最小秒數,才能被視為可用。這用於在使用 滾動更新 策略時檢查滾動進度。此欄位預設為 0 (Pod 一旦就緒就會被視為可用)。若要深入瞭解 Pod 何時被視為就緒,請參閱 容器探測。
Pod 身分
StatefulSet Pod 具有唯一的身份,包括序號、穩定網路身分和穩定儲存。身分會黏附到 Pod,無論它(重新)排程到哪個節點。
序號索引
對於具有 N 個 複本 的 StatefulSet,StatefulSet 中的每個 Pod 都會被指派一個整數序號,該序號在 Set 中是唯一的。依預設,Pod 將被指派從 0 到 N-1 的序號。StatefulSet 控制器也會新增具有此索引的 Pod 標籤:apps.kubernetes.io/pod-index
。
起始序號
Kubernetes v1.31 [穩定]
(預設啟用: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)
,其中 governing service 是由 StatefulSet 上的 serviceName
欄位所定義。
根據叢集中 DNS 的配置方式,您可能無法立即查詢到新運行的 Pod 的 DNS 名稱。當叢集中的其他用戶端在 Pod 建立之前就已發送對 Pod 主機名稱的查詢時,可能會發生這種情況。負面快取(在 DNS 中很常見)表示即使在 Pod 運行後,先前失敗查詢的結果仍會被記住並重複使用,至少幾秒鐘。
如果您需要在 Pod 建立後立即探索到它們,您有幾個選項
- 直接查詢 Kubernetes API(例如,使用監看 (watch))而不是依賴 DNS 查詢。
- 縮短 Kubernetes DNS 提供者中的快取時間(通常這表示編輯 CoreDNS 的 ConfigMap,目前預設快取時間為 30 秒)。
如限制章節所述,您有責任建立負責 Pod 網路身分識別的 Headless Service。
以下是一些叢集網域、Service 名稱、StatefulSet 名稱以及其如何影響 StatefulSet 的 Pod 的 DNS 名稱的範例。
叢集網域 | 服務 (ns/名稱) | StatefulSet (ns/名稱) | 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} |
穩定儲存
對於 StatefulSet 中定義的每個 VolumeClaimTemplate 項目,每個 Pod 都會收到一個 PersistentVolumeClaim。在上述 nginx 範例中,每個 Pod 都會收到一個 PersistentVolume,其 StorageClass 為 my-storage-class
,且已佈建 1 GiB 的儲存空間。如果未指定 StorageClass,則將使用預設的 StorageClass。當 Pod 被(重新)排程到節點上時,其 volumeMounts
會掛載與其 PersistentVolumeClaim 相關聯的 PersistentVolume。請注意,當 Pod 或 StatefulSet 被刪除時,與 Pod 的 PersistentVolumeClaim 相關聯的 PersistentVolume 不會被刪除。這必須手動完成。
Pod 名稱標籤
當 StatefulSet 控制器 建立 Pod 時,它會新增一個標籤 statefulset.kubernetes.io/pod-name
,該標籤設定為 Pod 的名稱。此標籤允許您將 Service 附加到 StatefulSet 中的特定 Pod。
Pod 索引標籤
Kubernetes v1.32 [穩定]
(預設啟用: 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 必須處於「執行中且就緒」狀態。
- 在 Pod 終止之前,其所有後續 Pod 必須完全關閉。
StatefulSet 不應指定 pod.Spec.TerminationGracePeriodSeconds
為 0。這種做法是不安全的,強烈建議不要使用。如需進一步說明,請參閱強制刪除 StatefulSet Pod。
當建立上述 nginx 範例時,將依序部署三個 Pod:web-0、web-1、web-2。web-0 執行中且就緒後,才會部署 web-1,而 web-1 執行中且就緒後,才會部署 web-2。如果 web-0 在 web-1 執行中且就緒後,但在 web-2 啟動之前失敗,則在 web-0 成功重新啟動並變為執行中且就緒之前,web-2 將不會啟動。
如果使用者要透過修補 StatefulSet 來擴充已部署的範例,例如將 replicas=1
,則 web-2 將首先被終止。在 web-2 完全關閉並刪除之前,web-1 不會被終止。如果 web-0 在 web-2 已終止且完全關閉之後,但在 web-1 終止之前失敗,則在 web-0 執行中且就緒之前,web-1 將不會被終止。
Pod 管理策略
StatefulSet 允許您透過其 .spec.podManagementPolicy
欄位放寬其排序保證,同時保留其唯一性和身分識別保證。
OrderedReady Pod 管理
OrderedReady
Pod 管理是 StatefulSet 的預設值。它實作了上述行為。
Parallel Pod 管理
Parallel
Pod 管理告訴 StatefulSet 控制器並行啟動或終止所有 Pod,並且在啟動或終止另一個 Pod 之前,不等待 Pod 變成「執行中且就緒」或完全終止。此選項僅影響擴充操作的行為。更新不受影響。
更新策略
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 處於「執行中且就緒」狀態,然後再更新其前導 Pod。如果您已設定 .spec.minReadySeconds
(請參閱最短就緒秒數),則控制平面還會在 Pod 變成就緒狀態後,額外等待該時間量,然後再繼續進行。
分割滾動更新
RollingUpdate
更新策略可以透過指定 .spec.updateStrategy.rollingUpdate.partition
來分割。如果指定了分割區,則當 StatefulSet 的 .spec.template
更新時,序號大於或等於分割區的所有 Pod 都將被更新。序號小於分割區的所有 Pod 將不會被更新,即使它們被刪除,它們也將以先前的版本重新建立。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition
大於其 .spec.replicas
,則對其 .spec.template
的更新將不會傳播到其 Pod。在大多數情況下,您不需要使用分割區,但是如果您想要暫存更新、推出 Canary 版本或執行階段性推出,它們會很有用。
最大不可用 Pod 數
Kubernetes v1.24 [Alpha]
您可以透過指定 .spec.updateStrategy.rollingUpdate.maxUnavailable
欄位來控制更新期間可能處於不可用狀態的最大 Pod 數。該值可以是絕對數字(例如,5
)或所需 Pod 的百分比(例如,10%
)。絕對數字是從百分比值向上捨入計算得出的。此欄位不能為 0。預設設定為 1。
此欄位適用於範圍 0
到 replicas - 1
中的所有 Pod。如果範圍 0
到 replicas - 1
中有任何不可用的 Pod,它將計入 maxUnavailable
。
強制回滾
當使用預設 Pod 管理策略 (OrderedReady
) 的 滾動更新 時,可能會進入需要手動介入才能修復的損壞狀態。
如果您將 Pod 範本更新為永遠不會變成「執行中且就緒」狀態的配置(例如,由於錯誤的二進位檔或應用程式層級配置錯誤),StatefulSet 將停止滾動並等待。
在這種狀態下,僅將 Pod 範本還原為良好配置是不夠的。由於 已知問題,StatefulSet 將繼續等待損壞的 Pod 變成「就緒」狀態(這永遠不會發生),然後才會嘗試將其還原為可運作的配置。
還原範本後,您還必須刪除 StatefulSet 已嘗試使用錯誤配置運行的任何 Pod。然後,StatefulSet 將開始使用還原的範本重新建立 Pod。
PersistentVolumeClaim 保留
Kubernetes v1.32 [穩定]
(預設啟用: true)選用的 .spec.persistentVolumeClaimRetentionPolicy
欄位控制在 StatefulSet 的生命週期中是否以及如何刪除 PVC。您必須在 API 伺服器和控制器管理員上啟用 StatefulSetAutoDeletePVC
功能閘道 才能使用此欄位。啟用後,您可以為每個 StatefulSet 配置兩種策略
whenDeleted
- 配置在刪除 StatefulSet 時應用的磁碟區保留行為
whenScaled
- 配置在減少 StatefulSet 的副本計數時應用的磁碟區保留行為;例如,縮減集合時。
對於您可以配置的每個策略,您可以將值設定為 Delete
或 Retain
。
Delete
- 從 StatefulSet
volumeClaimTemplate
建立的 PVC 會針對受策略影響的每個 Pod 刪除。使用whenDeleted
策略,來自volumeClaimTemplate
的所有 PVC 在其 Pod 刪除後都會被刪除。使用whenScaled
策略,僅刪除與縮減的 Pod 副本相對應的 PVC,在其 Pod 刪除後。 Retain
(預設)- 當 Pod 被刪除時,來自
volumeClaimTemplate
的 PVC 不受影響。這是此新功能之前的行為。
請記住,這些策略僅適用於由於 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 執行個體的擁有者參考會放置在與該 StatefulSet 相關聯的所有 PVC 上。
whenScaled
策略必須僅在 Pod 縮減時刪除 PVC,而不是在 Pod 因其他原因刪除時刪除 PVC。在協調時,StatefulSet 控制器會將其所需的副本計數與叢集上存在的實際 Pod 進行比較。任何 ID 大於副本計數的 StatefulSet Pod 都會被譴責並標記為刪除。如果 whenScaled
策略為 Delete
,則在刪除 Pod 之前,被譴責的 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 以及如何使用它來管理中斷期間的應用程式可用性。