StatefulSet

StatefulSet 运行一组 Pod,并为每个 Pod 维护一个粘滞标识。这对于管理需要持久存储或稳定、唯一网络标识的应用程序非常有用。

StatefulSet 是用于管理有状态应用程序的工作负载 API 对象。

管理一组 Pod 的部署和扩展,并提供有关这些 Pod 的排序和唯一性的保证

Deployment 类似,StatefulSet 管理基于相同容器规范的 Pod。与 Deployment 不同,StatefulSet 为其每个 Pod 维护一个粘滞标识。这些 Pod 是从相同的规范创建的,但不可互换:每个 Pod 都有一个持久标识符,该标识符在任何重新调度中都保持不变。

如果要使用存储卷为工作负载提供持久性,则可以将 StatefulSet 用作解决方案的一部分。尽管 StatefulSet 中的单个 Pod 容易出现故障,但持久 Pod 标识符可以更轻松地将现有卷与替换任何已失败 Pod 的新 Pod 相匹配。

使用 StatefulSet

StatefulSet 对于需要以下一项或多项功能的应用程序非常有用。

  • 稳定、唯一的网络标识符。
  • 稳定、持久的存储。
  • 有序、优雅的部署和扩展。
  • 有序、自动的滚动更新。

在上述内容中,稳定是指在 Pod(重新)调度中保持持久性。如果应用程序不需要任何稳定的标识符或有序的部署、删除或扩展,则应使用提供一组无状态副本的工作负载对象来部署应用程序。DeploymentReplicaSet 可能更适合您的无状态需求。

限制

  • 给定 Pod 的存储必须由 PersistentVolume Provisioner示例)根据请求的*存储类*进行配置,或者由管理员预先配置。
  • 删除和/或缩减 StatefulSet *不会*删除与 StatefulSet 关联的卷。这样做是为了确保数据安全,这通常比自动清除所有相关的 StatefulSet 资源更有价值。
  • StatefulSet 当前需要 Headless Service 来负责 Pod 的网络标识。您负责创建此服务。
  • 当 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

在上面的例子中

  • 名为 nginx 的 Headless Service 用于控制网络域。
  • 名为 web 的 StatefulSet 的 Spec 指示将在唯一的 Pod 中启动 3 个 nginx 容器副本。
  • volumeClaimTemplates 将使用 PersistentVolume Provisioner 配置的 PersistentVolume 提供稳定的存储。

StatefulSet 对象的名称必须是有效的 DNS 标签

Pod 选择器

您必须设置 StatefulSet 的 .spec.selector 字段以匹配其 .spec.template.metadata.labels 的标签。如果未指定匹配的 Pod 选择器,则在 StatefulSet 创建期间会导致验证错误。

卷声明模板

您可以设置 .spec.volumeClaimTemplates 字段来创建 PersistentVolumeClaim。如果满足以下任一条件,这将为 StatefulSet 提供稳定的存储:

  • 为卷声明指定的 StorageClass 设置为使用 动态配置,或者
  • 集群已包含具有正确 StorageClass 和足够可用存储空间的 PersistentVolume。

最小就绪秒数

特性状态: Kubernetes v1.25 [稳定]

.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.27 [测试版]

.spec.ordinals 是一个可选字段,允许您配置分配给每个 Pod 的整数序号。它默认为 nil。您必须启用 StatefulSetStartOrdinal 特性门控 才能使用此字段。启用后,您可以配置以下选项

  • .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 网络标识的 Headless Service

以下是一些关于集群域、服务名称、StatefulSet 名称以及这如何影响 StatefulSet 的 Pod 的 DNS 名称的选择示例。

集群域服务(命名空间/名称)StatefulSet(命名空间/名称)StatefulSet 域Pod DNSPod 主机名
cluster.localdefault/nginxdefault/webnginx.default.svc.cluster.localweb-{0..N-1}.nginx.default.svc.cluster.localweb-{0..N-1}
cluster.localfoo/nginxfoo/webnginx.foo.svc.cluster.localweb-{0..N-1}.nginx.foo.svc.cluster.localweb-{0..N-1}
kube.localfoo/nginxfoo/webnginx.foo.svc.kube.localweb-{0..N-1}.nginx.foo.svc.kube.localweb-{0..N-1}

稳定的存储

对于 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.28 [测试版]

当 StatefulSet 控制器 创建 Pod 时,新 Pod 会被标记为 apps.kubernetes.io/pod-index。此标签的值是 Pod 的序号索引。此标签允许您将流量路由到特定的 Pod 索引、使用 Pod 索引标签过滤日志/指标等。请注意,必须启用功能门控 PodIndexLabel 才能使用此功能,默认情况下启用此功能。

部署和扩展保证

  • 对于具有 N 个副本的 StatefulSet,在部署 Pod 时,将按 {0..N-1} 的顺序依次创建它们。
  • 删除 Pod 时,将按 {N-1..0} 的相反顺序终止它们。
  • 在对 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 处于正在运行且就绪状态。如果您设置了 .spec.minReadySeconds(请参阅最短就绪秒数),则控制平面还会在 Pod 变为就绪状态后等待该时间,然后再继续。

分区滚动更新

可以通过指定 .spec.updateStrategy.rollingUpdate.partitionRollingUpdate 更新策略进行分区。如果指定了分区,则当 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。

此字段适用于 0replicas - 1 范围内的所有 Pod。如果 0replicas - 1 范围内有任何不可用的 Pod,则会将其计入 maxUnavailable

强制回滚

当将滚动更新与默认的Pod 管理策略OrderedReady)一起使用时,可能会进入需要手动干预才能修复的损坏状态。

如果您将 Pod 模板更新为永远不会变为正在运行且就绪的配置(例如,由于二进制文件或应用程序级配置错误),StatefulSet 将停止推出并等待。

在这种状态下,仅将 Pod 模板恢复为良好的配置是不够的。由于已知问题,StatefulSet 将继续等待损坏的 Pod 变为就绪状态(这永远不会发生),然后才会尝试将其恢复为工作配置。

恢复模板后,您还必须删除 StatefulSet 已经尝试使用错误配置运行的所有 Pod。然后,StatefulSet 将开始使用恢复的模板重新创建 Pod。

PersistentVolumeClaim 保留

特性状态: Kubernetes v1.27 [测试版]

可选的 .spec.persistentVolumeClaimRetentionPolicy 字段控制在 StatefulSet 的生命周期中是否以及如何删除 PVC。您必须在 API 服务器和控制器管理器上启用 StatefulSetAutoDeletePVC 功能门控 才能使用此字段。启用后,您可以为每个 StatefulSet 配置两种策略

whenDeleted
配置在删除 StatefulSet 时应用的卷保留行为
whenScaled
配置在减少 StatefulSet 的副本计数时应用的卷保留行为;例如,缩减集合时。

对于您可以配置的每种策略,您可以将值设置为 DeleteRetain

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 终止后由垃圾收集器删除。这使得 Pod 能够在删除 PVC(以及删除后备 PV 和卷,具体取决于保留策略)之前干净地卸载所有卷。当您将 whenDeleted 策略设置为 Delete 时,所有者引用将放置在与该 StatefulSet 关联的所有 PVC 上的 StatefulSet 实例上。

whenScaled 策略必须仅在缩减 Pod 时删除 PVC,而不是在出于其他原因删除 Pod 时删除。协调时,StatefulSet 控制器会将其所需的副本计数与集群上存在的实际 Pod 进行比较。任何 ID 大于副本计数的 StatefulSet Pod 都将被谴责并标记为删除。如果 whenScaled 策略为 Delete,则在删除 Pod 之前,首先将谴责的 Pod 设置为关联的 StatefulSet 模板 PVC 的所有者。这会导致仅在谴责的 Pod 终止后才对 PVC 进行垃圾收集。

这意味着,如果控制器崩溃并重新启动,则在根据策略更新其所有者引用之前,不会删除任何 Pod。如果在控制器关闭时强制删除谴责的 Pod,则可能已设置也可能未设置所有者引用,具体取决于控制器崩溃的时间。更新所有者引用可能需要几个协调循环,因此某些谴责的 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 字段。

下一步

上次修改时间:2024 年 6 月 4 日上午 11:10(太平洋标准时间):将 registry.k8s.io/nginx-slim 版本从 0.8 修改为 0.24 (e9c7b069d5)