这篇文章已超过一年。较旧的文章可能包含过时内容。请检查页面信息自发布以来是否已失效。
Kubernetes 1.27:StatefulSet 起始序号简化迁移
Kubernetes v1.26 为 StatefulSet 引入了一个新的 Alpha 级别特性,用于控制 Pod 副本的序号编号。从 Kubernetes v1.27 开始,此特性已升级到 Beta。序号可以从任意非负数开始。本文将讨论如何使用此特性。
背景
StatefulSet 序号为 Pod 副本提供了顺序标识。使用 OrderedReady
Pod 管理时,Pod 会按照序号索引 0
到 N-1
的顺序创建。
如今在 Kubernetes 中,协调跨集群的 StatefulSet 迁移具有挑战性。备份和恢复解决方案确实存在,但这些方案要求应用在迁移前缩容到零副本。在当今完全连接的世界中,即使是计划内的应用停机也可能无法满足你的业务目标。你可以使用 Cascading Delete 或 On Delete 来迁移单个 Pod,但这容易出错且管理繁琐。当 Pod 失败或被逐出时,你会失去 StatefulSet 控制器的自愈益处。
Kubernetes v1.26 使 StatefulSet 能够负责 {0..N-1} 范围内的序号(即从 0 到 N-1 的序号)。有了它,你可以在源集群中缩容 {0..k-1} 范围内的副本,并在目标集群中扩容剩余范围 {k..N-1} 的副本,同时保持应用可用性。这使得你在协调跨集群迁移时,能够保留最多一个的语义(意味着在一个 StatefulSet 中,具有给定标识的 Pod 最多只有一个)和 滚动更新行为。
我为什么想使用这个特性?
假设你在一个集群中运行 StatefulSet,需要将其迁移到另一个不同的集群。有很多原因可能导致你需要这样做:
- 可扩展性:你的 StatefulSet 对当前集群而言规模过大,并已开始影响集群中其他工作负载的服务质量。
- 隔离性:你在一个被多个用户访问的集群中运行 StatefulSet,并且 namespace 隔离不足。
- 集群配置:你希望将 StatefulSet 迁移到另一个集群,以使用当前集群中不可用的某些环境。
- 控制平面升级:你希望将 StatefulSet 迁移到运行已升级控制平面的集群,并且无法承担原地控制平面升级的风险或停机时间。
如何使用它?
在集群上启用 StatefulSetStartOrdinal
Feature Gate,并创建一个具有自定义 .spec.ordinals.start
的 StatefulSet。
动手试试
在本演示中,我将使用新的机制将一个 StatefulSet 从一个 Kubernetes 集群迁移到另一个。将使用 redis-cluster Bitnami Helm Chart 来安装 Redis。
所需工具
先决条件
为此,我需要两个 Kubernetes 集群,它们都能够访问共同的网络和存储;我将我的集群命名为 source
和 destination
。具体而言,我需要:
- 在两个集群上都启用了
StatefulSetStartOrdinal
Feature Gate。 - 允许我作为管理员访问两个集群的
kubectl
客户端配置。 - 在两个集群上都安装了相同的
StorageClass
,并将其设置为两个集群的默认 StorageClass。此StorageClass
应能提供可从任一或两个集群访问的基础存储。 - 一种扁平网络拓扑,允许 Pod 在任一集群中相互发送和接收数据包。如果你在云提供商上创建集群,此配置可能被称为私有云或私有网络。
在两个集群上创建 demo namespace
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
复制到destination
。与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 贡献!