本文发布已超过一年。较旧的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已变得不正确。

Kubernetes 1.27:StatefulSet 启动序号简化迁移

Kubernetes v1.26 为 StatefulSets 引入了一项新的 alpha 级功能,用于控制 Pod 副本的序号编号。从 Kubernetes v1.27 开始,此功能现已进入 beta 阶段。序号可以从任意非负数开始。这篇博客文章将讨论如何使用此功能。

背景

StatefulSets 序号为 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)。

    源集群的步骤

    注意:如果使用配置了 reclaimPolicy: DeleteStorageClass,则应在删除之前使用 reclaimPolicy: Retain 补丁 source 中的 PV,以保留 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 以做出贡献!