有状态服务集
StatefulSet 是用于管理有状态应用程序的工作负载 API 对象。
管理一组 Pod 的部署和扩展,并提供关于这些 Pod 的排序和唯一性的保证。
与 Deployment 相似,StatefulSet 管理基于相同容器规范的 Pod。与 Deployment 不同,StatefulSet 为其每个 Pod 保持一个粘性身份。这些 Pod 是从相同的规范创建的,但不可互换:每个 Pod 都有一个持久标识符,它在任何重新调度中都保持不变。
如果你想使用存储卷来为你的工作负载提供持久性,你可以在解决方案中使用 StatefulSet。虽然 StatefulSet 中的单个 Pod 容易出现故障,但持久 Pod 标识符使将现有卷与替换任何已故障 Pod 的新 Pod 匹配变得更容易。
使用 StatefulSet
StatefulSet 对于需要以下一项或多项的应用程序非常有价值。
- 稳定、唯一的网络标识符。
- 稳定、持久的存储。
- 有序、优雅的部署和扩展。
- 有序、自动化的滚动更新。
在以上内容中,稳定等同于在 Pod(重新)调度中持久化。如果应用程序不需要任何稳定标识符或有序部署、删除或扩展,你应该使用提供一组无状态副本的工作负载对象来部署应用程序。 Deployment 或 ReplicaSet 可能更适合你的无状态需求。
限制
- 给定 Pod 的存储必须由 持久卷供应器 (此处示例) 根据请求的存储类进行配置,或者由管理员预先配置。
- 删除和/或缩减 StatefulSet 不会删除与 StatefulSet 关联的卷。这样做是为了确保数据安全,这通常比自动清除所有相关的 StatefulSet 资源更有价值。
- StatefulSet 目前需要 无头服务 来负责 Pod 的网络身份。你需要负责创建此服务。
- StatefulSet 不提供任何关于删除 StatefulSet 时 Pod 终止的保证。为了实现 Pod 在 StatefulSet 中的有序且优雅的终止,可以在删除之前将 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
的无头服务用于控制网络域。 - 名为
web
的 StatefulSet 具有一个 Spec,该 Spec 指示将以唯一的 Pod 启动 3 个 nginx 容器副本。 volumeClaimTemplates
将使用由持久卷供应器配置的 持久卷 提供稳定存储。
StatefulSet 对象的名称必须是有效的 DNS 标签。
Pod 选择器
你必须设置 StatefulSet 的 .spec.selector
字段以匹配其 .spec.template.metadata.labels
的标签。未能指定匹配的 Pod 选择器将导致在 StatefulSet 创建期间出现验证错误。
卷声明模板
你可以设置 .spec.volumeClaimTemplates
字段以创建 持久卷声明。如果以下任一条件成立,这将为 StatefulSet 提供稳定存储
- 为卷声明指定的存储类设置为使用 动态配置,或
- 集群中已包含具有正确存储类和足够可用存储空间的持久卷。
最小就绪秒数
Kubernetes v1.25 [稳定]
.spec.minReadySeconds
是一个可选字段,指定新创建的 Pod 应该运行并处于就绪状态而没有其任何容器崩溃的最短秒数,才能将其视为可用。当使用 滚动更新 策略时,此用于检查滚出的进度。此字段默认值为 0(只要 Pod 处于就绪状态,就会将其视为可用)。要了解有关何时将 Pod 视为就绪的更多信息,请参阅 容器探测。
Pod 身份
StatefulSet Pod 具有一个唯一的身份,该身份由序号、稳定网络身份和稳定存储组成。无论 Pod 在哪个节点上(重新)调度,身份都与 Pod 保持一致。
序号索引
对于具有 N 个 副本 的 StatefulSet,StatefulSet 中的每个 Pod 都将分配一个整数序号,该序号在整个 Set 中是唯一的。默认情况下,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 可以使用 无头服务 来控制其 Pod 的域。此服务管理的域采用以下形式:$(service name).$(namespace).svc.cluster.local
,其中“cluster.local”是集群域。在创建每个 Pod 时,它都会获得一个匹配的 DNS 子域,采用以下形式:$(podname).$(governing service domain)
,其中控制服务由 StatefulSet 上的 serviceName
字段定义。
根据你的集群中 DNS 的配置方式,你可能无法立即查找新运行的 Pod 的 DNS 名称。当集群中的其他客户端在 Pod 创建之前已经发送了对 Pod 主机名的查询时,可能会出现这种情况。负缓存(DNS 中很常见)意味着即使 Pod 正在运行,以前失败的查找结果也会被记住并重新使用,至少持续几秒钟。
如果你需要在 Pod 创建后立即发现 Pod,你有一些选择
- 直接查询 Kubernetes API(例如,使用 watch)而不是依赖 DNS 查找。
- 减少你的 Kubernetes DNS 提供程序中的缓存时间(通常这意味着编辑 CoreDNS 的配置图,它目前缓存 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 都将收到一个持久卷声明。在上面的 nginx 示例中,每个 Pod 都将收到一个存储类为 my-storage-class
且配置了 1 GiB 存储的持久卷。如果没有指定存储类,则将使用默认存储类。当 Pod 被(重新)调度到节点上时,其 volumeMounts
将挂载与其持久卷声明关联的持久卷。请注意,当 Pod 或 StatefulSet 被删除时,与其 Pod 的持久卷声明关联的持久卷不会被删除。这必须手动完成。
Pod 名称标签
当 StatefulSet 控制器 创建 Pod 时,它会添加一个标签 statefulset.kubernetes.io/pod-name
,该标签设置为 Pod 的名称。此标签允许你将服务附加到 StatefulSet 中的特定 Pod。
Pod 索引标签
Kubernetes v1.28 [beta]
当 StatefulSet 控制器 创建 Pod 时,新 Pod 会被标记为 apps.kubernetes.io/pod-index
。此标签的值是 Pod 的序号索引。此标签允许您将流量路由到特定的 Pod 索引,使用 Pod 索引标签过滤日志/指标,等等。请注意,此功能需要启用功能门 PodIndexLabel
,并且默认情况下已启用。
部署和扩展保证
- 对于具有 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-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
字段来放宽其排序保证,同时保留其唯一性和标识保证。
OrderedReady Pod 管理
OrderedReady
Pod 管理是 StatefulSet 的默认值。它实现了 上面 描述的行为。
Parallel 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 状态(永远不会发生),然后才会尝试将其恢复到正常配置。
恢复模板后,您还必须删除 StatefulSet 已经尝试使用错误配置运行的任何 Pod。然后,StatefulSet 将开始使用恢复的模板重新创建 Pod。
PersistentVolumeClaim 保留策略
Kubernetes v1.27 [beta]
可选的 .spec.persistentVolumeClaimRetentionPolicy
字段控制在 StatefulSet 的生命周期中是否以及如何删除 PVC。您必须在 API 服务器和控制器管理器上启用 StatefulSetAutoDeletePVC
功能门 才能使用此字段。启用后,您可以为每个 StatefulSet 配置两种策略
whenDeleted
- 配置删除 StatefulSet 时应用的卷保留行为
whenScaled
- 配置在 StatefulSet 的副本数量减少时应用的卷保留行为;例如,在缩减集时。
对于您可以配置的每项策略,您可以将值设置为 Delete
或 Retain
。
Delete
- 从 StatefulSet 的
volumeClaimTemplate
创建的 PVC 将为受策略影响的每个 Pod 删除。使用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,然后在 Pod 被终止后由 垃圾收集器 删除。这使得 Pod 能够在 PVC 被删除(以及在备份 PV 和卷被删除之前,取决于保留策略)之前干净地卸载所有卷。当您将 whenDeleted
策略设置为 Delete
时,所有者引用将被放置到与该 StatefulSet 关联的所有 PVC 上。
whenScaled
策略必须仅在缩减 Pod 时删除 PVC,而不要在因其他原因删除 Pod 时删除。在协调时,StatefulSet 控制器会将它期望的副本数量与集群中存在的实际 Pod 进行比较。任何 ID 大于副本数量的 StatefulSet Pod 都将被判处死刑并标记为删除。如果 whenScaled
策略为 Delete
,则在删除 Pod 之前,首先将被判处死刑的 Pod 设置为与之关联的 StatefulSet 模板 PVC 的所有者。这会导致 PVC 在只有被判处死刑的 Pod 终止后才会被垃圾回收。
这意味着如果控制器崩溃并重新启动,则在所有者引用根据策略更新之前,不会删除任何 Pod。如果在控制器关闭时强制删除了被判处死刑的 Pod,则所有者引用可能已经设置,也可能没有设置,这取决于控制器何时崩溃。更新所有者引用可能需要几个协调循环,因此一些被判处死刑的 Pod 可能会设置所有者引用,而另一些则可能不会。出于这个原因,我们建议等待控制器恢复,它将在终止 Pod 之前验证所有者引用。如果不可能这样做,操作员应验证 PVC 上的所有者引用,以确保在强制删除 Pod 时删除了预期对象。
副本
.spec.replicas
是一个可选字段,它指定期望的 Pod 数量。它默认为 1。
如果您手动扩展部署,例如通过 kubectl scale statefulset statefulset --replicas=X
,然后您根据清单更新该 StatefulSet(例如:通过运行 kubectl apply -f statefulset.yaml
),那么应用该清单将覆盖您之前进行的手动扩展。
如果 水平 Pod 自动伸缩器(或任何类似的水平伸缩 API)正在管理 StatefulSet 的伸缩,请勿设置 .spec.replicas
。相反,请允许 Kubernetes 控制平面 自动管理 .spec.replicas
字段。
下一步
- 了解有关 Pod 的信息。
- 了解如何使用 StatefulSet
- 遵循 部署有状态应用程序 的示例。
- 遵循 使用 StatefulSet 部署 Cassandra 的示例。
- 遵循 运行复制的有状态应用程序 的示例。
- 了解如何 扩展 StatefulSet。
- 了解在您 删除 StatefulSet 时涉及的内容。
- 了解如何 配置 Pod 以使用卷进行存储。
- 了解如何 配置 Pod 以使用持久卷进行存储。
StatefulSet
是 Kubernetes REST API 中的顶级资源。阅读 StatefulSet 对象定义以了解有状态集的 API。- 阅读有关 PodDisruptionBudget 的信息,以及如何在中断期间使用它来管理应用程序可用性。