StatefulSet
StatefulSet 是用于管理有状态应用的负载 API 对象。
管理一组 Pod 的部署和扩缩,并保证这些 Pod 的顺序和唯一性。
与 Deployment 一样,StatefulSet 管理基于相同容器 Spec 的 Pod。与 Deployment 不同的是,StatefulSet 为其每个 Pod 维护一个固定的身份。这些 Pod 是从相同的 Spec 创建的,但不可互换:每个 Pod 都具有持久的标识符,无论如何重新调度,它都会保持此标识符。
如果要使用存储卷为工作负载提供持久性,可以将 StatefulSet 用作解决方案的一部分。尽管 StatefulSet 中的单个 Pod 容易发生故障,但持久的 Pod 标识符使得更容易将现有卷与替换故障 Pod 的新 Pod 进行匹配。
使用 StatefulSet
StatefulSet 对于需要以下一项或多项功能的应用非常有用。
- 稳定、唯一的网络标识符。
- 稳定、持久的存储。
- 有序、优雅的部署和扩缩。
- 有序、自动化的滚动更新。
在上面,稳定与 Pod (重新)调度的持久性同义。如果应用不需要任何稳定的标识符或有序的部署、删除或扩缩,则应该使用提供无状态副本集合的工作负载对象来部署应用。 Deployment 或 ReplicaSet 可能更适合你的无状态需求。
限制
- 给定 Pod 的存储必须基于所请求的 存储类(storage class) 由 PersistentVolume Provisioner 进行供应(示例在此),或者由管理员预先供应。
- 删除和/或缩减 StatefulSet 不会删除与 StatefulSet 关联的卷。这样做是为了确保数据安全,这通常比自动清除所有相关的 StatefulSet 资源更有价值。
- StatefulSet 当前需要一个 Headless Service 来负责 Pod 的网络身份。你有责任创建此 Service。
- StatefulSet 不提供在删除 StatefulSet 时 Pod 终止方面的任何保证。为了实现 StatefulSet 中 Pod 的有序且优雅终止,可以在删除之前将 StatefulSet 缩减到 0。
- 当使用默认的 Pod 管理策略(
OrderedReady
)进行滚动更新时,可能会陷入损坏状态,需要手动干预进行修复。
组件
以下示例演示了 StatefulSet 的组件。
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
selector:
matchLabels:
app: nginx # has to match .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # by default is 1
minReadySeconds: 10 # by default is 0
template:
metadata:
labels:
app: nginx # has to match .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.24
ports:
- containerPort: 80
name: web
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "my-storage-class"
resources:
requests:
storage: 1Gi
注意
此示例为了简单起见使用了ReadWriteOnce
访问模式。对于生产用途,Kubernetes 项目建议改用 ReadWriteOncePod
访问模式。在以上示例中
- 一个名为
nginx
的 Headless Service 用于控制网络域。 - 名为
web
的 StatefulSet 有一个 Spec,指示将在唯一的 Pod 中启动 3 个 nginx 容器副本。 volumeClaimTemplates
将使用由 PersistentVolume Provisioner 供应的 PersistentVolumes 提供稳定的存储。
StatefulSet 对象的名称必须是有效的 DNS 标签。
Pod Selector
必须将 StatefulSet 的 .spec.selector
字段设置为与其 .spec.template.metadata.labels
的标签相匹配。未能指定匹配的 Pod Selector 将导致在创建 StatefulSet 时出现验证错误。
卷申领模板 (Volume Claim Templates)
可以设置 .spec.volumeClaimTemplates
字段来创建 PersistentVolumeClaim。这将为 StatefulSet 提供稳定的存储,如果满足以下任一条件:
- 为卷申领指定的 StorageClass 设置为使用动态供应,或者
- 集群中已经包含具有正确 StorageClass 和足够可用存储空间的 PersistentVolume。
最小就绪秒数
Kubernetes v1.25 [stable]
.spec.minReadySeconds
是一个可选字段,它指定新创建的 Pod 运行并就绪而没有任何容器崩溃的最小秒数,这样才算可用。在使用滚动更新策略时,这用于检查上线进度。此字段默认为 0(Pod 在就绪后立即被视为可用)。要了解有关何时 Pod 被视为就绪的更多信息,请参阅容器探针。
Pod 身份
StatefulSet Pod 具有由序号、稳定网络身份和稳定存储组成的唯一身份。无论 Pod 被(重新)调度到哪个节点上,该身份都将保留。
序号索引
对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 都将分配一个整数序号,该序号在集合中是唯一的。默认情况下,Pod 的序号从 0 到 N-1。StatefulSet 控制器还会添加一个带有此索引的 Pod 标签:apps.kubernetes.io/pod-index
。
起始序号
Kubernetes v1.31 [stable]
(enabled by default: true).spec.ordinals
是一个可选字段,允许你配置分配给每个 Pod 的整数序号。它默认为 nil。在该字段中,可以配置以下选项:
.spec.ordinals.start
:如果设置了.spec.ordinals.start
字段,Pod 的序号将从.spec.ordinals.start
开始,直到.spec.ordinals.start + .spec.replicas - 1
。
稳定网络 ID
StatefulSet 中的每个 Pod 的主机名都派生自 StatefulSet 的名称和 Pod 的序号。构造的主机名模式为 $(statefulset name)-$(ordinal)
。上面的示例将创建三个名为 web-0,web-1,web-2
的 Pod。StatefulSet 可以使用 Headless Service 来控制其 Pod 的域名。此 Service 管理的域名格式为:$(service name).$(namespace).svc.cluster.local
,其中“cluster.local”是集群域名。创建每个 Pod 时,它会获得一个匹配的 DNS 子域,格式为:$(podname).$(governing service domain)
,其中 governing service 由 StatefulSet 上的 serviceName
字段定义。
根据集群中 DNS 的配置方式,可能无法立即查找新运行的 Pod 的 DNS 名称。当集群中的其他客户端在 Pod 创建之前已经发送了对 Pod 主机名的查询时,可能会发生此行为。负缓存(DNS 中的正常现象)意味着即使 Pod 正在运行,之前失败查找的结果也会被记住并重用,至少持续几秒钟。
如果需要在 Pod 创建后立即发现它们,有几种选择:
- 直接查询 Kubernetes API(例如,使用 watch),而不是依赖 DNS 查找。
- 减少 Kubernetes DNS 提供者中的缓存时间(通常这意味着编辑 CoreDNS 的 ConfigMap,CoreDNS 当前缓存 30 秒)。
如限制部分所述,你有责任创建负责 Pod 网络身份的 Headless Service。
以下是一些集群域、Service 名称、StatefulSet 名称以及它们如何影响 StatefulSet 的 Pod DNS 名称的示例。
集群域 (Cluster Domain) | Service (命名空间/名称) | StatefulSet (命名空间/名称) | StatefulSet 域 | Pod DNS | Pod 主机名 |
---|---|---|---|---|---|
cluster.local | default/nginx | default/web | nginx.default.svc.cluster.local | web-{0..N-1}.nginx.default.svc.cluster.local | web-{0..N-1} |
cluster.local | foo/nginx | foo/web | nginx.foo.svc.cluster.local | web-{0..N-1}.nginx.foo.svc.cluster.local | web-{0..N-1} |
kube.local | foo/nginx | foo/web | nginx.foo.svc.kube.local | web-{0..N-1}.nginx.foo.svc.kube.local | web-{0..N-1} |
稳定存储
对于 StatefulSet 中定义的每个 VolumeClaimTemplate 条目,每个 Pod 都会获得一个 PersistentVolumeClaim。在上面的 nginx 示例中,每个 Pod 都会获得一个 StorageClass 为 my-storage-class
且已供应 1 GiB 存储空间的 PersistentVolume。如果没有指定 StorageClass,则将使用默认的 StorageClass。当 Pod 被(重新)调度到节点上时,其 volumeMounts
将挂载与其 PersistentVolume Claim 关联的 PersistentVolumes。请注意,与 Pod 的 PersistentVolume Claim 关联的 PersistentVolumes 在 Pod 或 StatefulSet 被删除时不会被删除。这必须手动完成。
Pod 名称标签
当 StatefulSet 控制器创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name
,该标签的值设置为 Pod 的名称。此标签允许将 Service 附加到 StatefulSet 中的特定 Pod。
Pod 索引标签
Kubernetes v1.32 [stable]
(enabled by default: true)当 StatefulSet 控制器创建 Pod 时,新 Pod 会被标记为 apps.kubernetes.io/pod-index
。此标签的值是 Pod 的序号索引。此标签允许你将流量路由到特定的 Pod 索引,使用 Pod 索引标签过滤日志/指标等。请注意,对于此特性,特性门控 PodIndexLabel
默认启用并锁定;要禁用它,用户必须使用服务器模拟版本 v1.31。
部署和扩缩保证
- 对于具有 N 个副本的 StatefulSet,在部署 Pod 时,它们将按顺序 {0..N-1} 依次创建。
- 在删除 Pod 时,它们将按相反顺序 {N-1..0} 依次终止。
- 在对 Pod 应用扩缩操作之前,其所有前驱必须处于 Running 和 Ready 状态。
- 在终止 Pod 之前,其所有后继必须完全关闭。
StatefulSet 不应指定 pod.Spec.TerminationGracePeriodSeconds
为 0。这种做法是不安全的,强烈不建议这样做。有关进一步说明,请参阅强制删除 StatefulSet Pod。
创建上述 nginx 示例时,将按 web-0、web-1、web-2 的顺序部署三个 Pod。web-1 不会在 web-0 处于Running 和 Ready 状态之前部署,web-2 不会在 web-1 处于 Running 和 Ready 状态之前部署。如果 web-0 在 web-1 处于 Running 和 Ready 状态后、但在 web-2 启动之前发生故障,则 web-2 将不会启动,直到 web-0 成功重新启动并处于 Running 和 Ready 状态为止。
如果用户通过 Patch StatefulSet 将 replicas=1
来缩减已部署的示例,则 web-2 将首先终止。web-1 不会在 web-2 完全关闭并删除之前终止。如果 web-0 在 web-2 已终止并完全关闭后但在 web-1 终止之前发生故障,则 web-1 将不会终止,直到 web-0 处于 Running 和 Ready 状态为止。
Pod 管理策略
StatefulSet 允许你通过 .spec.podManagementPolicy
字段在保留其唯一性和身份保证的同时放宽其顺序保证。
OrderedReady Pod 管理
OrderedReady
Pod 管理是 StatefulSet 的默认设置。它实现了上述描述的行为。
并行 Pod 管理
Parallel
Pod 管理告知 StatefulSet 控制器并行启动或终止所有 Pod,并且在启动或终止另一个 Pod 之前不等待 Pod 达到 Running 和 Ready 状态或完全终止。此选项仅影响扩缩操作的行为。更新不受影响。
更新策略
StatefulSet 的 .spec.updateStrategy
字段允许你配置和禁用 StatefulSet 中 Pod 的容器、标签、资源请求/限制和注解的自动化滚动更新。有两个可能的值:
OnDelete
- 当 StatefulSet 的
.spec.updateStrategy.type
设置为OnDelete
时,StatefulSet 控制器不会自动更新 StatefulSet 中的 Pod。用户必须手动删除 Pod,才能使控制器创建反映 StatefulSet 的.spec.template
所做修改的新 Pod。 RollingUpdate
RollingUpdate
更新策略为 StatefulSet 中的 Pod 实现自动化滚动更新。这是默认的更新策略。
滚动更新
当 StatefulSet 的 .spec.updateStrategy.type
设置为 RollingUpdate
时,StatefulSet 控制器将删除并重新创建 StatefulSet 中的每个 Pod。它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,一次更新一个 Pod。
Kubernetes 控制平面会等待更新后的 Pod 达到 Running 和 Ready 状态,然后才更新其前驱。如果设置了 .spec.minReadySeconds
(参见最小就绪秒数),控制平面会在 Pod 准备就绪后额外等待该时间,然后才继续。
分区滚动更新
通过指定 .spec.updateStrategy.rollingUpdate.partition
,可以对 RollingUpdate
更新策略进行分区。如果指定了分区,则当 StatefulSet 的 .spec.template
更新时,所有序号大于或等于该分区的 Pod 都将被更新。序号小于该分区的所有 Pod 都不会被更新,即使它们被删除,也将在之前版本重建。如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition
大于其 .spec.replicas
,则对其 .spec.template
的更新不会传播到其 Pod。在大多数情况下,你不需要使用分区,但如果你想分阶段更新、推出金丝雀版本或执行分阶段发布,分区会很有用。
最大不可用 Pod 数
Kubernetes v1.24 [alpha]
通过指定 .spec.updateStrategy.rollingUpdate.maxUnavailable
字段,可以控制更新期间不可用的 Pod 的最大数量。该值可以是绝对数字(例如,5
)或期望 Pod 数的百分比(例如,10%
)。绝对数字是通过百分比值向上取整计算得出。此字段不能为 0。默认设置为 1。
此字段适用于范围 0
到 replicas - 1
中的所有 Pod。如果在范围 0
到 replicas - 1
中有任何不可用的 Pod,它将被计入 maxUnavailable
。
强制回滚
当使用默认的 Pod 管理策略(OrderedReady
)进行滚动更新时,可能会陷入损坏状态,需要手动干预进行修复。
如果将 Pod 模板更新为永远不会达到 Running 和 Ready 状态的配置(例如,由于错误的二进制文件或应用级配置错误),StatefulSet 将停止上线并等待。
在此状态下,仅将 Pod 模板恢复到良好配置是不够的。由于存在已知问题,StatefulSet 将继续等待损坏的 Pod 变为 Ready(这永远不会发生),然后才会尝试将其恢复到工作配置。(Preserved link)
恢复模板后,还必须删除 StatefulSet 已经尝试使用不良配置运行的任何 Pod。然后,StatefulSet 将开始使用已恢复的模板重新创建 Pod。
PersistentVolumeClaim 保留策略
Kubernetes v1.32 [stable]
(enabled by default: true)可选字段 .spec.persistentVolumeClaimRetentionPolicy
控制在 StatefulSet 生命周期中是否以及如何删除 PVC。必须在 API 服务器和控制器管理器上启用 StatefulSetAutoDeletePVC
特性门控 才能使用此字段。启用后,可以为每个 StatefulSet 配置两个策略:
whenDeleted
- 配置在 StatefulSet 被删除时应用的卷保留行为
whenScaled
- 配置在 StatefulSet 的副本数减少时应用的卷保留行为;例如,缩减 StatefulSet 时。
对于可以配置的每个策略,可以将值设置为 Delete
或 Retain
。
Delete
- 对于受策略影响的每个 Pod,根据 StatefulSet
volumeClaimTemplate
创建的 PVC 将被删除。使用whenDeleted
策略,来自volumeClaimTemplate
的所有 PVC 在其 Pod 被删除后都会被删除。使用whenScaled
策略,只有与正在缩减的 Pod 副本对应的 PVC 会在其 Pod 被删除后被删除。 Retain
(默认)- 当 Pod 被删除时,来自
volumeClaimTemplate
的 PVC 不受影响。这是此新特性之前的行为。
请记住,这些策略仅在由于 StatefulSet 被删除或缩减而导致 Pod 被移除时适用。例如,如果与 StatefulSet 关联的 Pod 由于节点故障而失败,并且控制平面创建了替代 Pod,则 StatefulSet 会保留现有的 PVC。现有卷不受影响,集群会将其附加到新 Pod 即将启动的节点上。
策略的默认值为 Retain
,与此新特性之前的 StatefulSet 行为匹配。
以下是一个策略示例。
apiVersion: apps/v1
kind: StatefulSet
...
spec:
persistentVolumeClaimRetentionPolicy:
whenDeleted: Retain
whenScaled: Delete
...
StatefulSet 控制器将其 PVC 添加到所有者引用中,这些 PVC 会在 Pod 终止后被垃圾收集器删除。这使得 Pod 能够在 PVC 被删除(以及根据保留策略,在其背后的 PV 和卷被删除)之前干净地卸载所有卷。将 whenDeleted
策略设置为 Delete
时,与该 StatefulSet 关联的所有 PVC 上都会放置对 StatefulSet 实例的所有者引用。
whenScaled
策略必须仅在 Pod 被缩减时删除 PVC,而不是因其他原因删除 Pod 时。在协调时,StatefulSet 控制器会将其期望的副本计数与集群中实际存在的 Pod 进行比较。任何 ID 大于副本数的 StatefulSet Pod 都将被标记为淘汰和删除。如果 whenScaled
策略是 Delete
,则被淘汰的 Pod 在被删除之前,会首先被设置为关联 StatefulSet 模板 PVC 的所有者。这会导致仅在被淘汰的 Pod 终止后,这些 PVC 才会被垃圾收集。
这意味着如果控制器崩溃并重启,在根据策略适当更新其所有者引用之前,不会删除任何 Pod。如果在控制器停止运行时强制删除了一个待删除的 Pod,那么其所有者引用可能已设置,也可能未设置,具体取决于控制器何时崩溃。更新所有者引用可能需要多个协调周期,因此一些待删除的 Pod 可能已设置了所有者引用,而另一些则没有。因此,我们建议等待控制器重新启动,它会在终止 Pod 之前验证所有者引用。如果无法做到这一点,操作员应验证 PVC 上的所有者引用,以确保当 Pod 被强制删除时,预期的对象也会被删除。
副本数
.spec.replicas
是一个可选字段,它指定了期望的 Pod 数量。默认为 1。
如果你手动扩缩了 StatefulSet,例如通过 kubectl scale statefulset statefulset --replicas=X
,然后你基于 manifest 更新了该 StatefulSet(例如:通过运行 kubectl apply -f statefulset.yaml
),那么应用该 manifest 将会覆盖你之前手动执行的扩缩操作。
如果一个 HorizontalPodAutoscaler (或任何类似的水平扩缩 API) 正在管理 StatefulSet 的扩缩,请不要设置 .spec.replicas
。相反,让 Kubernetes 控制平面自动管理 .spec.replicas
字段。
接下来
- 了解 Pod。
- 了解如何使用 StatefulSet
- 学习部署有状态应用的示例。
- 学习使用 Stateful Set 部署 Cassandra 的示例。
- 学习运行有副本的有状态应用的示例。
- 了解如何扩缩 StatefulSet。
- 了解删除 StatefulSet 时涉及的内容。
- 了解如何配置 Pod 使用卷存储。
- 了解如何配置 Pod 使用 PersistentVolume 存储。
StatefulSet
是 Kubernetes REST API 中的顶级资源。阅读StatefulSet 对象定义以了解 StatefulSet 的 API。- 阅读有关 PodDisruptionBudget 的信息,以及如何在中断期间使用它来管理应用程序的可用性。