Kubernetes StatefulSets

2022-05-05 14:13 更新

StatefulSets

StatefulSet 是用來管理有狀態(tài)應用的工作負載 API 對象。

StatefulSet 用來管理某 Pod 集合的部署和擴縮, 并為這些 Pod 提供持久存儲和持久標識符。

和 Deployment 類似, StatefulSet 管理基于相同容器規(guī)約的一組 Pod。但和 Deployment 不同的是, StatefulSet 為它們的每個 Pod 維護了一個有粘性的 ID。這些 Pod 是基于相同的規(guī)約來創(chuàng)建的, 但是不能相互替換:無論怎么調(diào)度,每個 Pod 都有一個永久不變的 ID。

如果希望使用存儲卷為工作負載提供持久存儲,可以使用 StatefulSet 作為解決方案的一部分。 盡管 StatefulSet 中的單個 Pod 仍可能出現(xiàn)故障, 但持久的 Pod 標識符使得將現(xiàn)有卷與替換已失敗 Pod 的新 Pod 相匹配變得更加容易。

使用 StatefulSets

StatefulSets 對于需要滿足以下一個或多個需求的應用程序很有價值:

  • 穩(wěn)定的、唯一的網(wǎng)絡標識符。
  • 穩(wěn)定的、持久的存儲。
  • 有序的、優(yōu)雅的部署和縮放。
  • 有序的、自動的滾動更新。

在上面描述中,“穩(wěn)定的”意味著 Pod 調(diào)度或重調(diào)度的整個過程是有持久性的。 如果應用程序不需要任何穩(wěn)定的標識符或有序的部署、刪除或伸縮,則應該使用 由一組無狀態(tài)的副本控制器提供的工作負載來部署應用程序,比如 Deployment 或者 ReplicaSet 可能更適用于你的無狀態(tài)應用部署需要。

限制 

  • 給定 Pod 的存儲必須由 PersistentVolume 驅動 基于所請求的 ?storage class? 來提供,或者由管理員預先提供。
  • 刪除或者收縮 StatefulSet 并不會刪除它關聯(lián)的存儲卷。 這樣做是為了保證數(shù)據(jù)安全,它通常比自動清除 StatefulSet 所有相關的資源更有價值。
  • StatefulSet 當前需要無頭服務 來負責 Pod 的網(wǎng)絡標識。你需要負責創(chuàng)建此服務。
  • 當刪除 StatefulSets 時,StatefulSet 不提供任何終止 Pod 的保證。 為了實現(xiàn) StatefulSet 中的 Pod 可以有序地且體面地終止,可以在刪除之前將 StatefulSet 縮放為 0。
  • 在默認 Pod 管理策略(?OrderedReady?) 時使用 滾動更新,可能進入需要人工干預 才能修復的損壞狀態(tài)。

組件

下面的示例演示了 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
  template:
    metadata:
      labels:
        app: nginx # has to match .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        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

上述例子中:

  • 名為 ?nginx ?的 Headless Service 用來控制網(wǎng)絡域名。
  • 名為 ?web ?的 StatefulSet 有一個 Spec,它表明將在獨立的 3 個 Pod 副本中啟動 nginx 容器。
  • ?volumeClaimTemplates ?將通過 PersistentVolumes 驅動提供的 PersistentVolumes 來提供穩(wěn)定的存儲。

StatefulSet 的命名需要遵循DNS 子域名規(guī)范。

Pod選擇算符

你必須設置 StatefulSet 的 ?.spec.selector? 字段,使之匹配其在 ?.spec.template.metadata.labels? 中設置的標簽。在 Kubernetes 1.8 版本之前, 被忽略 ?.spec.selector? 字段會獲得默認設置值。 在 1.8 和以后的版本中,未指定匹配的 Pod 選擇器將在創(chuàng)建 StatefulSet 期間導致驗證錯誤。

Pod 標識 

StatefulSet Pod 具有唯一的標識,該標識包括順序標識、穩(wěn)定的網(wǎng)絡標識和穩(wěn)定的存儲。 該標識和 Pod 是綁定的,不管它被調(diào)度在哪個節(jié)點上。

有序索引 

對于具有 N 個副本的 StatefulSet,StatefulSet 中的每個 Pod 將被分配一個整數(shù)序號, 從 0 到 N-1,該序號在 StatefulSet 上是唯一的。

穩(wěn)定的網(wǎng)絡 ID 

StatefulSet 中的每個 Pod 根據(jù) StatefulSet 的名稱和 Pod 的序號派生出它的主機名。 組合主機名的格式為?$(StatefulSet 名稱)-$(序號)?。 上例將會創(chuàng)建三個名稱分別為 ?web-0、web-1、web-2? 的 Pod。 StatefulSet 可以使用 無頭服務 控制它的 Pod 的網(wǎng)絡域。管理域的這個服務的格式為: ?$(服務名稱).$(命名空間).svc.cluster.local?,其中 ?cluster.local? 是集群域。 一旦每個 Pod 創(chuàng)建成功,就會得到一個匹配的 DNS 子域,格式為: ?$(pod 名稱).$(所屬服務的 DNS 域名)?,其中所屬服務由 StatefulSet 的 ?serviceName ?域來設定。

取決于集群域內(nèi)部 DNS 的配置,有可能無法查詢一個剛剛啟動的 Pod 的 DNS 命名。 當集群內(nèi)其他客戶端在 Pod 創(chuàng)建完成前發(fā)出 Pod 主機名查詢時,就會發(fā)生這種情況。 負緩存 (在 DNS 中較為常見) 意味著之前失敗的查詢結果會被記錄和重用至少若干秒鐘, 即使 Pod 已經(jīng)正常運行了也是如此。

如果需要在 Pod 被創(chuàng)建之后及時發(fā)現(xiàn)它們,有以下選項:

  • 直接查詢 Kubernetes API(比如,利用 watch 機制)而不是依賴于 DNS 查詢
  • 縮短 Kubernetes DNS 驅動的緩存時長(通常這意味著修改 CoreDNS 的 ConfigMap,目前緩存時長為 30 秒)

正如限制中所述,你需要負責創(chuàng)建無頭服務 以便為 Pod 提供網(wǎng)絡標識。

下面給出一些選擇集群域、服務名、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?,除非有其他配置。

穩(wěn)定的存儲

對于 StatefulSet 中定義的每個 VolumeClaimTemplate,每個 Pod 接收到一個 PersistentVolumeClaim。在上面的 nginx 示例中,每個 Pod 將會得到基于 StorageClass ?my-storage-class? 提供的 1 Gib 的 PersistentVolume。 如果沒有聲明 StorageClass,就會使用默認的 StorageClass。 當一個 Pod 被調(diào)度(重新調(diào)度)到節(jié)點上時,它的 ?volumeMounts ?會掛載與其 PersistentVolumeClaims 相關聯(lián)的 PersistentVolume。 請注意,當 Pod 或者 StatefulSet 被刪除時,與 PersistentVolumeClaims 相關聯(lián)的 PersistentVolume 并不會被刪除。要刪除它必須通過手動方式來完成。

Pod 名稱標簽 

當 StatefulSet 控制器(Controller) 創(chuàng)建 Pod 時, 它會添加一個標簽 ?statefulset.kubernetes.io/pod-name?,該標簽值設置為 Pod 名稱。 這個標簽允許你給 StatefulSet 中的特定 Pod 綁定一個 Service。

部署和擴縮保證 

  • 對于包含 N 個 副本的 StatefulSet,當部署 Pod 時,它們是依次創(chuàng)建的,順序為 ?0..N-1?。
  • 當刪除 Pod 時,它們是逆序終止的,順序為 ?N-1..0?。
  • 在將縮放操作應用到 Pod 之前,它前面的所有 Pod 必須是 Running 和 Ready 狀態(tài)。
  • 在 Pod 終止之前,所有的繼任者必須完全關閉。

StatefulSet 不應將 ?pod.Spec.TerminationGracePeriodSeconds? 設置為 0。 這種做法是不安全的,要強烈阻止。

在上面的 nginx 示例被創(chuàng)建后,會按照 web-0、web-1、web-2 的順序部署三個 Pod。 在 web-0 進入 Running 和 Ready 狀態(tài)前不會部署 web-1。在 web-1 進入 Running 和 Ready 狀態(tài)前不會部署 web-2。 如果 web-1 已經(jīng)處于 Running 和 Ready 狀態(tài),而 web-2 尚未部署,在此期間發(fā)生了 web-0 運行失敗,那么 web-2 將不會被部署,要等到 web-0 部署完成并進入 Running 和 Ready 狀態(tài)后,才會部署 web-2。

如果用戶想將示例中的 StatefulSet 收縮為 ?replicas=1?,首先被終止的是 web-2。 在 web-2 沒有被完全停止和刪除前,web-1 不會被終止。 當 web-2 已被終止和刪除、web-1 尚未被終止,如果在此期間發(fā)生 web-0 運行失敗, 那么就不會終止 web-1,必須等到 web-0 進入 Running 和 Ready 狀態(tài)后才會終止 web-1。

Pod 管理策略

在 Kubernetes 1.7 及以后的版本中,StatefulSet 允許你放寬其排序保證, 同時通過它的 ?.spec.podManagementPolicy? 域保持其唯一性和身份保證。

OrderedReady Pod 管理

?OrderedReady ?Pod 管理是 StatefulSet 的默認設置。它實現(xiàn)了 上面描述的功能。

并行 Pod 管理 

?Parallel ?Pod 管理讓 StatefulSet 控制器并行的啟動或終止所有的 Pod, 啟動或者終止其他 Pod 前,無需等待 Pod 進入 Running 和 ready 或者完全停止狀態(tài)。 這個選項只會影響伸縮操作的行為,更新則不會被影響。

更新策略 

StatefulSet 的 ?.spec.updateStrategy? 字段讓 你可以配置和禁用掉自動滾動更新 Pod 的容器、標簽、資源請求或限制、以及注解。 有兩個允許的值:

  • OnDelete
    • 當 StatefulSet 的 ?.spec.updateStrategy.type? 設置為 ?OnDelete ?時, 它的控制器將不會自動更新 StatefulSet 中的 Pod。 用戶必須手動刪除 Pod 以便讓控制器創(chuàng)建新的 Pod,以此來對 StatefulSet 的 ?.spec.template? 的變動作出反應。
  • RollingUpdate
    • ?RollingUpdate ?更新策略對 StatefulSet 中的 Pod 執(zhí)行自動的滾動更新。這是默認的更新策略。

滾動更新

當 StatefulSet 的 ?.spec.updateStrategy.type? 被設置為 ?RollingUpdate ?時, StatefulSet 控制器會刪除和重建 StatefulSet 中的每個 Pod。 它將按照與 Pod 終止相同的順序(從最大序號到最小序號)進行,每次更新一個 Pod。

Kubernetes 控制面會等到被更新的 Pod 進入 Running 和 Ready 狀態(tài),然后再更新其前身。 如果你設置了 ?.spec.minReadySeconds?(查看最短就緒秒數(shù)),控制面在 Pod 就緒后會額外等待一定的時間再執(zhí)行下一步。

分區(qū)滾動更新 

通過聲明 ?.spec.updateStrategy.rollingUpdate.partition? 的方式,?RollingUpdate ?更新策略可以實現(xiàn)分區(qū)。 如果聲明了一個分區(qū),當 StatefulSet 的 ?.spec.template? 被更新時, 所有序號大于等于該分區(qū)序號的 Pod 都會被更新。 所有序號小于該分區(qū)序號的 Pod 都不會被更新,并且,即使他們被刪除也會依據(jù)之前的版本進行重建。 如果 StatefulSet 的 ?.spec.updateStrategy.rollingUpdate.partition? 大于它的 ?.spec.replicas?,對它的 ?.spec.template? 的更新將不會傳遞到它的 Pod。 在大多數(shù)情況下,你不需要使用分區(qū),但如果你希望進行階段更新、執(zhí)行金絲雀或執(zhí)行 分階段上線,則這些分區(qū)會非常有用。

強制回滾

在默認 Pod 管理策略(?OrderedReady?) 下使用 滾動更新 ,可能進入需要人工干預才能修復的損壞狀態(tài)。

如果更新后 Pod 模板配置進入無法運行或就緒的狀態(tài)(例如,由于錯誤的二進制文件 或應用程序級配置錯誤),StatefulSet 將停止回滾并等待。

在這種狀態(tài)下,僅將 Pod 模板還原為正確的配置是不夠的。由于 已知問題,StatefulSet 將繼續(xù)等待損壞狀態(tài)的 Pod 準備就緒(永遠不會發(fā)生),然后再嘗試將其恢復為正常工作配置。

恢復模板后,還必須刪除 StatefulSet 嘗試使用錯誤的配置來運行的 Pod。這樣, StatefulSet 才會開始使用被還原的模板來重新創(chuàng)建 Pod。

最短就緒秒數(shù) 

FEATURE STATE: Kubernetes v1.22 [alpha]

?.spec.minReadySeconds? 是一個可選字段,用于指定新創(chuàng)建的 Pod 就緒(沒有任何容器崩潰)后被認為可用的最小秒數(shù)。 默認值是 0(Pod 就緒時就被認為可用)。

請注意只有當你啟用 ?StatefulSetMinReadySeconds ?特性門控時,該字段才會生效。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號