15.实践篇:StatefulSet控制器是如何实现金丝雀发布的?
15.实践篇:StatefulSet控制器是如何实现金丝雀发布的?
如果说Deployments和ReplicaSets解决的是无状态服务部署、扩缩容的刚需,那么StatefulSet控制器则是为了解决有状态服务的部署难题。
1.What is StatefulSet?
StatefulSet是一个管理有状态应用的API对象。StatefulSet作为Controller为Pod提供其唯一的标识。它可以保证应用程序部署和扩展的先后顺序
2.StatefulSet的应用场景
StatefulSet 适用于有以下某个或多个需求的应用场景:
稳定,具有唯一的网络标志
稳定,含有持久化存储
有序,需要优雅地部署和扩展
有序,能够自动滚动升级
3.StatefulSet的使用限制
为了解决有状态应用的部署难题,StatefulSet也有其对应的使用限制:
对应pod上面的存储必须以存储类(https://github.com/kubernetes/examples/tree/master/staging/persistent-volume-provisioning/README.md)的方式提供,或者预先由管理员进行手动配置
我们再来回顾一下:
PVC:好比接口,使用者只需要知道这个接口如何使用即可,比如该传哪些参数,哪些是必传的等等,他并不需要了解接口是如何实现的。
PV:就是这些接口的实现,内部是用nfs,还是ceph的存储系统等等。
SC(存储类):则是这些接口根据一系列规则所进行的抽象类,通过接收pvc请求,从而启到动态实例化pv的效果。
删除或扩展StatefulSet将不会删除与StatefulSet相关联的volum。
这样做是为了确保数据安全性,通常比自动清除所有相关StatefulSet资源更有价值
StatefulSets现在要求以 Headless Service(https://kubernetes.io/docs/concepts/services-networking/service/#headless-services)网络模式进行`Pod`的网络身份标示。
Headless Service知识点回顾:
这是一个比较特殊的service类型,有时候,你没必要或者不需要负载均衡和一个对外提供服务的ip地址。
在这种情况下,你可以在.spec.clusterIp中定义None字段,来申明一个Headless Service。
他可以通过coredns组件内部的解析功能,以完成相关地址解析的支持作用。
4.StatefulSet组成
Headless Service:用于为Pod资源标识符生成可解析的DNS记录,每个pod有唯一且有序的名称(在 [0,N)之间)。注意: [0,N)是一个左闭右开的区间!!!
VolumeClaimTemplates(存储卷申请模板):基于静态或动态PV供给方式为Pod资源提供专有的固定存储。
StatefulSet:用于管控Pod资源。
1>Headless Service在statefulset中是十分必要的,他给每个pod分配了唯一且有序的名称,当pod资源重建时候,他们将保持自己原有的序号。
2>statefulset定义中的每一个pod都不能使用同一个存储卷,并且有状态应用多半都需要存储系统,所以这时候这就需要引入volumeClainTemplate,当在使用statefulset创建pod时,会自动生成一个PVC,从而绑定一个自己专用的PV。
5.简单示例
开始操作前的准备工作:
你需要先准备一个存储类:storage-class。
我这里的存储类就选择之前配置的managed-nfs-storage(NFS Provisioner),你要记得使用的时候替换关键配置信息!
1)首先我们创建nginx.yaml的配置文件,其中serviceName申明了自己的headless service域,另外storageClassName需要指定成你自己的存储类。
apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None #我是headless selector: app: nginx --- apiVersion: apps/v1 kind: StatefulSet metadata: name: web spec: serviceName: "nginx" #声明它属于哪个Headless Service. replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web volumeMounts: - name: www mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: www spec: accessModes: ["ReadWriteOnce"] volumeMode: Filesystem resources: requests: storage: 50Mi storageClassName: managed-nfs-storage #存储类名,记得改为集群中已存在的
其中:
Headless Service:名为nginx,用来定义Pod网络标识( DNS domain)。
StatefulSet:定义具体应用,名为Nginx,有2个Pod副本,并为每个Pod定义了一个域名。
volumeClaimTemplates: 存储卷申请模板,创建PVC,指定pvc名称大小,将自动创建pvc,且pvc必须由存储类供应。
2)使用kubectl apply -f生成资源,你会发现:
1.pod资源的生成是有序的,从0到1
2.pvc和pod有一定的命名规范
3.pvc和pv均为动态供给
#pod生成的条目 web-0 0/1 Pending 0 0s <none> <none> <none> <none> web-0 0/1 Pending 0 0s <none> <none> <none> <none> web-0 0/1 Pending 0 0s <none> k8s-node01.shared <none> <none> web-0 0/1 ContainerCreating 0 0s <none> k8s-node01.shared <none> <none> web-0 1/1 Running 0 9s 10.244.0.34 k8s-node01.shared <none> <none> web-1 0/1 Pending 0 <invalid> <none> <none> <none> <none> web-1 0/1 Pending 0 <invalid> <none> <none> <none> <none> web-1 0/1 Pending 0 <invalid> <none> k8s-node01.shared <none> <none> web-1 0/1 ContainerCreating 0 <invalid> <none> k8s-node01.shared <none> <none> web-1 1/1 Running 0 4s 10.244.0.35 k8s-node01.shared <none> <none> #sc、pv和pvc详情 [root@k8s-etcd-mater01 web]# kubectl get sc,pv,pvc NAME PROVISIONER AGE storageclass.storage.k8s.io/managed-nfs-storage fuseim.pri/ifs 23m NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-0 managed-nfs-storage 2m46s persistentvolume/pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-1 managed-nfs-storage 2m32s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 2m46s persistentvolumeclaim/www-web-1 Bound pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 2m32s
3)接下来我们对其进行扩容成5个节点,观察pod的生成次序以及pvc和pv的变化
kubectl scale statefulset web --replicas=5
#pod按序生成 web-2 0/1 Pending 0 0s <none> <none> <none> <none> web-2 0/1 Pending 0 0s <none> <none> <none> <none> web-2 0/1 Pending 0 0s <none> <none> <none> <none> web-2 0/1 Pending 0 0s <none> k8s-node01.shared <none> <none> web-2 0/1 ContainerCreating 0 0s <none> k8s-node01.shared <none> <none> web-2 0/1 ErrImagePull 0 18s 10.244.0.37 k8s-node01.shared <none> <none> web-2 0/1 ImagePullBackOff 0 33s 10.244.0.37 k8s-node01.shared <none> <none> web-2 1/1 Running 0 56s 10.244.0.37 k8s-node01.shared <none> <none> web-3 0/1 Pending 0 5s <none> <none> <none> <none> web-3 0/1 Pending 0 5s <none> <none> <none> <none> web-3 0/1 Pending 0 5s <none> <none> <none> <none> web-3 0/1 Pending 0 5s <none> k8s-node01.shared <none> <none> web-3 0/1 ContainerCreating 0 5s <none> k8s-node01.shared <none> <none> web-3 1/1 Running 0 11s 10.244.0.38 k8s-node01.shared <none> <none> web-4 0/1 Pending 0 0s <none> <none> <none> <none> web-4 0/1 Pending 0 0s <none> <none> <none> <none> web-4 0/1 Pending 0 3s <none> <none> <none> <none> web-4 0/1 Pending 0 3s <none> k8s-node01.shared <none> <none> web-4 0/1 ContainerCreating 0 3s <none> k8s-node01.shared <none> <none> web-4 0/1 ErrImagePull 0 31s 10.244.0.39 k8s-node01.shared <none> <none> web-4 0/1 ImagePullBackOff 0 43s 10.244.0.39 k8s-node01.shared <none> <none> web-4 1/1 Running 0 47s 10.244.0.39 k8s-node01.shared <none> <none> #sc、pv和pvc详情 [root@k8s-etcd-mater01 web]# kubectl get sc,pv,pvc NAME PROVISIONER AGE storageclass.storage.k8s.io/managed-nfs-storage fuseim.pri/ifs 99m NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-0 managed-nfs-storage 78m persistentvolume/pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-1 managed-nfs-storage 78m persistentvolume/pvc-8d755bf2-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-2 managed-nfs-storage 61m persistentvolume/pvc-ac75e90e-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-3 managed-nfs-storage 60m persistentvolume/pvc-b32b1c9b-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-4 managed-nfs-storage 60m NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 78m persistentvolumeclaim/www-web-1 Bound pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 78m persistentvolumeclaim/www-web-2 Bound pvc-8d755bf2-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 61m persistentvolumeclaim/www-web-3 Bound pvc-ac75e90e-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 60m persistentvolumeclaim/www-web-4 Bound pvc-b32b1c9b-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 60m
你可以发现他们都是按序生成的。
4)现在,我们将其缩容至1个节点
kubectl patch statefulset web -p '{"spec":{"replicas":1}}'
#pod是逆序下线的 web-4 1/1 Terminating 0 62m 10.244.0.39 k8s-node01.shared <none> <none> web-4 0/1 Terminating 0 62m 10.244.0.39 k8s-node01.shared <none> <none> web-4 0/1 Terminating 0 63m 10.244.0.39 k8s-node01.shared <none> <none> web-4 0/1 Terminating 0 63m 10.244.0.39 k8s-node01.shared <none> <none> web-3 1/1 Terminating 0 63m 10.244.0.38 k8s-node01.shared <none> <none> web-3 0/1 Terminating 0 63m <none> k8s-node01.shared <none> <none> web-3 0/1 Terminating 0 63m <none> k8s-node01.shared <none> <none> web-3 0/1 Terminating 0 63m <none> k8s-node01.shared <none> <none> web-2 1/1 Terminating 0 64m 10.244.0.37 k8s-node01.shared <none> <none> web-2 0/1 Terminating 0 64m 10.244.0.37 k8s-node01.shared <none> <none> web-2 0/1 Terminating 0 64m 10.244.0.37 k8s-node01.shared <none> <none> web-2 0/1 Terminating 0 64m 10.244.0.37 k8s-node01.shared <none> <none> web-1 1/1 Terminating 0 81m 10.244.0.35 k8s-node01.shared <none> <none> web-1 0/1 Terminating 0 81m 10.244.0.35 k8s-node01.shared <none> <none> web-1 0/1 Terminating 0 81m 10.244.0.35 k8s-node01.shared <none> <none> web-1 0/1 Terminating 0 81m 10.244.0.35 k8s-node01.shared <none> <none> #但pvc和pv还是存在的 [root@k8s-etcd-mater01 web]# kubectl get sc,pv,pvc NAME PROVISIONER AGE storageclass.storage.k8s.io/managed-nfs-storage fuseim.pri/ifs 103m NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-0 managed-nfs-storage 83m persistentvolume/pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-1 managed-nfs-storage 83m persistentvolume/pvc-8d755bf2-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-2 managed-nfs-storage 65m persistentvolume/pvc-ac75e90e-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-3 managed-nfs-storage 64m persistentvolume/pvc-b32b1c9b-2ef6-11ea-82c1-001c42662fdd 50Mi RWO Delete Bound default/www-web-4 managed-nfs-storage 64m NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/www-web-0 Bound pvc-1a1a952c-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 83m persistentvolumeclaim/www-web-1 Bound pvc-2283c042-2ef4-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 83m persistentvolumeclaim/www-web-2 Bound pvc-8d755bf2-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 65m persistentvolumeclaim/www-web-3 Bound pvc-ac75e90e-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 64m persistentvolumeclaim/www-web-4 Bound pvc-b32b1c9b-2ef6-11ea-82c1-001c42662fdd 50Mi RWO managed-nfs-storage 64m
5)最后我们delete -f nginx.yaml,你会发现pvc和pv还是存在的,这种设计是很合理的。
如果数据不再使用,也是需要手动删除的
kubectl delete pvc www-web-0 www-web-1 www-web-2 www-web-3 www-web-4
提示:pvc和pv的数据是否关联删除,是通过存储类中的archiveOnDelete字段决定的。
你是否还记得呢?
6.金丝雀发布的实现
我在deployment中也提过如何使用pause命令进行金丝雀发布,不知你是否还记得?
这里,我要讲的是statefulset中金丝雀发布的逻辑。
1) 具体金丝雀发布流程如下图所示
2) 说明:
statefulset资源是按序生成、命名规律,且逆序更新(是否还记得刚才的缩容操作)
partition参数:指定需要分区的数量。
当partition=3时,表示将Pod web-0、Pod web-1、Pod web-2视为1个分区,新版本只会更新Pod web-3和Pod web-4
当partition=0时,发布策略将会把剩余的Pod也都发布成新版本
partition字段的申明帮助:
kubectl explain sts.spec.updateStrategy.rollingUpdate.partition
@版权声明:51CTO独家出品,未经允许不能转载,否则追究法律责任