StatefulSet
- 概述
- 解决的问题
- 组件
- Pod 选择算符
- 卷声明模板
- 最短就绪秒数
- 更新策略
- 滚动更新
- 分区滚动更新
- 最大不可用 Pod
- 强制回滚
- 副本数
- StatefulSet的网络标识
- StatefulSet存储状态
概述
StatefulSet 是用来管理有状态应用的工作负载 API 对象。
StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符。
和 Deployment 类似, StatefulSet 管理基于相同容器规约的一组 Pod。但和 Deployment 不同的是, StatefulSet 为它们的每个 Pod 维护了一个有粘性的 ID。这些 Pod 是基于相同的规约来创建的, 但是不能相互替换:无论怎么调度,每个 Pod 都有一个永久不变的 ID。
如果希望使用存储卷为工作负载提供持久存储,可以使用 StatefulSet 作为解决方案的一部分。 尽管 StatefulSet 中的单个 Pod 仍可能出现故障, 但持久的 Pod 标识符使得将现有卷与替换已失败 Pod 的新 Pod 相匹配变得更加容易。
在运行过程中会保存数据或状态的工作负载称为“有状态工作负载(statefulset)”。例如Mysql,它需要存储产生的新数据。
因为容器可以在不同主机间迁移,所以在宿主机上并不会保存数据,这依赖于CCE提供的高可用存储卷,将存储卷挂载在容器上,从而实现有状态工作负载的数据持久化。
StatefulSet 对于需要满足以下一个或多个需求的应用程序很有价值:
- 稳定的、唯一的网络标识符。
- 稳定的、持久的存储。
- 有序的、优雅的部署和扩缩。
- 有序的、自动的滚动更新。
“稳定的”意味着 Pod 调度或重调度的整个过程是有持久性的。 如果应用程序不需要任何稳定的标识符或有序的部署、删除或扩缩, 则应该使用由一组无状态的副本控制器提供的工作负载来部署应用程序,比如 Deployment 或者 ReplicaSet 可能更适用于你的无状态应用部署需要。
解决的问题
- StatefulSet给每个Pod提供固定名称,Pod名称增加从0-N的固定后缀,Pod重新调度后Pod名称和HostName不变。
- StatefulSet通过Headless Service给每个Pod提供固定的访问域名,Service的概念会在后面章节中详细介绍。
- StatefulSet通过创建固定标识的PVC保证Pod重新调度后还是能访问到相同的持久化数据。
组件
下面的示例演示了 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 # 必须匹配 .spec.template.metadata.labels
serviceName: "nginx"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: nginx
image: registry.k8s.io/nginx-slim:0.8
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,它表明将在独立的 3 个 Pod 副本中启动 nginx 容器。 volumeClaimTemplates
将通过 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储。
StatefulSet 的命名需要遵循 DNS 子域名规范。
Pod 选择算符
你必须设置 StatefulSet 的 .spec.selector
字段,使之匹配其在
.spec.template.metadata.labels
中设置的标签。 未指定匹配的 Pod 选择算符将在创建 StatefulSet 期间导致验证错误。
卷声明模板
你可以设置 .spec.volumeClaimTemplates
, 它可以使用 PersistentVolume 制备程序所准备的 PersistentVolumes 来提供稳定的存储。
PersistentVolumes持久卷
存储的管理是一个与计算实例的管理完全不同的问题。 PersistentVolume 子系统为用户和管理员提供了一组 API, 将存储如何制备的细节从其如何被使用中抽象出来。 为了实现这点,我们引入了两个新的 API 资源:PersistentVolume 和 PersistentVolumeClaim。
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
最短就绪秒数
特性状态: Kubernetes v1.25 [stable]
.spec.minReadySeconds
是一个可选字段。 它指定新创建的 Pod 应该在没有任何容器崩溃的情况下运行并准备就绪,才能被认为是可用的。 这用于在使用滚动更新策略时检查滚动的进度。 该字段默认为 0(Pod 准备就绪后将被视为可用)。
更新策略
StatefulSet 的 .spec.updateStrategy
字段让你可以配置和禁用掉自动滚动更新 Pod 的容器、标签、资源请求或限制、以及注解。有两个允许的值:
**OnDelete
当 StatefulSet 的 .spec.updateStrategy.type
设置为 OnDelete
时, 它的控制器将不会自动更新 StatefulSet 中的 Pod。 用户必须手动删除 Pod 以便让控制器创建新的 Pod,以此来对 StatefulSet 的 .spec.template
的变动作出反应。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,这类 Pod 将被计入 maxUnavailable
值。
说明:maxUnavailable
字段处于 Alpha 阶段,仅当 API 服务器启用了
MaxUnavailableStatefulSet
特性门控时才起作用。
强制回滚
在默认 Pod 管理策略(OrderedReady
) 下使用滚动更新, 可能进入需要人工干预才能修复的损坏状态。
如果更新后 Pod 模板配置进入无法运行或就绪的状态(例如, 由于错误的二进制文件或应用程序级配置错误),StatefulSet 将停止回滚并等待。
在这种状态下,仅将 Pod 模板还原为正确的配置是不够的。 由于已知问题,StatefulSet 将继续等待损坏状态的 Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。
恢复模板后,还必须删除 StatefulSet 尝试使用错误的配置来运行的 Pod。这样, StatefulSet 才会开始使用被还原的模板来重新创建 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
字段。
StatefulSet的网络标识
StatefulSet创建后,可以看下Pod是有固定名称的,那Headless Service是如何起作用的呢,那就是使用DNS,为Pod提供固定的域名,这样Pod间就可以使用域名访问,即便Pod被重新创建而导致Pod的IP地址发生变化,这个域名也不会发生变化。
Headless Service创建后,每个Pod的IP都会有下面格式的域名。
例如上面的三个Pod的域名就是:
- nginx-0.nginx.default.svc.cluster.local
- nginx-1.nginx.default.svc.cluster.local
- nginx-2.nginx.default.svc.cluster.local
实际访问时可以省略后面的**..svc.cluster.local**。
StatefulSet存储状态
StatefulSet可以通过PVC做持久化存储,保证Pod重新调度后还是能访问到相同的持久化数据,在删除Pod时,PVC不会被删除。
StatefulSet的Pod重建过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kFAFNb6Y-1669729524856)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/9864a379-6bbe-474e-8006-5d156d320416/Untitled.png)]
下面将通过实际操作验证这一点是如何做到的,执行下面的命令,在nginx-1的目录/usr/share/nginx/html中写入一些内容,例如将index.html的内容修改为“hello world”。
kubectl exec nginx-1 -- sh -c 'echo hello world > /usr/share/nginx/html/index.html'
修改完后,如果在Pod中访问“http://localhost”,那就会返回“hello world”。
kubectl exec -it nginx-1 -- curl localhost
hello world
此时如果手动删除nginx-1这个Pod,然后再次查询Pod,可以看到StatefulSet重新创建了一个名称相同的Pod,通过创建时间4s可以看出nginx-1是刚刚创建的。
kubectl delete pod nginx-1
pod "nginx-1" deleted
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-0 1/1 Running 0 14m
nginx-1 1/1 Running 0 4s
nginx-2 1/1 Running 0 13m
再次访问该Pod的index.html页面,会发现仍然返回“hello world”,这说明这个Pod仍然是访问相同的存储。
kubectl exec -it nginx-1 -- curl localhost
hello world
------------------欢迎观看更多文章,也可关注下方公众号--------------------------
本文作者:Java技术债务
原文链接:https://cuizb.top/myblog/article/1669730284
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 3.0 CN协议进行许可。转载请署名作者且注明文章出处。