k8s–基础–21–Statefulset
1、概念
StatefulSet是为了解决有状态服务的问题而设计,对应Deployments和ReplicaSets是为无状态服务。
1.1、应用场景
- 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据
- 基于PVC来实现
- 稳定的网络标志,即Pod重新调度后其PodName和HostName不变
- 基于Headless Service(即没有Cluster IP的Service)来实现
- 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1,在下一个Pod运行之前的所有Pod必须都是Running和Ready状态)
- 基于init containers来实现
- 有序收缩,有序删除(即从N-1到0)
1.2、StatefulSet组成
- 通过Headless Service生成可解析的DNS记录
- 通过volumeClaimTemplates创建pvc和对应的pv绑定
- 定义StatefulSet来创建pod
2、StatefulSet和Deployment区别
2.1、相同点
- StatefulSet和Deployment 都管理了基于相同容器定义的一组Pod。
- StatefulSet和Deployment 都使用相同的工作模式
2.2、不同点
- StatefulSet 为它们的每个 Pod 维护了一个固定的ID。
- 这些 Pod 是基于相同的声明来创建的,但是不能相互替换
- 无论怎么调度,每个 Pod 都有一个永久不变的ID。
- Deployment 没有为它们的每个 Pod 维护一个固定的ID。
- 这些 Pod 能相互替换
- 每个 Pod 的ID不是固定
- 在deployment中创建的存储卷是一个共享的存储卷,不能适用于有状态应用。
- 多个pod使用同一个存储卷
- 多个pod存储数据是同步的
- 在statefulset中创建的存储卷不是一个共享的存储卷,适用于有状态应用。
- 每个pod都不会出现共享存储卷
- 每个pod的存储卷都不一样
3、StatefulSet中每个Pod的DNS格式
statefulSetName-{0..N-1}.serviceName.namespace.svc.cluster.local
- serviceName:Headless Service的名字
- 0…N-1:Pod所在的序号,从0开始到N-1
- statefulSetName:StatefulSet的名字
- namespace:服务所在的namespace,Headless Servic和StatefulSet必须在相同的namespace
- svc.cluster.local:Cluster Domain
4、部署
4.1、涉及的命令
kubectl apply -f /root/test3/nginx-service.yaml
kubectl delete -f /root/test3/nginx-service.yaml
kubectl apply -f /root/test3/statefulset-nginx.yaml
kubectl delete -f /root/test3/statefulset-nginx.yaml
kubectl get svc nginx-service
kubectl describe svc nginx-service
kubectl get pods -w -l la_nginx=nginx
4.2、创建Service
vi /root/test3/nginx-service.yaml
内容
apiVersion: v1
kind: Service
metadata:
name: nginx-service
labels:
la_nginx-service: nginx
spec:
ports:
- port: 80
name: nginx-port
clusterIP: None
selector:
la-nginx: nginx
4.2、创建pod
vi /root/test3/statefulset-nginx.yaml
内容
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: statefulset-nginx
spec:
selector:
matchLabels:
la_nginx: nginx
serviceName: "nginx"
replicas: 2
template:
metadata:
labels:
la_nginx: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
name: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Mi
4.3、为什么要使用volumeClaimTemplate
- 要适用有状态应用,需要保证statefulset定义中的每一个pod都不能使用同一个存储卷。
- volumeClainTemplate可以满足上面的要求
- 当在使用statefulset创建pod时,会自动生成一个PVC,从而请求绑定一个PV,这样每一个pod都有自己专用的存储卷。
4.3.1、Pod、PVC和PV对应的关系图
4.3.2、使用volumeClaimTemplate的前提
需要创建pv和pvc。
4.4、查看
4.4.1、 查看pod
[root@master1 test3]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
statefulset-nginx-0 1/1 Running 0 64m controller-revision-hash=statefulset-nginx-59bd4967d6,la_nginx=nginx,statefulset.kubernetes.io/pod-name=statefulset-nginx-0
statefulset-nginx-1 1/1 Running 0 64m controller-revision-hash=statefulset-nginx-59bd4967d6,la_nginx=nginx,statefulset.kubernetes.io/pod-name=statefulset-nginx-1
kubectl get pods -w -l la_nginx=nginx
4.4.2、 查看service
kubectl get svc nginx-service
4.4.3、查看statefulset
kubectl get statefulset statefulset-nginx
4.4.4、查看PersistentVolumeClaims
kubectl get pvc -l la_nginx=nginx
StatefulSet控制器 自动创建了2个PersistentVolumeClaims,自动绑定到2个PersistentVolumes。
5、顺序创建 Pod
- 对于拥有N个副本的 StatefulSet,Pod被部署时是按照 {0…N-1}的序号顺序创建的。
- 注意:在nginx-0 Pod处于Running和Ready状态后,nginx-1 Pod才会被启动。
6、StatefulSet中的Pod
Pod拥有一个唯一的顺序索引和稳定的网络身份标识
6.1、顺序索引
- StatefulSet控制器分配给每个Pod的唯一顺序索引,Pod的名称的形式如下
- - 举例:上面StatefulSet拥有两个副本,所以它创建了两个Pod:nginx-0和nginx-1
6.2、稳定的网络身份标识
- 每个Pod都拥有一个基于其顺序索引的稳定的主机名
- 主机名:由statefulset的名称和有序索引组成
6.2.1、查看主机名
# 获取pod的主机名
kubectl exec statefulset-nginx-0 -- sh -c 'hostname';
kubectl exec statefulset-nginx-1 -- sh -c 'hostname';
6.2.2、查看集群内部的DNS地址(域名地址|SRV记录)
# 查看集群内部的DNS地址
kubectl exec statefulset-nginx-0 -- hostname -f
kubectl exec statefulset-nginx-1 -- hostname -f
6.2.3、验证网络身份标识是稳定的
6.2.3.1、在一个终端中查看 StatefulSet的Pod。
kubectl get pods -w -l la_nginx=nginx
6.2.3.2、在另一个终端中删除StatefulSet中所有的Pod。
kubectl delete Pod -l la_nginx=nginx
6.2.3.3、等待StatefulSet重启它们,并且两个Pod都变成Running,Ready状态
6.2.3.4、查看集群内部的DNS地址
- Pod的序号、主机名、SRV条目和记录名称没有改变,但和Pod相关联的IP地址可能发生了改变,所以不要在其他应用中使用 StatefulSet中的Pod的IP 地址进行连接
- 通过查询Headless Service 的CNAME来连接一个StatefulSet的活动成员
- CNAME相关联的 SRV记录 只会包含StatefulSet中处于Running和Ready状态的Pod。
- 如果你的应用已经实现了用于测试liveness和readiness的连接逻辑,你可以使用Pod的SRV记录,因为他们是稳定的,并且当你的Pod的状态变为 Running 和 Ready 时,你的应用就能够发现它们的地址。以下是上面pod的SRV记录
- statefulset-nginx-0.nginx.default.svc.cluster.local
- statefulset-nginx-1.nginx.default.svc.cluster.local
7、部署和扩缩容
- 对于包含N个副本的StatefulSet,当部署Pod时,它们是依次创建的,顺序为 0…N-1。
- 当删除Pod 时,它们是逆序终止的,顺序为 N-1…0。
- 在将缩放操作应用到Pod之前,它前面的所有Pod必须是Running和Ready状态。
- 在Pod终止之前,所有的继任者必须完全关闭。
7.1、注意
- StatefulSet不应将pod.Spec.TerminationGracePeriodSeconds设置为0。
- 这种做法是不安全的,要强烈阻止。
- 更多的解释请参考
https://kubernetes.io/docs/tasks/run-application/force-delete-stateful-set-pod/
7.2、案例说明
假设部署POD数量为3,对应的名称如下
statefulset-nginx-0
statefulset-nginx-1
statefulset-nginx-2
7.2.1、顺序部署pod
- statefulset-nginx被创建后,会按照statefulset-nginx-0、statefulset-nginx-1、statefulset-nginx-2的顺序部署Pod。
- 在 statefulset-nginx-0 进入 Running 和 Ready 状态前不会部署 statefulset-nginx-1。
- 在 statefulset-nginx-1 进入 Running 和 Ready 状态前不会部署 statefulset-nginx-2。
- 如果 statefulset-nginx-1 已经处于 Running 和 Ready 状态,而 statefulset-nginx-2 尚未部署,在此期间发生了 statefulset-nginx-0 运行失败,那么 statefulset-nginx-2 将不会被部署,要等到 statefulset-nginx-0 部署完成并进入 Running 和 Ready 状态后,才会部署 statefulset-nginx-2。
7.2.2、收缩Pod–>将 statefulset-nginx 收缩为 replicas=1
- 首先被终止的是 statefulset-nginx-2。
- 在 statefulset-nginx-2 没有被完全停止和删除前,statefulset-nginx-1 不会被终止。
- 当 statefulset-nginx-2 已被终止和删除、statefulset-nginx-1 尚未被终止,如果在此期间发生 statefulset-nginx-0 运行失败,那么就不会终止 statefulset-nginx-1,必须等到 statefulset-nginx-0 进入 Running 和 Ready 状态后才会终止 statefulset-nginx-1。
8、Statefulset 重点内容
8.1、Pod selector
必须设置StatefulSet的.spec.selector字段,使之匹配其在.spec.template.metadata.labels中设置的标签。
8.2、Pod Identity(pod标识)
- StatefulSet Pod 具有唯一的标识,该标识包括顺序标识、稳定的网络标识和稳定的存储。2. 该标识和Pod是绑定的,不管它被调度在哪个节点上。
8.3、有序索引
- 对于具有N个副本的StatefulSet,StatefulSet中的每个Pod 将被分配一个整数序号,从 0 到 N-1
- 该序号在StatefulSet上是唯一的。
8.4、稳定的网络ID
8.4.1、主机名,网络域,DNS子域
- StatefulSet中的每个Pod根据StatefulSet的名称和Pod的序号派生出它的主机名。
2. 组合主机名格式:(序号)。 - StatefulSet可以使用headless服务 控制它的Pod的网络域。
- 格式为: (命名空间).svc.cluster.local
- cluster.local:集群域。
- 一旦每个Pod创建成功,就会得到一个匹配的DNS子域
- 格式为:(所属服务的 DNS 域名)
- 所属服务由 StatefulSet的serviceName域来设定。
- 案例:下面给出一些选择集群域、服务名、StatefulSet名、及其怎样影响StatefulSet的Pod上的DNS名称的示例
- 注意: 集群域会被设置为 cluster.local,除非有其他配置。
8.5、标签
- 当StatefulSet控制器创建 Pod 时,它会添加一个标签statefulset.kubernetes.io/pod-name,该标签设置为Pod名称。
- 这个标签允许您给 StatefulSet 中的特定 Pod 绑定一个 Service。
9、扩展
9.1、Pod 管理策略
- StatefulSet允许您放宽pod的排序保证
- 通过.spec.podManagementPolicy,保证pod的唯一性和身份保证。
9.2、OrderedReadyPod 管理
- 是 StatefulSet的默认设置。
- 实现了9.1的功能。
9.3、ParallelPod 管理
让StatefulSet控制器并行的启动或终止所有的Pod,启动或者终止其他Pod前,无需等待Pod进入Running和ready或者完全停止状态。
9.4、更新策略
通过.spec.updateStrategy让你可以配置和禁用掉自动滚动更新Pod的容器、标签、资源请求或限制、以及注解。
9.5、删除策略
- .spec.updateStrategy.type=OnDelete
- 它的控制器将不会自动更新 StatefulSet中的Pod。用户必须手动删除Pod以便让控制器创建新的Pod,以此来对StatefulSet的 .spec.template 的变动作出反应。
9.6、滚动更新
- 字段:.spec.updateStrategy
- 值:RollingUpdate(默认值)
- 对 StatefulSet中的Pod 执行自动的滚动更新。
- StatefulSet控制器会删除和重建 StatefulSet中的每个Pod
- 它将从Pod的最大序号到最小序号进行
- 每次更新一个Pod。它会等到被更新的Pod进入Running和Ready 状态,然后再更新其前身。
9.7、分区
- 字段:.spec.updateStrategy.rollingUpdate.partition
- RollingUpdate 更新策略可以实现分区。
- 如果声明了一个分区,当 StatefulSet的 .spec.template 被更新时
- 所有序号大于等于该分区序号的Pod 都会被更新
- 所有序号小于该分区序号的Pod 都不会被更新
- 即使他们被删除也会依据之前的版本进行重建。
- 如果.spec.updateStrategy.rollingUpdate.partition 大于.spec.replicas,对它的 .spec.template 的更新将不会传递到它的Pod。
- 在大多数情况下,您不需要使用分区,但如果您希望进行阶段更新、执行金丝雀或执行分阶段展开,则这些分区会非常有用。
9.8、强制回滚
- Pod 管理策略(OrderedReady)默认使用 滚动更新 ,可能进入需要人工干预才能修复的损坏状态。
- 如果更新后Pod 模板配置进入无法运行或就绪的状态(例如,由于错误的二进制文件或应用程序级配置错误),StatefulSet将停止回滚并等待。
- 在这种状态下,仅将Pod 模板还原为正确的配置是不够的。由于已知问题,StatefulSet将继续等待损坏状态的Pod 准备就绪(永远不会发生),然后再尝试将其恢复为正常工作配置。
- 恢复模板后,还必须删除StatefulSet尝试使用错误的配置来运行的Pod。这样,StatefulSet才会开始使用被还原的模板来重新创建Pod。