StatefulSets
StatefulSet 是用于管理有状态应用程序的工作负载 API 对象。
管理基于相同容器规范的一组 Pod 的部署和伸缩,并保证这些 Pod 的排序和唯一性。
与 Deployment 一样,StatefulSet 管理基于相同的容器规范的 Pod。与 Deployment 不同,StatefulSet 为其每个 Pod 维护一个稳定的标识。这些 Pod 是从相同的规范创建的,但不能互换:每个 Pod 都有一个持久的标识符,即使在重新调度时也会保留。
如果希望使用存储卷为工作负载提供持久性,可以将 StatefulSet 作为解决方案的一部分使用。虽然 StatefulSet 中的单个 Pod 容易发生故障,但持久的 Pod 标识符使得将现有的卷与替换任何已失败 Pod 的新 Pod 匹配更容易。
使用 StatefulSets
StatefulSet 对于需要以下一项或多项的应用程序非常有用
- 稳定、唯一的网络标识。
- 稳定、持久的存储。
- 有序、优雅的部署和伸缩。
- 有序、自动的滚动更新。
在上述情况下,稳定与跨 Pod(重新)调度的持久性同义。如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应使用提供一组无状态副本的工作负载对象部署应用程序。 Deployment 或 ReplicaSet 可能更适合您的无状态需求。
限制
- 给定 Pod 的存储必须由 PersistentVolume Provisioner 根据请求的 storage class 进行配置,或者由管理员预先配置。
- 删除和/或伸缩 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 指示将启动 3 个 nginx 容器的副本,这些副本在唯一的 Pod 中启动。 volumeClaimTemplates将使用由 PersistentVolume Provisioner 配置的 PersistentVolumes 提供稳定的存储。
StatefulSet 对象的名称必须是有效的 DNS 标签。
Pod 选择器
必须将 StatefulSet 的 .spec.selector 字段设置为与 .spec.template.metadata.labels 的标签匹配。如果在 StatefulSet 创建期间未指定匹配的 Pod Selector,则会导致验证错误。
卷声明模板
您可以设置 .spec.volumeClaimTemplates 字段以创建 PersistentVolumeClaim。如果满足以下任一条件,这将为 StatefulSet 提供稳定的存储
- 为卷声明指定的 StorageClass 设置为使用 动态配置。
- 集群已经包含具有正确的 StorageClass 和足够的可用存储空间的 PersistentVolume。
最小就绪秒数
Kubernetes v1.25 [稳定].spec.minReadySeconds 是一个可选字段,用于指定新创建的 Pod 应该运行和就绪的最小秒数,且其任何容器都不会崩溃,才能被认为可用。这用于在使用 滚动更新 策略时检查滚动更新的进度。该字段默认为 0(Pod 准备就绪后将被认为可用)。要了解有关 Pod 何时被认为就绪的更多信息,请参阅 容器探测。
Pod 标识
StatefulSet Pod 具有唯一的标识,包括序号、稳定的网络标识和稳定的存储。无论 Pod 在哪个节点上(重新)调度,该标识都将保留在 Pod 中。
序号索引
对于具有 N 副本 的 StatefulSet,StatefulSet 中的每个 Pod 都将被分配一个唯一的整数序号。默认情况下,Pod 将被分配从 0 到 N-1 的序号。StatefulSet 控制器还会添加带有此索引的 Pod 标签:apps.kubernetes.io/pod-index。
起始序号
Kubernetes v1.31 [稳定](默认启用).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),其中管理 Service 由 StatefulSet 上的 serviceName 字段定义。
根据集群中 DNS 的配置方式,可能无法立即查找新运行 Pod 的 DNS 名称。当集群中的其他客户端已经为 Pod 的主机名发送了查询,但在 Pod 创建之前,可能会发生这种情况。负缓存(DNS 中很常见)意味着先前失败的查找结果会被记住并重用,即使 Pod 正在运行,至少持续几秒钟。
如果您需要在 Pod 创建后立即发现 Pod,则有几种选择
- 直接查询 Kubernetes API(例如,使用 watch),而不是依赖 DNS 查找。
- 减少 Kubernetes DNS 提供程序中的缓存时间(通常这意味着编辑 CoreDNS 的 config map,目前缓存时间为 30 秒)。
如 限制 部分所述,您负责创建负责 Pod 网络身份的 无头服务。
以下是一些集群域、服务名称、StatefulSet 名称的选择示例,以及这些选择如何影响 StatefulSet 的 Pod 的 DNS 名称。
| 集群域 | 服务 (ns/name) | StatefulSet (ns/name) | 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 都会收到一个具有 my-storage-class 存储类的单个 PersistentVolume 和 1 GiB 的已配置存储。 如果未指定 StorageClass,则将使用默认 StorageClass。 当 Pod 被(重新)调度到节点上时,其 volumeMounts 会挂载与 PersistentVolumeClaim 关联的 PersistentVolume。 请注意,当 Pod 或 StatefulSet 被删除时,与 Pod 的 PersistentVolumeClaim 关联的 PersistentVolume 不会被删除。 这必须手动完成。
Pod 名称标签
当 StatefulSet 控制器 创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name,该标签设置为 Pod 的名称。 此标签允许您将服务附加到 StatefulSet 中的特定 Pod。
Pod 索引标签
Kubernetes v1.32 [稳定](默认启用)当 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 之前,所有其前置 Pod 必须处于 Running 和 Ready 状态。 如果设置了
.spec.minReadySeconds,则前置 Pod 必须可用(Ready 至少minReadySeconds时间)。 - 在终止 Pod 之前,所有其后继 Pod 必须完全关闭。
StatefulSet 不应指定 pod.Spec.TerminationGracePeriodSeconds 为 0。 这种做法不安全,强烈不建议。 有关更多说明,请参阅 强制删除 StatefulSet Pod。
当创建上面的 nginx 示例时,将按 web-0、web-1、web-2 的顺序部署三个 Pod。 在 web-0 变为 Running 和 Ready 之前,不会部署 web-1,并且在 web-1 变为 Running 和 Ready 之前,不会部署 web-2。 如果 web-0 失败,在 web-1 变为 Running 和 Ready 之后,但在启动 web-2 之前,web-2 将不会启动,直到 web-0 成功重新启动并变为 Running 和 Ready。
如果用户通过修补 StatefulSet 将 replicas=1 来缩放部署的示例,web-2 将首先被终止。 在 web-2 完全关闭和删除之前,不会终止 web-1。 如果 web-0 在 web-2 已被终止并完全关闭后,但在终止 web-1 之前失败,web-1 将不会被终止,直到 web-0 变为 Running 和 Ready。
Pod 管理策略
StatefulSet 允许您通过其 .spec.podManagementPolicy 字段放宽其排序保证,同时保留其唯一性和身份保证。
有序就绪 Pod 管理
OrderedReady Pod 管理是 StatefulSet 的默认设置。 它实现了 部署和伸缩保证 中描述的行为。
并行 Pod 管理
Parallel Pod 管理告诉 StatefulSet 控制器并行启动或终止所有 Pod,并且在启动或终止另一个 Pod 之前,不等待 Pod 变为 Running 和 Ready 或完全终止。
对于伸缩操作,这意味着所有 Pod 将同时创建或终止。
对于滚动更新,当 .spec.updateStrategy.rollingUpdate.maxUnavailable 大于 1 时,StatefulSet 控制器将同时终止和创建最多 maxUnavailable 个 Pod(也称为“爆发”)。 这可以加快更新速度,但可能会导致 Pod 准备顺序混乱,这可能不适合需要严格排序的应用程序。
更新策略
StatefulSet 的 .spec.updateStrategy 字段允许您配置和禁用 StatefulSet 中 Pod 的容器、标签、资源请求/限制和注释的自动滚动更新。 有两种可能的值
OnDelete- 当 StatefulSet 的
.spec.updateStrategy.type设置为OnDelete时,StatefulSet 控制器不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod,才能导致控制器创建反映 StatefulSet 的.spec.template修改的新 Pod。 RollingUpdateRollingUpdate更新策略实现了 StatefulSet 中 Pod 的自动、滚动更新。 这是默认的更新策略。
滚动更新
当 StatefulSet 的 .spec.updateStrategy.type 设置为 RollingUpdate 时,StatefulSet 控制器将删除并重新创建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序进行(从最大的序号到最小的序号),一次更新一个 Pod。
Kubernetes 控制平面等待更新的 Pod 变为 Running 和 Ready 后,才会更新其前置 Pod。 如果您设置了 .spec.minReadySeconds(请参阅 最小就绪秒数),控制平面还会等待该时间量,Pod 变为 Ready 后,才会继续。
分区滚动更新
通过指定 .spec.updateStrategy.rollingUpdate.partition,可以对 RollingUpdate 更新策略进行分区。 如果指定了分区,则当 StatefulSet 的 .spec.template 更新时,所有序号大于或等于分区的 Pod 都将被更新。 所有序号小于分区的 Pod 将不会被更新,即使它们被删除,它们也会以之前的版本重新创建。 如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于其 .spec.replicas,则对其 .spec.template 的更新将不会传播到其 Pod。 在大多数情况下,您不需要使用分区,但它们对于分阶段更新、推出金丝雀或执行分阶段推出非常有用。
最大不可用 Pod 数
Kubernetes v1.35 [beta]您可以通过指定 .spec.updateStrategy.rollingUpdate.maxUnavailable 字段来控制更新期间可以不可用的最大 Pod 数。 该值可以是绝对数字(例如,5)或所需 Pod 的百分比(例如,10%)。 绝对数字是通过向上舍入百分比值计算得出的。 该字段不能为 0。 默认设置为 1。
此字段适用于范围 0 到 replicas - 1 中的所有 Pod。 如果范围 0 到 replicas - 1 中有任何不可用的 Pod,它将计入 maxUnavailable。
说明
maxUnavailable 字段处于 Beta 阶段,默认启用。强制回滚
在使用 滚动更新 和默认 Pod 管理策略(OrderedReady)时,可能会进入需要手动干预才能修复的损坏状态。
如果您将 Pod 模板更新为永远无法变为 Running 和 Ready 的配置(例如,由于错误的二进制文件或应用程序级别配置错误),StatefulSet 将停止推出并等待。
在这种状态下,仅仅将 Pod 模板恢复到良好的配置是不够的。 由于 已知问题,StatefulSet 将继续等待损坏的 Pod 变为 Ready(永远不会发生),然后才会尝试将其恢复到工作配置。
在恢复模板后,您还必须删除 StatefulSet 已经尝试使用错误配置运行的任何 Pod。 然后,StatefulSet 将开始使用恢复的模板重新创建 Pod。
修订历史记录
ControllerRevision 是 Kubernetes API 资源,由控制器(例如 StatefulSet 控制器)用于跟踪历史配置更改。
StatefulSet 使用 ControllerRevision 来维护修订历史记录,从而实现回滚和版本跟踪。
StatefulSets 如何使用 ControllerRevisions 跟踪变更
当您更新 StatefulSet 的 Pod 模板 (spec.template) 时,StatefulSet 控制器会
- 准备一个新的 ControllerRevision 对象
- 存储 Pod 模板和元数据的快照
- 分配一个递增的修订号
关键属性
请参阅 ControllerRevision 以了解更多关于关键属性和其他细节的信息。
管理修订历史
使用 .spec.revisionHistoryLimit 控制保留的修订版本
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: webapp
spec:
revisionHistoryLimit: 5 # Keep last 5 revisions
# ... other spec fields ...
- 默认值:如果未指定,则保留 10 个修订版本
- 清理:超出限制时,会垃圾回收最旧的修订版本
执行回滚
您可以使用以下方法回滚到之前的配置
# View revision history
kubectl rollout history statefulset/webapp
# Rollback to a specific revision
kubectl rollout undo statefulset/webapp --to-revision=3
这将
- 应用修订版本 3 中的 Pod 模板
- 创建一个具有更新的修订号的新 ControllerRevision
检查 ControllerRevisions
要查看关联的 ControllerRevisions
# List all revisions for the StatefulSet
kubectl get controllerrevisions -l app.kubernetes.io/name=webapp
# View detailed configuration of a specific revision
kubectl get controllerrevision/webapp-3 -o yaml
最佳实践
保留策略
- 对于大多数工作负载,将
revisionHistoryLimit设置为 5–10 之间。 - 仅当需要 深度回滚历史 时才增加。
监控
使用以下命令定期检查修订版本
kubectl get controllerrevisions
- 对 修订版本数量快速增长 发出警报。
避免
- 手动编辑 ControllerRevision 对象。
- 将修订版本用作备份机制(使用实际的备份工具)。
- 设置
revisionHistoryLimit: 0(禁用回滚功能)。
PersistentVolumeClaim 保留
Kubernetes v1.32 [稳定](默认启用)可选的 .spec.persistentVolumeClaimRetentionPolicy 字段控制在 StatefulSet 生命周期期间是否以及如何删除 PVC。您必须在 API 服务器和控制器管理器上启用 StatefulSetAutoDeletePVC feature gate 才能使用此字段。启用后,您可以为每个 StatefulSet 配置两种策略
whenDeleted- 配置在删除 StatefulSet 时应用的卷保留行为。
whenScaled- 配置在减少 StatefulSet 的副本数时应用的卷保留行为;例如,在缩减集时。
对于您可以配置的每种策略,您可以将值设置为 Delete 或 Retain。
删除- 来自 StatefulSet
volumeClaimTemplate创建的 PVC 会因策略影响的每个 Pod 而删除。使用whenDeleted策略,在删除其 Pod 后,所有来自volumeClaimTemplate的 PVC 都会被删除。使用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,然后由 垃圾回收器 在终止 Pod 后删除它们。这使得 Pod 能够在删除 PVC(以及取决于保留策略的后端 PV 和卷)之前干净地卸载所有卷。当您将 whenDeleted 策略设置为 Delete 时,对所有与该 StatefulSet 关联的 PVC 会放置一个指向 StatefulSet 实例的所有者引用。
whenScaled 策略必须仅在缩减 Pod 时删除 PVC,而不能在因其他原因删除 Pod 时删除。在调解时,StatefulSet 控制器将期望的副本计数与集群上存在的实际 Pod 进行比较。任何 ID 大于副本计数的 StatefulSet Pod 都会被判决并标记为删除。如果 whenScaled 策略为 Delete,则首先将判决的 Pod 设置为关联的 StatefulSet 模板 PVC 的所有者,然后删除 Pod。这会导致在仅终止判决的 Pod 后删除 PVC。
这意味着,如果控制器崩溃并重新启动,则不会在更新所有者引用之前删除任何 Pod。如果控制器关闭时强制删除了一个判决的 Pod,则所有者引用可能已设置,也可能未设置,具体取决于控制器崩溃的时间。更新所有者引用可能需要几个调解循环,因此有些判决的 Pod 可能已设置所有者引用,而另一些则可能未设置。因此,我们建议等待控制器恢复,这将会在终止 Pod 之前验证所有者引用。如果不可能,则操作员应验证 PVC 上的所有者引用,以确保在强制删除 Pod 时删除预期的对象。
副本
.spec.replicas 是一个可选字段,用于指定所需的 Pod 数量。默认值为 1。
如果您通过 kubectl scale statefulset statefulset --replicas=X 手动缩放 StatefulSet,然后基于清单更新该 StatefulSet(例如:通过运行 kubectl apply -f statefulset.yaml),则应用该清单会覆盖您之前所做的手动缩放。
如果 HorizontalPodAutoscaler(或任何类似的水平缩放 API)正在管理 Statefulset 的缩放,请不要设置 .spec.replicas。相反,允许 Kubernetes 控制平面 自动管理 .spec.replicas 字段。
接下来
- 了解 Pods。
- 了解如何使用 StatefulSets
- 按照 部署有状态应用程序 的示例。
- 按照 使用 Stateful Sets 部署 Cassandra 的示例。
- 按照 运行复制的有状态应用程序 的示例。
- 了解如何 缩放 StatefulSet。
- 了解在您 删除 StatefulSet 时会发生什么。
- 了解如何 配置 Pod 以使用卷进行存储。
- 了解如何 配置 Pod 以使用 PersistentVolume 进行存储。
StatefulSet是 Kubernetes REST API 中的顶级资源。阅读 StatefulSet 对象定义以了解有状态集合的 API。- 了解 PodDisruptionBudget 以及如何使用它来管理中断期间的应用程序可用性。