StatefulSet 控制器

1. StatefulSet 概述

StatefulSet 用于管理有状态的 Pod 副本集,它与 Deployment 管理的无状态 Pod 副本集的主要区别在于:“有状态” 意味着每个 Pod 副本都应该具有唯一不变的身份标识,例如 ID 或者服务名称;多个 Pod 副本不是对等无差别的,而是相互之间可能需要通信来实现某种功能(例如组成应用集群、Leader选举);每个Pod 需要独立的持久化存储(保存日志或数据);以及多个 Pod 可能需要按固定的顺序启动等。

StatefulSet 会为有状态 Pod 副本集提供以下功能:

  • 每个 Pod 都具有唯一且不变的身份标识,包括 ID 和网络访问地址。
  • 为每个Pod 都配置稳定的持久化存储。
  • 对多个Pod提供有序的、优雅的部署和扩缩容等管理功能。
  • 对多个Pod提供有序的、优雅的滚动更新等管理功能。

StatefulSet 的特性:

  • 为每个 Pod 配置的持久化存储必须是 PVC 类型的共享存储。
  • 删除 Pod 时不会删除关联的后端存储,主要考虑的是容器应用的数据通常都很有业务价值,需要保留。如果需要删除这些数据,只能通过手动方式进行删除。
  • 必须创建一个 Headless Service(无头服务),用于创建每个 Pod 的网络访问地址。
  • 删除 StatefulSet 资源时,系统不保证 Pod 终止。为了实现优雅的终止,可以先将 Pod 副本数量缩容为 0,再删除 StatefulSet 资源。
  • 在使用 OrderedReady 策略执行滚动更新时,可能存在某个 Pod 损坏,永远不能处于 Ready 状态的情况,这时需要人工干预才能修复损坏的 Pod。

2. StatefulSet 配置示例

# nginx-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  serviceName: "nginx"  # Headless Service 的名称
  replicas: 3
  minReadySeconds: 10
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: nginx:1.26.2
        ports:
        - name: web
          containerPort: 80
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:    # PVC 模版
  - metadata:
      name: www
    spec:
      storageClassName: "nfs-client"
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

---
apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  selector:
    app: nginx
  ports:
  - port: 80
    name: web
  clusterIP: None    # Headless Service
YAML

StatefulSet 资源配置字段说明:

  • serviceName:关联的 Headless Service 名称。
  • updateStrategy:更新策略,可选项包括 OnDelete 和 RolingUpdate,默认值为 RollingUpdate。
  • podManagementPolicy:Pod 管理策略,可选项包括 OrderedReady 和 Parallel,默认值为 OrderedReady。OrderedReady 表示按顺序管理 Pod 的创建和删除,Parallel 表示可以并行创建或删除所有 Pod,该选项只影响扩缩容操作,不会影响更新操作。
  • volumeClaimTemplates:后端存储的 PVC 模板。
  • persistentVolumeClaimRetentionPolicy:PVC 存储的保留策略,详见下面小节。
  • ordinals.start:Pod 名称的起始序号。

3. StatefulSet 工作机制

3.1. StatefulSet 创建 Pod 副本的过程

上述部署示例:

# 需要先部署 nfs storageClass
kubectl apply -f nginx-statefulset.yaml
ShellScript

查看部署过程:

kubectl get pod -w
ShellScript

可以看到 StatefulSet 为每个 Pod 都设置了从0开始的序号,然后按顺序逐个创建了3个Pod 副本,并且只有在一个 Pod 处于可用状态时才开始创建下一个 Pod。

3.2. StatefulSet 为 Pod 设置后端存储

由于在 volumeClaimTemplates 模板中指定了storageClassName=nfs-client,如果集群中这个 StorageClass 正常工作,并且能够根据 PVC 的要求自动完成后端 PV 的创建和与 PVC 的绑定,通过 kubectl get pvc 命令可以看到系统自动创建的 PVC 信息。

如果某个 Pod 发生故障或者 Pod 所在 Node发生故障,StatefulSet 会重新创建一个同名的 Pod,并且把之前与之关联的 PVC 再挂载给新的 Pod,保证每个 Pod 使用的存储都是稳定不变的。

3.3. StatefulSet 的删除机制

StatefulSet 支持以级联模式删除全部 Pod 或者以非级联模式保留 Pod,默认以级联模式删除全部 Pod。这可以通过 kubectl delete 命令的 –cascadc 参数指定是否使用级联模式。

通过 kubectl delete 命令删除 StatefulSet,系统会并发删除全部 Pod,不会像创建或扩缩容那样,需要等待前一个 Pod 删除完成后,再开始删除下一个 Pod。

如果指定以非级联模式删除 StatefulSet (设置 –cascade=orphan=true),则可以看到原先的 Pod 不受影响,继续运行。

# --cascade=background  在后台级联删除资源,不等待子资源删除完成。
# --cascade=orphan  级联删除资源及其子资源,但不删除子资源的控制器。
# --cascade=foreground  前台级联删除资源及其所有子资源,等待子资源删除完成。

kubectl delete statefulset web --cascade=orphan

kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
pod/web-0                       1/1     Running   0          159m
pod/web-1                       1/1     Running   0          159m
pod/web-2                       1/1     Running   0          158m
ShellScript

如果以非级联模式删除 StatefulSet 之后又重新创建了该 StatefulSet ,但是副本数量不同了,则系统将保留正在运行的序号在副本数量范围内的 Pod,同时删除其他序号的 Pod。

# 修改 nginx-statefulset.yaml 副本数为 2
...
replicas: 2
...


# 重新创建 statefulset
kubectl apply -f nginx-statefulset.yaml

kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
web-0                       1/1     Running   0          165m
web-1                       1/1     Running   0          165m
# 可以看到还剩下两个副本
ShellScript

4. StatefulSet 的 Pod 水平扩缩

StatefulSet 扩缩容过程:

  • 扩容:与创建的过程一致,即先创建一个Pod,等待其处于可用状态后,再创建下一个Pod。
  • 缩容:缩容的过程则是按反向顺序有序删除Pod,即先删除序号最大的Pod,等待该Pod完全终止后,才开始删除下一个Pod。

扩缩容命令:

# 修改预期副本数
kubectl scale statefulset web --replicas=5
ShellScript

5. StatefulSet 的更新策略

statefulset 的更新策略(updateStrategy)包括两种:OnDelete 和 RollingUpdate 默认值为 RollingUpdate。如果未设置 updateStrategy 字段,则系统默认使用 RollingUpdate 策略。

  • OnDelete:当 updateStrategy 的值被设置为 OnDelete 时,StatefulSet Controller 并不会自动更新 StatefulSet 中的 Pod 实例,而是需要用户手动删除这些 Pod 实例并触发 StatefulSet Controller 创建新的 Pod 实例来弥补。
  • RollingUpdate:当 updateStrategy 的值被设置为 RollingUpdate 时,StatefulSet Controller 会删除并创建与 StatefulSet 相关的每个 Pod 对象,其处理顺序与 StatefulSet 缩容 Pod 的顺序一致,即从序号最大的 Pod 开始重新创建,每次更新一个 Pod。

滚动更新策略 RollingUpdate 还支持按分区进行更新的方式。通过 spec.updateStrategy.rollingUpdate.partition 字段指定一个序号(代表分区的含义),大于或等于此序号的 Pod 实例会被更新,小于此序号的 Pod 实例则保持不变,即使这些 Pod 被删除、重建,也仍能保持原来的配置。

示例:

# 设置 partition=2 来表示下次更新时仅更新 Pod 序号大于或等于 2 的副本
updateStrategy: 
  rollingUpdate: 
    partition: 2
  type: RollingUpdate
YAML

StatefulSet 更新的方式同 Deployment,详见 Deployment 的更新机制

6. StatefulSet 的 Pod 管理策略

StatefulSet 默认按一定的顺序启动或者扩缩容多个Pod,但是对于某些有状态应用来说,并没有这种按顺序操作的要求,通常它们只需要保证具有唯一的身份标识和网络访问地址就可以。k8s 引入了 spec.podManagementPolicy 字段,用于设置 Pod 的管理策略。可以设置的策略包括 OrderedReady (按顺序的)和 Parallel(并行的)两种,默认值为 OrderedReady。该策略只会影响 Pod 副本集的创建过程和扩编容操作,而不会影响更新操作。

  • OrderedReady:表示按顺序管理 Pod 的创建和扩缩容操作。
  • Parallel:表示可以并行创建或删除所有 Pod。

示例:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx
  ...
  podManagementPolicy: "Parallel"
  ...
YAML