简介
StatefulSet 常用于部署有状态的且需要有序启动的应用程序。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
如部署的用用有以下需求的
- 需要稳定的独一无二的网络标识符。
- 需要持久化数据。
- 需要有序的、优雅的部署和扩展。
- 需要有序的自动滚动更新。
如果应用程序不需要任何稳定的标识符或者有序的部署、删除或者扩展,应该使用无状态的控制器部署应用程序,比如Deployment。
使用限制
- 给定 Pod 的存储必须由 PersistentVolume Provisioner 基于所请求的 storage class 来制备,或者由管理员预先制备。
- 删除或者扩缩 StatefulSet 并不会删除它关联的存储卷。 这样做是为了保证数据安全,它通常比自动清除 StatefulSet 所有相关的资源更有价值。
- StatefulSet 当前需要无头服务来负责 Pod 的网络标识。你需要负责创建此服务。
当删除一个 StatefulSet 时,该 StatefulSet 不提供任何终止 Pod 的保证。 为了实现 StatefulSet 中的 Pod 可以有序且体面地终止,可以在删除之前将 StatefulSet 缩容到 0。 - 在默认 Pod 管理策略(OrderedReady) 时使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。
创建StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
creationTimestamp: "2024-03-28T07:57:59Z"
generation: 1
name: web
namespace: apps
resourceVersion: "5178385"
uid: 3841bb6f-14cc-4189-86e4-d99555ef12c0
spec:
minReadySeconds: 10
podManagementPolicy: OrderedReady
replicas: 3 # 默认是1
revisionHistoryLimit: 10 # 默认是0
selector:
matchLabels:
app: nginx
serviceName: nginx
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: lowyard/nginx-slim:0.8
imagePullPolicy: IfNotPresent
name: nginx
ports:
- containerPort: 80
name: web
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /usr/share/nginx/html
name: www
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 10
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
name: www
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: rook-ceph-rbd
volumeMode: Filesystem
status:
phase: Pending
部署完成后:
[root@master01 ~]# kubectl get po -n apps
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 5m16s
web-1 1/1 Running 0 3m26s
web-2 1/1 Running 0 2m6s
pod名字:
对于具有 N 个副本的 StatefulSet,该 StatefulSet 中的每个 Pod 将被分配一个整数序号, 该序号在此 StatefulSet 中是唯一的。默认情况下,这些 Pod 将被赋予从 0 到 N-1 的序号,也就是 web-0 web-1 web-2。
稳定的网络 ID
StatefulSet 中的每个 Pod 根据 StatefulSet 的名称和 Pod 的序号派生出它的主机名。
组合主机名的格式为:
$(StatefulSet 名称)-$(序号) ## 也就是 web-0、web-1、web-2 的 Pod
StatefulSet 可以使用无头服务控制它的 Pod 的网络域
管理域的这个服务的格式为:
$(服务名称).$(名字空间).svc.cluster.local ## 也就是 web-0.apps.svc.cluster.local
其中 cluster.local 是集群域。 一旦每个 Pod 创建成功,就会得到一个匹配的 DNS 子域,格式为: ( p o d 名称 ) . (pod 名称). (pod名称).(所属服务的 DNS 域名),其中所属服务由 StatefulSet 的 serviceName 域来设定。
稳定的存储
对于 StatefulSet 中定义的每个 VolumeClaimTemplate,每个 Pod 接收到一个 PersistentVolumeClaim。 在上面的 nginx 示例中,每个 Pod 将会得到基于 StorageClass my-storage-class 制备的 1 GiB 的 PersistentVolume。如果没有指定 StorageClass,就会使用默认的 StorageClass。 当一个 Pod 被调度(重新调度)到节点上时,它的 volumeMounts 会挂载与其 PersistentVolumeClaims 相关联的 PersistentVolume。
请注意,当 Pod 或者 StatefulSet 被删除时,与 PersistentVolumeClaims 相关联的 PersistentVolume 并不会被删除。要删除它必须通过手动方式来完成。
部署和删除
- 对于包含 N 个 副本的 StatefulSet,当部署 Pod 时,它们是依次创建的,从0创建到N-1。
- 当删除 Pod 时,它们是逆序终止的,删除顺序是N-1一直删除到0。
- 在将扩缩操作应用到 Pod 之前,它前面的所有 Pod 必须是 Running 和 Ready 状态。
- 在一个 Pod 终止之前,所有的继任者必须完全关闭。
更新策略
StatefulSet 的 .spec.updateStrategy 字段让你可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值,OnDelete 和 RollingUpdate。
OnDelete 手动删除更新
当 StatefulSet 的 .spec.updateStrategy.type 设置为 OnDelete 时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template 的变动作出反应。
示例:
# 展示的为部分yaml文件
updateStrategy:
type: OnDelete
RollingUpdate 滚动更新
RollingUpdate 更新策略对 StatefulSet 中的 Pod 执行自动的滚动更新。这是默认的更新策略。
当 StatefulSet 的 .spec.updateStrategy.type 被设置为 RollingUpdate 时, StatefulSet 控制器会删除和重建 StatefulSet 中的每个 Pod。 它将按照与 Pod 终止相同的顺序(从最大序号到最小序号)进行,每次更新一个 Pod。
示例:
# 展示的为部分yaml文件
updateStrategy:
rollingUpdate:
partition: 0
type: RollingUpdate
查看实时更新命令
更新完StatefulSet 或者是扩容完以后可以用这个命令查看实时更新进度。
# 正常情况下还需要加上命名空间 -n “命名空间名字”
kubectl get pods -w -l app=nginx
分区滚动更新(主要用于灰度发布)
通过声明 .spec.updateStrategy.rollingUpdate.partition 的方式,RollingUpdate 更新策略可以实现分区。 如果声明了一个分区,当 StatefulSet 的 .spec.template 被更新时, 所有序号大于等于该分区序号的 Pod 都会被更新。 所有序号小于该分区序号的 Pod 都不会被更新,并且,即使它们被删除也会依据之前的版本进行重建。 如果 StatefulSet 的 .spec.updateStrategy.rollingUpdate.partition 大于它的 .spec.replicas,则对它的 .spec.template 的更新将不会传递到它的 Pod。 在大多数情况下,你不需要使用分区,但如果你希望进行阶段更新、执行金丝雀或执行分阶段上线,则这些分区会非常有用。
示例:
当partition: 2 设置为2时 更新大于等于pod名字是2以上的。
# 展示的为部分yaml文件
updateStrategy:
rollingUpdate:
partition: 2 # 默认是0 就是更新大于等于0的pod 也就是更新所有pod
type: RollingUpdate
最大不可用pod
你可以通过指定 .spec.updateStrategy.rollingUpdate.maxUnavailable 字段来控制更新期间不可用的 Pod 的最大数量。 该值可以是绝对值(例如,“5”)或者是期望 Pod 个数的百分比(例如,10%)。 绝对值是根据百分比值四舍五入计算的。 该字段不能为 0。默认设置为 1。
示例:
strategy:
rollingUpdate:
maxUnavailable: 25% # 指定在回滚或更新时最大不可用的Pod的数量,可选字段,默认25%,可以设置成数字或百分比,如果该值为0,那么maxSurge就不能为0
type: RollingUpdate # 更新deployment的方式,默认是RollingUpdate日方
更新 StatefulSet 出问题时
在默认 Pod 管理策略(OrderedReady) 下使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。
如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如, 由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。
在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。 由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。
恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样, StatefulSet 才会开始使用被还原的模板来重新创建 Pod。
扩容/缩容 StatefulSet
扩容
在一个终端窗口监视 StatefulSet 的 Pod:
# 如果你已经有一个正在运行的 wach,你可以继续使用它。
# 否则,就启动一个。
# 当 StatefulSet 有 5 个健康的 Pod 时结束此 watch
kubectl get pods --watch -l app=nginx
在另一个终端窗口使用 kubectl scale 扩展副本数为 5:
# sts 是StatefulSet的缩写
kubectl scale sts web --replicas=5
在第一个 终端中检查 kubectl get 命令的输出,等待增加的 3 个 Pod 的状态变为 Running 和 Ready。
# 这应该已经处于 Running 状态
kubectl get pod --watch -l app=nginx
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 2h
web-1 1/1 Running 0 2h
NAME READY STATUS RESTARTS AGE
web-2 0/1 Pending 0 0s
web-2 0/1 Pending 0 0s
web-2 0/1 ContainerCreating 0 0s
web-2 1/1 Running 0 19s
web-3 0/1 Pending 0 0s
web-3 0/1 Pending 0 0s
web-3 0/1 ContainerCreating 0 0s
web-3 1/1 Running 0 18s
web-4 0/1 Pending 0 0s
web-4 0/1 Pending 0 0s
web-4 0/1 ContainerCreating 0 0s
web-4 1/1 Running 0 19s
StatefulSet 控制器扩展了副本的数量。 如同创建 StatefulSet 所述,StatefulSet 按序号索引顺序创建各个 Pod,并且会等待前一个 Pod 变为 Running 和 Ready 才会启动下一个 Pod。
缩容
终端使用 kubectl patch 将 StatefulSet 缩容回三个副本:
kubectl patch sts web -p '{"spec":{"replicas":3}}'
缩容实时查看方法和扩容的一样。