StatefulSet
StatefulSet 是用于管理有状态应用程序的工作负载 API 对象。
管理一组 Pod 的部署和伸缩,*并提供关于这些 Pod 的排序和唯一性的保证*。
与 Deployment 类似,StatefulSet 管理基于相同容器规范的 Pod。与 Deployment 不同,StatefulSet 为其每个 Pod 维护一个粘性标识。这些 Pod 是从相同的规范创建的,但不可互换:每个 Pod 都有一个持久标识符,该标识符在任何重新调度中都保持不变。
如果要使用存储卷为工作负载提供持久性,则可以将 StatefulSet 作为解决方案的一部分。尽管 StatefulSet 中的单个 Pod 容易出现故障,但持久的 Pod 标识符可以更轻松地将现有卷与替换任何已失效 Pod 的新 Pod 匹配。
使用 StatefulSets
StatefulSets 对于需要以下一项或多项的应用程序非常有价值。
- 稳定、唯一的网络标识符。
- 稳定、持久的存储。
- 有序、优雅的部署和伸缩。
- 有序、自动的滚动更新。
在以上内容中,稳定是指 Pod(重新)调度期间的持久性。如果应用程序不需要任何稳定的标识符或有序的部署、删除或伸缩,则应使用提供一组无状态副本的工作负载对象来部署应用程序。Deployment 或 ReplicaSet 可能更适合您的无状态需求。
限制
- 给定 Pod 的存储必须由 PersistentVolume Provisioner(示例请参见此处)根据请求的 *存储类* 进行配置,或者由管理员预先配置。
- 缩减和/或删除 StatefulSet *不会* 删除与 StatefulSet 关联的卷。这样做是为了确保数据安全,这通常比自动清除所有相关的 StatefulSet 资源更有价值。
- StatefulSets 目前需要一个 Headless Service 来负责 Pod 的网络标识。您负责创建此服务。
- 删除 StatefulSet 时,StatefulSets 不提供任何关于 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 选择器
您必须设置 StatefulSet 的 .spec.selector 字段以匹配其 .spec.template.metadata.labels 的标签。未指定匹配的 Pod 选择器将导致 StatefulSet 创建期间出现验证错误。
卷声明模板
您可以设置 .spec.volumeClaimTemplates 字段来创建 PersistentVolumeClaim。如果满足以下任一条件,这将为 StatefulSet 提供稳定的存储:
- 为卷声明指定的 StorageClass 设置为使用动态配置,或者
- 集群已包含具有正确 StorageClass 和足够可用存储空间的 PersistentVolume。
最小就绪秒数
.spec.minReadySeconds 是一个可选字段,用于指定新创建的 Pod 应该运行并准备就绪的最短秒数,且其容器均未崩溃,以便将其视为可用。这用于在使用滚动更新策略时检查 rollout 的进度。此字段默认为 0(Pod 将在其准备就绪后立即被视为可用)。要了解有关何时将 Pod 视为就绪的更多信息,请参阅容器探测。
Pod 标识
StatefulSet Pod 具有唯一的标识,该标识由序号、稳定的网络标识和稳定的存储组成。无论 Pod 被(重新)调度到哪个节点上,该标识都将始终与其关联。
序号索引
对于具有 N 个副本的 StatefulSet,StatefulSet 中的每个 Pod 都将被分配一个在集合中唯一的整数序号。默认情况下,Pod 将被分配从 0 到 N-1 的序号。StatefulSet 控制器还将添加一个带有此索引的 Pod 标签:apps.kubernetes.io/pod-index。
起始序号
.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 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 的配置映射,CoreDNS 目前的缓存时间为 30 秒)。
如限制部分所述,您负责创建负责 Pod 网络标识的无头服务。
以下是一些集群域名、服务名称、StatefulSet 名称以及它们如何影响 StatefulSet 的 Pod 的 DNS 名称的示例。
集群域名 | 服务 (命名空间/名称) | 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} |
注意
除非另行配置,否则集群域将设置为 `cluster.local`。稳定存储
对于 StatefulSet 中定义的每个 VolumeClaimTemplate 条目,每个 Pod 都会收到一个 PersistentVolumeClaim。在上面的 nginx 示例中,每个 Pod 都会收到一个 PersistentVolume,其 StorageClass 为 `my-storage-class`,并预配了 1 GiB 的存储空间。如果未指定 StorageClass,则将使用默认 StorageClass。当 Pod 被(重新)调度到一个节点上时,它的 `volumeMounts` 会挂载与其 PersistentVolume Claim 关联的 PersistentVolume。请注意,当 Pod 或 StatefulSet 被删除时,与 Pod 的 PersistentVolume Claim 关联的 PersistentVolume 不会被删除。这必须手动完成。
Pod 名称标签
当 StatefulSet 控制器创建一个 Pod 时,它会添加一个标签 `statefulset.kubernetes.io/pod-name`,该标签设置为 Pod 的名称。此标签允许您将服务附加到 StatefulSet 中的特定 Pod。
Pod 索引标签
Kubernetes v1.32 [稳定]
(默认启用: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 应用扩展操作之前,它的所有前驱 Pod 必须处于运行并就绪状态。
- 在终止 Pod 之前,它的所有后继 Pod 必须完全关闭。
StatefulSet 不应将 `pod.Spec.TerminationGracePeriodSeconds` 指定为 0。这种做法是不安全的,强烈建议不要这样做。有关进一步的说明,请参阅强制删除 StatefulSet Pod。
当创建上面的 nginx 示例时,将按 web-0、web-1、web-2 的顺序部署三个 Pod。在 web-0 处于运行并就绪状态之前,不会部署 web-1;在 web-1 处于运行并就绪状态之前,不会部署 web-2。如果 web-0 应该在 web-1 处于运行并就绪状态之后但在 web-2 启动之前发生故障,则在 web-0 成功重新启动并变为运行并就绪状态之前,不会启动 web-2。
如果用户通过修补 StatefulSet 将已部署的示例扩展为 `replicas=1`,则 web-2 将首先终止。在 web-2 完全关闭并删除之前,不会终止 web-1。如果 web-0 在 web-2 已终止并完全关闭之后但在 web-1 终止之前发生故障,则在 web-0 处于运行并就绪状态之前,不会终止 web-1。
Pod 管理策略
StatefulSet 允许您通过其 `spec.podManagementPolicy` 字段放松其排序保证,同时保留其唯一性和身份保证。
OrderedReady Pod 管理
`OrderedReady` pod 管理是 StatefulSet 的默认设置。它实现了上文所述的行为。
并行 Pod 管理
`Parallel` pod 管理指示 StatefulSet 控制器并行启动或终止所有 Pod,并且在启动或终止另一个 Pod 之前不等待 Pod 变为运行并就绪或完全终止。此选项仅影响扩展操作的行为。更新不受影响。
更新策略
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 处于运行并就绪状态后,再更新其前驱 Pod。如果您设置了 `.spec.minReadySeconds`(请参阅最小就绪秒数),则控制平面还会在 Pod 变为就绪状态后等待该时间量,然后再继续操作。
分区滚动更新
可以通过指定 `.spec.updateStrategy.rollingUpdate.partition` 来对 `RollingUpdate` 更新策略进行分区。如果指定了分区,则当 StatefulSet 的 `.spec.template` 更新时,序号大于或等于分区的所有 Pod 都将更新。序号小于分区的所有 Pod 将不会更新,即使它们被删除,它们也会以先前的版本重新创建。如果 StatefulSet 的 `.spec.updateStrategy.rollingUpdate.partition` 大于其 `.spec.replicas`,则对其 `.spec.template` 的更新不会传播到其 Pod。在大多数情况下,您不需要使用分区,但如果您想暂存更新、推出 Canary 版本或执行分阶段推出,则分区很有用。
最大不可用 Pod 数
Kubernetes v1.24 [alpha]
您可以通过指定 `.spec.updateStrategy.rollingUpdate.maxUnavailable` 字段来控制更新期间最大不可用 Pod 数。该值可以是绝对数字(例如,`5`)或所需 Pod 的百分比(例如,`10%`)。绝对数字是通过向上取整百分比值计算得出的。此字段不能为 0。默认设置为 1。
此字段适用于 `0` 到 `replicas - 1` 范围内的所有 Pod。如果 `0` 到 `replicas - 1` 范围内有任何不可用 Pod,它将计入 `maxUnavailable`。
注意
`maxUnavailable` 字段处于 Alpha 阶段,并且仅当 API 服务器启用了 `MaxUnavailableStatefulSet` 功能门控时才会生效。强制回滚
当使用默认Pod 管理策略(`OrderedReady`)进行滚动更新时,可能会进入需要手动干预才能修复的损坏状态。
如果您将 Pod 模板更新为永远不会变为运行并就绪的配置(例如,由于二进制文件或应用程序级配置错误),StatefulSet 将停止推出并等待。
在这种状态下,仅将 Pod 模板恢复为良好配置是不够的。由于已知问题,StatefulSet 将继续等待损坏的 Pod 变为就绪状态(这永远不会发生),然后才会尝试将其恢复为工作配置。
恢复模板后,您还必须删除 StatefulSet 已经尝试使用错误配置运行的所有 Pod。然后,StatefulSet 将开始使用恢复的模板重新创建 Pod。
PersistentVolumeClaim 保留
Kubernetes v1.32 [稳定]
(默认启用:true)可选的 `.spec.persistentVolumeClaimRetentionPolicy` 字段控制在 StatefulSet 的生命周期中是否以及如何删除 PVC。您必须在 API 服务器和控制器管理器上启用 `StatefulSetAutoDeletePVC` 功能门控才能使用此字段。启用后,您可以为每个 StatefulSet 配置两种策略:
whenDeleted
- 配置在删除 StatefulSet 时应用的卷保留行为。
whenScaled
- 配置在 StatefulSet 的副本计数减少时应用的卷保留行为;例如,缩小集合时。
对于您可以配置的每个策略,您可以将值设置为 `Delete` 或 `Retain`。
Delete
- 对于受策略影响的每个 Pod,将删除从 StatefulSet 的
volumeClaimTemplate
创建的 PVC。使用whenDeleted
策略,在 Pod 删除后,将删除volumeClaimTemplate
中的所有 PVC。使用whenScaled
策略,仅在 Pod 删除后,才会删除与缩减副本对应的 Pod 的 PVC。 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 终止后由垃圾回收器删除这些 PVC。这使得 Pod 可以在删除 PVC(以及根据保留策略删除后备 PV 和卷)之前干净地卸载所有卷。当您将 whenDeleted
策略设置为 Delete
时,所有与该 StatefulSet 关联的 PVC 上都会放置对 StatefulSet 实例的所有者引用。
whenScaled
策略必须仅在 Pod 缩减时删除 PVC,而不是在 Pod 因其他原因删除时删除。在协调时,StatefulSet 控制器会将其所需的副本计数与集群上存在的实际 Pod 进行比较。任何 ID 大于副本计数的 StatefulSet Pod 都将被 condemned 并标记为删除。如果 whenScaled
策略为 Delete
,则在删除 condemned 的 Pod 之前,会先将这些 Pod 设置为关联的 StatefulSet 模板 PVC 的所有者。这会导致仅在 condemned 的 Pod 终止后,PVC 才被垃圾回收。
这意味着,如果控制器崩溃并重新启动,则在根据策略更新其所有者引用之前,不会删除任何 Pod。如果在控制器关闭时强制删除 condemned 的 Pod,则可能已设置或未设置所有者引用,具体取决于控制器崩溃的时间。更新所有者引用可能需要几个协调循环,因此某些 condemned 的 Pod 可能已设置所有者引用,而其他 Pod 可能没有。因此,我们建议等待控制器重新启动,这将在终止 Pod 之前验证所有者引用。如果无法做到这一点,则操作员应验证 PVC 上的所有者引用,以确保在强制删除 Pod 时删除预期的对象。
副本
.spec.replicas
是一个可选字段,用于指定所需的 Pod 数量。默认为 1。
如果您手动缩放部署,例如通过 kubectl scale statefulset statefulset --replicas=X
,然后根据清单更新该 StatefulSet(例如:通过运行 kubectl apply -f statefulset.yaml
),则应用该清单会覆盖您之前进行的手动缩放。
如果HorizontalPodAutoscaler(或任何类似的水平缩放 API)正在管理 Statefulset 的缩放,请不要设置 .spec.replicas
。相反,允许 Kubernetes 控制平面自动管理 .spec.replicas
字段。
后续步骤
- 了解Pod。
- 了解如何使用 StatefulSet
- 按照示例部署有状态应用程序。
- 按照示例使用 StatefulSet 部署 Cassandra。
- 按照示例运行复制的有状态应用程序。
- 了解如何缩放 StatefulSet。
- 了解删除 StatefulSet时涉及的内容。
- 了解如何配置 Pod 以使用卷进行存储。
- 了解如何配置 Pod 以使用 PersistentVolume 进行存储。
StatefulSet
是 Kubernetes REST API 中的顶级资源。阅读StatefulSet 对象定义以了解有状态集的 API。- 阅读有关 PodDisruptionBudget 以及如何使用它来管理中断期间的应用程序可用性的信息。