如果说Deployments和ReplicaSets解决的是无状态服务部署、扩缩容的刚需,那么StatefulSet控制器则是为了解决有状态服务的部署难题。
StatefulSet是一个管理有状态应用的API对象。
StatefulSet作为Controller为Pod提供其唯一的标识。
它可以保证应用程序部署和扩展的先后顺序
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组件内部的解析功能,以完成相关地址解析的支持作用。
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。
开始操作前的准备工作:
你需要先准备一个存储类: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字段决定的。
我在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