本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
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 集群,它们都可以访问公共的网络和存储;我将我的集群命名为 source
和 destination
。具体来说,我需要:
- 在两个集群上都启用
StatefulSetStartOrdinal
特性门控。 - 为
kubectl
配置客户端,让我能够以管理员身份访问两个集群。 - 在两个集群上都安装了相同的
StorageClass
,并将其设置为两个集群的默认 StorageClass。此StorageClass
应能提供可从任一集群或两个集群访问的底层存储。 - 一个扁平的网络拓扑,允许 Pod 向任一集群中的 Pod 发送和接收数据包。如果您在云提供商上创建集群,此配置可能称为私有云或私有网络。
在两个集群上创建一个演示命名空间
kubectl create ns kep-3335
在源集群中部署一个包含六个副本的 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
在源集群中检查复制状态
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
在目标集群中部署一个包含零个副本的 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
在源集群中将
redis-redis-cluster
StatefulSet 的副本数减少 1,以移除副本redis-redis-cluster-5
kubectl patch sts redis-redis-cluster -p '{"spec": {"replicas": 5}}'
将依赖项从源集群迁移到目标集群
以下命令将资源从
source
复制到destionation
。与destination
集群无关的详细信息(例如:uid
、resourceVersion
、status
)将被删除。源集群的步骤
注意:如果使用的
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
在目标集群中将
redis-redis-cluster
StatefulSet 扩容 1 个副本,起始序号为 5kubectl patch sts redis-redis-cluster -p '{"spec": {"ordinals": {"start": 5}, "replicas": 1}}'
在目标集群中检查复制状态
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
对其余副本重复步骤 #5 到 #7,直到源集群中的 Redis StatefulSet 缩减到 0 个副本,且目标集群中的 Redis StatefulSet 状态健康并拥有总共 6 个副本。
接下来呢?
此功能为 StatefulSet 跨集群拆分提供了基础构建块,但并未规定 StatefulSet 应如何迁移。迁移需要协调 StatefulSet 副本,以及存储和网络层的编排。这取决于 StatefulSet 安装的应用的存储和连接要求。此外,许多 StatefulSet 由Operator管理,这给迁移增加了另一层复杂性。
如果您有兴趣构建增强功能以简化这些流程,请参与 SIG Multicluster 贡献您的力量!