Kubernetes 一文详解StatefulSet 控制器

网友投稿 752 2022-09-27

Kubernetes 一文详解StatefulSet 控制器

Kubernetes 一文详解StatefulSet 控制器

无状态与有状态

Deployment控制器设计原则:管理的所有Pod一模一样,提供同一个服务,也不考虑在哪台Node运行,可随意扩容和缩容。这种应用称为“无状态”,例如Web服务

在实际的场景中,并不能满足所有应用,尤其是分布式应用,会部署多个实例,这些实例之间往往有依赖关系,例如主从关系、主备关系,这种应用称为“有状态”,例如MySQL主从、Etcd集群

StatefulSet 控制器概述

StatefulSet控制器用于部署有状态应用,满足一些有状态应

用的需求:

• Pod有序的部署、扩容、删除和停止

• Pod分配一个稳定的且唯一的网络标识

• Pod分配一个独享的存储

Deployment数据卷是共享的,当创建3个pod都是用的同一个数据卷,对外提供统一的服务。

StatefulSet 控制器:主机名

稳定的网络标识:使用Headless Service(相比普通Service只是将spec.clusterIP定义为None)来维护Pod网络身份,会为每个Pod分配一个数字编号并且按照编号顺序部署。还需要在StatefulSet添加serviceName: “nginx”字段指定StatefulSet控制器要使用这个Headless Service。

稳定主要体现在主机名和Pod A记录:

• 主机名:-<编号>

• Pod DNS A记录:. ..svc.cluster.local

之前的service会自动的帮你填充cluster ip,这里将字段设置为none,不会分配,和deployment不同,deployment都是提供相同的统一的入口。有状态的都是提供独立的访问。

[root@k8s-master ~]# cat service.yaml apiVersion: v1kind: Servicemetadata: name: headless-web namespace: defaultspec: clusterIP: None ports: - port: 80 protocol: TCP targetPort: 80 selector: app: nginx type: ClusterIP[root@k8s-master ~]# kubectl apply -f service.yaml service/headless-web created[root@k8s-master ~]# kubectl get svcNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEheadless-web ClusterIP None 80/TCP 9skubernetes ClusterIP 10.96.0.1 443/TCP 76d

可以看到没有为其分配cluster ip

稳定的网络标识怎么体现的,为每个pod分配固定的主机名,并且会创建dns记录

[root@k8s-master ~]# cat statefulset-web.yml apiVersion: apps/v1kind: StatefulSetmetadata: name: webspec: serviceName: "headless-web" replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web[root@k8s-master ~]# kubectl get podNAME READY STATUS RESTARTS AGEnfs-client-provisioner-d565bb89b-8d9hp 1/1 Running 4 26hweb-0 1/1 Running 0 99sweb-1 1/1 Running 0 39sweb-2 1/1 Running 0 22s

这个主机名是固定的,即使pod被删除掉了,重新启动也是这个名称上面体现了稳定的主机名

[root@k8s-master ~]# kubectl exec web-0 -- hostnameweb-0[root@k8s-master ~]# kubectl exec web-1 -- hostnameweb-1[root@k8s-master ~]# kubectl exec web-2 -- hostnameweb-2

可见,对于一个拥有 N 个副本的 StatefulSet 来说,Pod 在部署时按照 {0 …… N-1} 的序号顺序创建的,而删除的时候按照逆序逐个删除,这便是我想说的第一个特性。StatefulSet 创建出来的 Pod 都具有固定的、且确切的主机名。

StatefulSet 控制器:网络标识

可以看到,每个 Pod 都有一个对应的 A 记录。

[root@k8s-master ~]# kubectl run -i -t dns-test --image busybox:1.28.4 /bin/shIf you don't see a command prompt, try pressing enter./ # nslookup headless-web.default.svc.cluster.localServer: 10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName: headless-web.default.svc.cluster.localAddress 1: 10.244.169.147 web-2.headless-web.default.svc.cluster.localAddress 2: 10.244.169.146 web-1.headless-web.default.svc.cluster.localAddress 3: 10.244.36.81 web-0.headless-web.default.svc.cluster.local/ # ping web-0.headless-web.default.svc.cluster.localPING web-0.headless-web.default.svc.cluster.local (10.244.36.81): 56 data bytes64 bytes from 10.244.36.81: seq=0 ttl=62 time=1.105 ms64 bytes from 10.244.36.81: seq=1 ttl=62 time=2.990 ms64 bytes from 10.244.36.81: seq=2 ttl=62 time=1.408 ms^C

这里解析出来了三条,任意访问其中的一条可以访问具体的pod,即使pod重建ip发生变化,也能够通过这个记录解析到新的Ip上面

有头服务只会解析出一条记录

上面固定的dns名称和主机名就是网络标识的体现

删除一下这些 Pod,看看会有什么变化:

[root@k8s-master ~]# kubectl get podNAME READY STATUS RESTARTS AGEdns-test 1/1 Running 0 10mnfs-client-provisioner-d565bb89b-8d9hp 1/1 Running 4 26hweb-0 1/1 Running 0 14mweb-1 1/1 Running 0 13mweb-2 1/1 Running 0 13m[root@k8s-master ~]# kubectl delete pod web-0pod "web-0" deleted/ # nslookup headless-web.default.svc.cluster.localServer: 10.96.0.10Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.localName: headless-web.default.svc.cluster.localAddress 1: 10.244.36.82 web-0.headless-web.default.svc.cluster.localAddress 2: 10.244.169.147 web-2.headless-web.default.svc.cluster.localAddress 3: 10.244.169.146 web-1.headless-web.default.svc.cluster.local

可以看出,DNS 记录中 Pod 的域名没有发生变化,仅仅 IP 地址发生了更换。因此当 Pod 所在的节点发生故障导致 Pod 飘移到其他节点上,或者 Pod 因故障被删除重建,Pod 的 IP 都会发生变化,但是 Pod 的域名不会有任何变化,这也就意味着服务间可以通过不变的 Pod 域名来保障通信稳定,而不必依赖 Pod IP。

有了spec.serviceName这个字段,保证了 StatefulSet 关联的 Pod 可以有稳定的网络身份标识,即 Pod 的序号、主机名、DNS 记录名称等。

StatefulSet 控制器:独享存储

最后一个我想说的是,对于有状态的服务来说,每个副本都会用到持久化存储,且各自使用的数据是不一样的。

StatefulSet 通过 PersistentVolumeClaim(PVC)可以保证 Pod 的存储卷之间一一对应的绑定关系。同时,删除 StatefulSet 关联的 Pod 时,不会删除其关联的 PVC。

独享存储:StatefulSet的存储卷使用VolumeClaimTemplate创建,称为卷申请模板,当StatefulSet使用VolumeClaimTemplate创建一个PersistentVolume时,同样也会为每个Pod分配并创建一个编号的PVC,每个PVC绑定对应的PV,从而保证每个Pod都拥有独立的存储。

[root@k8s-master ~]# cat statefulset-web.yml apiVersion: apps/v1kind: StatefulSet metadata: name: webspec: serviceName: "headless-web" replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: spec: storageClassName: "managed-nfs-storage" accessModes: - ReadWriteOnce resources: requests: storage: 1Gi[root@k8s-master ~]# kubectl apply -f statefulset-web.yml statefulset.apps/web created

volumeClaimTemplates: 这个字段delploy是不支持的,只要state支持,这个是通过动态供给完成的,实际上还是申请pvc pv ,~]# kubectl get pvc,pvNAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGEpersistentvolumeclaim/ Bound pvc-7403fc64-998a-4d94-9bf7-00cde6f4e52d 1Gi RWO managed-nfs-storage 87spersistentvolumeclaim/ Bound pvc-9c10db54-3a82-4374-ad24-b14b59d4819e 1Gi RWO managed-nfs-storage 66spersistentvolumeclaim/ Bound pvc-d8ea2562-6137-404f-bef9-b0c9f700a040 1Gi RWO managed-nfs-storage 44sNAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGEpersistentvolume/pvc-7403fc64-998a-4d94-9bf7-00cde6f4e52d 1Gi RWO Delete Bound default/ managed-nfs-storage 86spersistentvolume/pvc-9c10db54-3a82-4374-ad24-b14b59d4819e 1Gi RWO Delete Bound default/ managed-nfs-storage 66spersistentvolume/pvc-d8ea2562-6137-404f-bef9-b0c9f700a040 1Gi RWO Delete Bound default/ managed-nfs-storage

Pvc和pv一一绑定的,可以看到每个独立的Pod都有独立的pv

[root@reg kubernetes]# pwd/ifs/kubernetes[root@reg kubernetes]# lsdefault-pvc不会被删除,并且删除重建之后还是可以读取之前的数据,当pod被重建之后,k8s还是根据pod编号找对应的pvc进行挂载

如何更新升级 StatefulSet

那么,如果想对一个 StatefulSet 进行升级,该怎么办呢?

在 StatefulSet 中,支持两种更新升级策略,即 RollingUpdate 和 OnDelete。

RollingUpdate策略是默认的更新策略。可以实现 Pod 的滚动升级,跟我们上一节课中 Deployment 介绍的RollingUpdate策略一样。比如我们这个时候做了镜像更新操作,那么整个的升级过程大致如下,先逆序删除所有的 Pod,然后依次用新镜像创建新的 Pod 出来。这里你可以通过kubectl get pod -n demo -w -l app=nginx来动手观察下。

总结

现在我们就总结下 StatefulSet 的特点:

具备固定的网络标记,比如主机名,域名等;支持持久化存储,而且最好能够跟实例一一绑定;可以按照顺序来部署和扩展;可以按照顺序进行终止和删除操作;在进行滚动升级的时候,也会按照一定顺序。

借助 StatefulSet 的这些能力,我们就可以去部署一些有状态服务,比如 MySQL、ZooKeeper、MongoDB 等。你可以跟着这个教程在 Kubernetes 中搭建一个 ZooKeeper 集群。

同时使用 RollingUpdate 更新策略还支持通过 partition 参数来分段更新一个 StatefulSet。所有序号大于或者等于 partition 的Pod 都将被更新。你这里也可以手动更新 StatefulSet 的配置来实验下。

当你把更新策略设置为 OnDelete 时,我们就必须手动先删除 Pod,才能触发新的 Pod 更新。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Jenkins 基础 01 简介与部署详解
下一篇:Jenkins 基础 06 项目类型和参数和并发构建
相关文章

 发表评论

暂时没有评论,来抢沙发吧~