本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

Kubernetes 1.27:StatefulSet 起始序號簡化遷移

Kubernetes v1.26 為 StatefulSet 引入了一項新的 Alpha 級別功能,用於控制 Pod 副本的序號。自 Kubernetes v1.27 起,此功能已進入 Beta 階段。序號可以從任意非負數開始。本部落格文章將討論如何使用此功能。

背景

StatefulSet 的序號為 Pod 副本提供順序標識。使用 OrderedReady Pod 管理時,Pod 會從序號索引 0 建立到 N-1

在當前的 Kubernetes 中,跨叢集協調 StatefulSet 遷移具有挑戰性。雖然存在備份和恢復解決方案,但這些方案要求在遷移前將應用的副本數縮減到零。在當今完全互聯的世界中,即使是計劃內的應用停機也可能無法滿足您的業務目標。您可以使用級聯刪除On Delete策略來遷移單個 Pod,但這容易出錯且管理繁瑣。當您的 Pod 失敗或被驅逐時,您將失去 StatefulSet 控制器的自愈優勢。

Kubernetes v1.26 允許一個 StatefulSet 負責 {0..N-1} 範圍內的一個序號區間(即序號 0, 1, ... 直到 N-1)。有了它,您可以在源叢集中縮減 {0..k-1} 範圍的副本,並在目標叢集中擴容互補的 {k..N-1} 範圍的副本,同時保持應用可用性。這使您在協調跨叢集遷移時,能夠保持*最多一個*的語義(即在 StatefulSet 中最多隻有一個具有給定標識的 Pod 在執行)和滾動更新行為。

我為什麼要使用這個功能?

假設您在一個叢集中執行 StatefulSet,需要將其遷移到另一個叢集。您可能需要這樣做的原因有很多:

  • 可擴充套件性:您的 StatefulSet 規模變得太大,超出了叢集的承載能力,並開始影響叢集中其他工作負載的服務質量。
  • 隔離性:您正在一個多使用者訪問的叢集中執行 StatefulSet,而名稱空間隔離不足以滿足需求。
  • 叢集配置:您想將 StatefulSet 遷移到另一個叢集,以使用當前叢集不具備的某些環境。
  • 控制平面升級:您想將 StatefulSet 遷移到一個執行著升級版控制平面的叢集,並且無法承擔原地升級控制平面的風險或停機時間。

我該如何使用它?

在叢集上啟用 StatefulSetStartOrdinal 特性門控,並建立一個帶有自定義 .spec.ordinals.start 的 StatefulSet。

立即試用

在此演示中,我將使用新機制將 StatefulSet 從一個 Kubernetes 叢集遷移到另一個。我們將使用 redis-cluster Bitnami Helm chart 來安裝 Redis。

所需工具

先決條件

為此,我需要兩個 Kubernetes 叢集,它們都可以訪問公共的網路和儲存;我將我的叢集命名為 sourcedestination。具體來說,我需要:

  • 在兩個叢集上都啟用 StatefulSetStartOrdinal 特性門控。
  • kubectl 配置客戶端,讓我能夠以管理員身份訪問兩個叢集。
  • 在兩個叢集上都安裝了相同的 StorageClass,並將其設定為兩個叢集的預設 StorageClass。此 StorageClass 應能提供可從任一叢集或兩個叢集訪問的底層儲存。
  • 一個扁平的網路拓撲,允許 Pod 向任一叢集中的 Pod 傳送和接收資料包。如果您在雲提供商上建立叢集,此配置可能稱為私有云或私有網路。
  1. 在兩個叢集上建立一個演示名稱空間

    kubectl create ns kep-3335
    
  2. 在源叢集中部署一個包含六個副本的 Redis 叢集

    helm repo add bitnami https://charts.bitnami.com/bitnami
    helm install redis --namespace kep-3335 \
      bitnami/redis-cluster \
      --set persistence.size=1Gi \
      --set cluster.nodes=6
    
  3. 在源叢集中檢查複製狀態

    kubectl exec -it redis-redis-cluster-0 -- /bin/bash -c \
      "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;"
    
    2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 myself,master - 0 1669764411000 3 connected 10923-16383                                                                                                                                              
    7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669764410000 3 connected                                                                                             
    961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669764411000 1 connected                                                                                                             
    7136e37d8864db983f334b85d2b094be47c830e5 10.104.0.15:6379@16379 slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669764412595 2 connected                                                                                                                    
    a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669764411592 1 connected 0-5460                                                                                                                                                   
    2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669764410000 2 connected 5461-10922
    
  4. 在目標叢集中部署一個包含零個副本的 Redis 叢集

    helm install redis --namespace kep-3335 \
      bitnami/redis-cluster \
      --set persistence.size=1Gi \
      --set cluster.nodes=0 \
      --set redis.extraEnvVars\[0\].name=REDIS_NODES,redis.extraEnvVars\[0\].value="redis-redis-cluster-headless.kep-3335.svc.cluster.local" \
      --set existingSecret=redis-redis-cluster
    
  5. 在源叢集中將 redis-redis-cluster StatefulSet 的副本數減少 1,以移除副本 redis-redis-cluster-5

    kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}'
    
  6. 將依賴項從源叢集遷移到目標叢集

    以下命令將資源從 source 複製到 destionation。與 destination 叢集無關的詳細資訊(例如:uidresourceVersionstatus)將被刪除。

    源叢集的步驟

    注意:如果使用的 StorageClass 配置了 reclaimPolicy: Delete,您應在刪除前將 source 中的 PV 的 reclaimPolicy 修改為 Retain,以保留在 destination 中使用的底層儲存。更多詳情請參閱更改 PersistentVolume 的回收策略

    kubectl get pvc redis-data-redis-redis-cluster-5 -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .status)' > /tmp/pvc-redis-data-redis-redis-cluster-5.yaml
    kubectl get pv $(yq '.spec.volumeName' /tmp/pvc-redis-data-redis-redis-cluster-5.yaml) -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion, .metadata.annotations, .metadata.finalizers, .spec.claimRef, .status)' > /tmp/pv-redis-data-redis-redis-cluster-5.yaml
    kubectl get secret redis-redis-cluster -o yaml | yq 'del(.metadata.uid, .metadata.resourceVersion)' > /tmp/secret-redis-redis-cluster.yaml
    

    目標叢集的步驟

    注意:對於 PV/PVC,此過程僅在您的 PV 使用的底層儲存系統支援被複制到 destination 時才有效。與特定節點或拓撲關聯的儲存可能不受支援。此外,一些儲存系統可能會在 PV 物件之外儲存有關卷的附加元資料,並且可能需要更專門的步驟來匯入卷。

    kubectl create -f /tmp/pv-redis-data-redis-redis-cluster-5.yaml
    kubectl create -f /tmp/pvc-redis-data-redis-redis-cluster-5.yaml
    kubectl create -f /tmp/secret-redis-redis-cluster.yaml
    
  7. 在目標叢集中將 redis-redis-cluster StatefulSet 擴容 1 個副本,起始序號為 5

    kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}'
    
  8. 在目標叢集中檢查複製狀態

    kubectl exec -it redis-redis-cluster-5 -- /bin/bash -c \
      "redis-cli -c -h redis-redis-cluster -a $(kubectl get secret redis-redis-cluster -o jsonpath="{.data.redis-password}" | base64 -d) CLUSTER NODES;"
    

    我應該會看到新的副本(標記為 myself)已加入 Redis 叢集(其 IP 地址屬於與源叢集中副本不同的 CIDR 塊)。

    2cff613d763b22c180cd40668da8e452edef3fc8 10.104.0.17:6379@16379 master - 0 1669766684000 2 connected 5461-10922
    7136e37d8864db983f334b85d2b094be47c830e5 10.108.0.22:6379@16379 myself,slave 2cff613d763b22c180cd40668da8e452edef3fc8 0 1669766685609 2 connected
    2ce30362c188aabc06f3eee5d92892d95b1da5c3 10.104.0.14:6379@16379 master - 0 1669766684000 3 connected 10923-16383
    961f35e37c4eea507cfe12f96e3bfd694b9c21d4 10.104.0.18:6379@16379 slave a8765caed08f3e185cef22bd09edf409dc2bcc61 0 1669766683600 1 connected
    a8765caed08f3e185cef22bd09edf409dc2bcc61 10.104.0.19:6379@16379 master - 0 1669766685000 1 connected 0-5460
    7743661f60b6b17b5c71d083260419588b4f2451 10.104.0.16:6379@16379 slave 2ce30362c188aabc06f3eee5d92892d95b1da5c3 0 1669766686613 3 connected
    
  9. 對其餘副本重複步驟 #5 到 #7,直到源叢集中的 Redis StatefulSet 縮減到 0 個副本,且目標叢集中的 Redis StatefulSet 狀態健康並擁有總共 6 個副本。

接下來呢?

此功能為 StatefulSet 跨叢集拆分提供了基礎構建塊,但並未規定 StatefulSet 應如何遷移。遷移需要協調 StatefulSet 副本,以及儲存和網路層的編排。這取決於 StatefulSet 安裝的應用的儲存和連線要求。此外,許多 StatefulSet 由Operator管理,這給遷移增加了另一層複雜性。

如果您有興趣構建增強功能以簡化這些流程,請參與 SIG Multicluster 貢獻您的力量!