作者:Mayank Kumar(Salesforce)
Kubernetes StatefulSets[1]自从在 1.5 中引入,并在 1.9 中变得稳定以来,已经被广泛用于运行有状态应用程序。它提供稳定的单元身份、持久的单元存储,以及有序的部署、扩展和滚动更新。你可以将 StatefulSet 视为运行复杂的有状态应用程序的原子构建块。随着 Kubernetes 的使用越来越多,需要 StatefulSets 的场景也越来越多。在你对 StatefulSets 使用 OrderedReady pod 管理策略的情况下,许多这样的场景需要比当前支持的一次一个 Pod 更新更快的滚动更新。
以下是一些例子:
为了支持这样的场景,Kubernetes 1.24 包含了一个新的 alpha 特性来提供帮助。在使用新功能之前,你必须启用 MaxUnavailableStatefulSet 功能标志。一旦启用了它,就可以指定一个名为 maxUnavailable 的新字段,这是 StatefulSet 规范的一部分。例如:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
namespace: default
spec:
podManagementPolicy: OrderedReady # you must set OrderedReady
replicas: 5
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: k8s.gcr.io/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
updateStrategy:
rollingUpdate:
maxUnavailable: 2 # this is the new alpha field, whose default value is 1
partition: 0
type: RollingUpdate
如果启用了新功能,并且没有在 StatefulSet 中指定 maxUnavailable 的值,Kubernetes 会应用默认的 maxUnavailable: 1。这与你在不启用新功能时看到的行为相匹配。
我将运行一个基于该示例清单的场景来演示这个特性是如何工作的。我将部署一个有 5 个副本的 StatefulSet,其中 maxUnavailable 设置为 2,partition 设置为 0。
我可以通过将镜像更改为 k8s.gcr.io/nginx-slim:0.9 来触发滚动更新。一旦启动滚动更新,我就可以一次查看 2 个 pod 更新,因为 maxUnavailable 的当前值是 2。下面的输出显示了一段时间,并且不完整。maxUnavailable 可以是一个绝对数字(例如,2)或所需 pod 的百分比(例如,10%)。绝对数是通过向下舍入的百分比计算出来的。
kubectl get pods --watch
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 85s
web-1 1/1 Running 0 2m6s
web-2 1/1 Running 0 106s
web-3 1/1 Running 0 2m47s
web-4 1/1 Running 0 2m27s
web-4 1/1 Terminating 0 5m43s ----> start terminating 4
web-3 1/1 Terminating 0 6m3s ----> start terminating 3
web-3 0/1 Terminating 0 6m7s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-4 0/1 Terminating 0 5m48s
web-4 0/1 Terminating 0 5m48s
web-3 0/1 ContainerCreating 0 2s
web-3 1/1 Running 0 2s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 1/1 Running 0 1s
web-2 1/1 Terminating 0 5m46s ----> start terminating 2 (only after both 4 and 3 are running)
web-1 1/1 Terminating 0 6m6s ----> start terminating 1
web-2 0/1 Terminating 0 5m47s
web-1 0/1 Terminating 0 6m7s
web-1 0/1 Pending 0 0s
web-1 0/1 Pending 0 0s
web-1 0/1 ContainerCreating 0 1s
web-1 1/1 Running 0 2s
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 1s
web-0 1/1 Terminating 0 6m6s ----> start terminating 0 (only after 2 and 1 are running)
web-0 0/1 Terminating 0 6m7s
web-0 0/1 Pending 0 0s
web-0 0/1 Pending 0 0s
web-0 0/1 ContainerCreating 0 0s
web-0 1/1 Running 0 1s
请注意,一旦滚动更新开始,4 和 3(两个最高序号的 pod)将同时终止。序号为 4 和 3 的 pod 可以按照自己的速度准备好。一旦 4 号和 3 号 pod 准备就绪,2 号 pod 和 1 号 pod 同时开始终止。当 pod 2 和 1 都在运行并就绪时,pod 0 开始终止。
在 Kubernetes 中,更新 Pod 时,对 StatefulSets 的更新遵循严格的顺序。在此示例中,更新从副本 4 开始,然后是副本 3,然后是副本 2,依此类推,一次一个 pod。当一次处理一个 pod 时,3 不可能在 4 之前运行并准备好。当 maxUnavailable 大于 1 时(在示例场景中,我将 maxUnavailable 设置为 2),副本 3 可能在副本 4 准备好之前就准备好并运行了,这是可以的。如果你是一名开发人员,并且将 maxUnavailable 设置为大于 1,那么你应该知道这种结果是可能的,并且你必须确保你的应用程序能够处理可能出现的排序问题。当你将 maxUnavailable 设置为大于 1 时,将保证每批 pod 更新之间的顺序。这种保证意味着更新批次 2 中的 pod(副本 2 和 1)不能开始更新,直到批次 0 中的 pod(副本 4 和 3)准备就绪。
尽管 Kubernetes 将这些称为副本(replica),但是你的有状态应用程序可能有不同的视图,并且 StatefulSet 的每个 pod 可能持有与其他 pod 完全不同的数据。这里重要的是对 StatefulSets 的更新是成批发生的,现在可以拥有大于 1 的批处理大小(作为 alpha 特性)。
另请注意,上述行为是针对 podManagementPolicy: OrderedReady 的。如果你将 StatefulSet 定义为 podManagementPolicy: Parallel,则不仅 maxUnavailable 数量的副本被同时终止;maxUnavailable 副本数也同时于 ContainerCreating 阶段启动。这叫 bursting。
所以,现在你可能有很多关于以下方面的问题:
你自己试试看可能更好。这是一个 alpha 特性,Kubernetes 的贡献者正在寻找关于这个特性的反馈。这是否有助于你实现你的有状态场景?你是否发现了一个 bug,或者你是否认为实现的行为不直观,或者可能会破坏应用程序或让它们措手不及?请提交问题[2]让我们知道。
[1]
StatefulSets: https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/
[2]
提交问题: https://github.com/kubernetes/kubernetes/issues
[3]
最大不可用 Pod: https://kubernetes.io/zh/docs/concepts/workloads/controllers/statefulset/#%E6%9C%80%E5%A4%A7%E4%B8%8D%E5%8F%AF%E7%94%A8-pod
[4]
KEP: https://github.com/kubernetes/enhancements/tree/master/keps/sig-apps/961-maxunavailable-for-statefulset
[5]
实现: https://github.com/kubernetes/kubernetes/pull/82162/files
[6]
增强: https://github.com/kubernetes/enhancements/issues/961
CNCF (Cloud Native Computing Foundation)成立于2015年12月,隶属于Linux Foundation,是非营利性组织。
CNCF(云原生计算基金会)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用