【K8s教程】StatefulSet 基础
参考:https://kubernetes.io/zh/docs/tutorials/stateful-application/basic-stateful-set/
创建 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:
serviceName: "nginx"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.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" ]
resources:
requests:
storage: 1Gi
顺序创建 Pod
对于一个拥有 N 个副本的 StatefulSet,Pod 被部署时是按照 {0 …… N-1} 的序号顺序创建的。
StatefulSet 中的 Pod
StatefulSet 中的 Pod 拥有一个唯一的顺序索引和稳定的网络身份标识。
检查 Pod 的顺序索引
如同 StatefulSets 概念中所提到的, StatefulSet 中的 Pod 拥有一个具有黏性的、独一无二的身份标志。 这个标志基于 StatefulSet 控制器分配给每个 Pod 的唯一顺序索引。 Pod 的名称的形式为
使用稳定的网络身份标识
每个 Pod 都拥有一个基于其顺序索引的稳定的主机名。使用kubectl exec在每个 Pod 中执行hostname。
使用 kubectl run 运行一个提供 nslookup 命令的容器,该命令来自于 dnsutils 包。 通过对 Pod 的主机名执行 nslookup,你可以检查他们在集群内部的 DNS 地址。
headless service 的 CNAME 指向 SRV 记录(记录每个 Running 和 Ready 状态的 Pod)。 SRV 记录指向一个包含 Pod IP 地址的记录表项。
Pod 的序号、主机名、SRV 条目和记录名称没有改变,但和 Pod 相关联的 IP 地址可能发生了改变。 在本教程中使用的集群中它们就改变了。这就是为什么不要在其他应用中使用 StatefulSet 中的 Pod 的 IP 地址进行连接,这点很重要。
如果你需要查找并连接一个 StatefulSet 的活动成员,你应该查询 Headless Service 的 CNAME。 和 CNAME 相关联的 SRV 记录只会包含 StatefulSet 中处于 Running 和 Ready 状态的 Pod。
如果你的应用已经实现了用于测试 liveness 和 readiness 的连接逻辑,你可以使用 Pod 的 SRV 记录(web-0.nginx.default.svc.cluster.local, web-1.nginx.default.svc.cluster.local)。因为他们是稳定的,并且当你的 Pod 的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。
写入稳定的存储
如果 web-0 和 web-1 被重新调度了,但它们仍然继续监听各自的主机名,因为和它们的 PersistentVolumeClaim 相关联的 PersistentVolume 被重新挂载到了各自的 volumeMount 上。 不管 web-0 和 web-1 被调度到了哪个节点上,它们的 PersistentVolumes 将会被挂载到合适的挂载点上。
扩容/缩容 StatefulSet
扩容/缩容 StatefulSet 指增加或减少它的副本数。这通过更新 replicas 字段完成。 你可以使用kubectl scale 或者kubectl patch来扩容/缩容一个 StatefulSet。
扩容
kubectl get pods -w -l app=nginx
kubectl scale sts web --replicas=5
StatefulSet 控制器扩展了副本的数量。 如同创建 StatefulSet 所述,StatefulSet 按序号索引顺序的创建每个 Pod,并且会等待前一个 Pod 变为 Running 和 Ready 才会启动下一个 Pod。
缩容
kubectl get pods -w -l app=nginx
kubectl patch sts web -p '{"spec":{"replicas":3}}'
顺序终止 Pod
控制器会按照与 Pod 序号索引相反的顺序每次删除一个 Pod。在删除下一个 Pod 前会等待上一个被完全关闭。
查看 Pod 的 稳定存储,我们发现当删除 StatefulSet 的 Pod 时,挂载到 StatefulSet 的 Pod 的 PersistentVolumes 不会被删除。 当这种删除行为是由 StatefulSet 缩容引起时也是一样的。
更新 StatefulSet
Kubernetes 1.7 版本的 StatefulSet 控制器支持自动更新。 更新策略由 StatefulSet API Object 的spec.updateStrategy 字段决定。这个特性能够用来更新一个 StatefulSet 中的 Pod 的 container images,resource requests,以及 limits,labels 和 annotations。 RollingUpdate滚动更新是 StatefulSets 默认策略。
Rolling Update 策略
RollingUpdate 更新策略会更新一个 StatefulSet 中所有的 Pod,采用与序号索引相反的顺序并遵循 StatefulSet 的保证。
Patch web StatefulSet 来执行 RollingUpdate 更新策略。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate"}}}'
StatefulSet 里的 Pod 采用和序号相反的顺序更新。在更新下一个 Pod 前,StatefulSet 控制器终止每个 Pod 并等待它们变成 Running 和 Ready。 请注意,虽然在顺序后继者变成 Running 和 Ready 之前 StatefulSet 控制器不会更新下一个 Pod,但它仍然会重建任何在更新过程中发生故障的 Pod,使用的是它们当前的版本。 已经接收到更新请求的 Pod 将会被恢复为更新的版本,没有收到请求的 Pod 则会被恢复为之前的版本。 像这样,控制器尝试继续使应用保持健康并在出现间歇性故障时保持更新的一致性。
小窍门:你还可以使用 kubectl rollout status sts/
分段更新
你可以使用 RollingUpdate 更新策略的 partition 参数来分段更新一个 StatefulSet。 分段的更新将会使 StatefulSet 中的其余所有 Pod 保持当前版本的同时仅允许改变 StatefulSet 的 .spec.template。
Patch web StatefulSet 来对 updateStrategy 字段添加一个分区。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":3}}}}'
请注意,虽然更新策略是 RollingUpdate,StatefulSet 控制器还是会使用原始的容器恢复 Pod。 这是因为 Pod 的序号比 updateStrategy 指定的 partition 更小。
灰度发布
你可以通过减少 上文指定的 partition 来进行灰度发布,以此来测试你的程序的改动。
通过 patch 命令修改 StatefulSet 来减少分区。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":2}}}}'
当指定了分区时,如果更新了 StatefulSet 的 .spec.template,则所有序号大于或等于分区的 Pod 都将被更新。 如果一个序号小于分区的 Pod 被删除或者终止,它将被按照原来的配置恢复。
分阶段的发布
你可以使用类似灰度发布的方法执行一次分阶段的发布(例如一次线性的、等比的或者指数形式的发布)。 要执行一次分阶段的发布,你需要设置 partition 为希望控制器暂停更新的序号。
分区当前为2。请将分区设置为0。
kubectl patch statefulset web -p '{"spec":{"updateStrategy":{"type":"RollingUpdate","rollingUpdate":{"partition":0}}}}'
将 partition 改变为 0 以允许 StatefulSet 控制器继续更新过程。
On Delete 策略
OnDelete 更新策略实现了传统(1.7 之前)行为,它也是默认的更新策略。 当你选择这个更新策略并修改 StatefulSet 的 .spec.template 字段时,StatefulSet 控制器将不会自动的更新 Pod。
删除 StatefulSet
StatefulSet 同时支持级联和非级联删除。使用非级联方式删除 StatefulSet 时,StatefulSet 的 Pod 不会被删除。使用级联删除时,StatefulSet 和它的 Pod 都会被删除。
非级联删除
使用 kubectl delete 删除 StatefulSet。 请确保提供了 --cascade=false 参数给命令。这个参数告诉 Kubernetes 只删除 StatefulSet 而不要删除它的任何 Pod。
kubectl delete statefulset web --cascade=false
StatefulSet 永远不会删除和一个 Pod 相关联的 PersistentVolumes。
级联删除
kubectl delete statefulset web
如同你在缩容一节看到的,Pod 按照和他们序号索引相反的顺序每次终止一个。 在终止一个 Pod 前,StatefulSet 控制器会等待 Pod 后继者被完全终止。
请注意,虽然级联删除会删除 StatefulSet 和它的 Pod,但它并不会删除和 StatefulSet 关联的 Headless Service。你必须手动删除nginx Service。
即使你已经删除了 StatefulSet 和它的全部 Pod,这些 Pod 将会被重新创建并挂载它们的 PersistentVolumes。
Pod 管理策略
对于某些分布式系统来说,StatefulSet 的顺序性保证是不必要和/或者不应该的。 这些系统仅仅要求唯一性和身份标志。为了解决这个问题,在 Kubernetes 1.7 中 我们针对 StatefulSet API 对象引入了 .spec.podManagementPolicy。 此选项仅影响扩缩操作的行为。更新不受影响。
OrderedReady Pod 管理策略
OrderedReady pod 管理策略是 StatefulSets 的默认选项。它告诉 StatefulSet 控制器遵循上文展示的顺序性保证。
Parallel Pod 管理策略
Parallel pod 管理策略告诉 StatefulSet 控制器并行的终止所有 Pod, 在启动或终止另一个 Pod 前,不必等待这些 Pod 变成 Running 和 Ready 或者完全终止状态。
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:
serviceName: "nginx"
podManagementPolicy: "Parallel"
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: k8s.gcr.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" ]
resources:
requests:
storage: 1Gi
这份清单和上文完全一样,只是 web StatefulSet 的 .spec.podManagementPolicy 设置成了 Parallel。
清理现场
kubectl delete sts web
kubectl get pod -l app=nginx -w
kubectl delete svc nginx
你需要删除本教程中用到的 PersistentVolumes 的持久化存储介质。基于你的环境、存储配置和提供方式,按照必须的步骤保证回收所有的存储。