k8s之StatefulSet
StatefulSet(状态集)
1,什么是StatefulSet ?
StatefulSet又叫PetSet(之前的名称),它和RS,RC,Deployment等一样,都是pod控制器。
StatefulSet是为了解决有状态服务的问题(对应于deoloyment和RS,RC,ReplicaSet都是为无状态服务而设计的)。
什么是无状态服务?
在生产环境中,pod的名称是随机的,扩缩容的是时候没有规律,每一个pod都可以被新生成的pod代替。
2,StatefulSet的应用场景包括:
- 稳定的持久化存储:即pod重新调度后还是能够访问到相同的持久化数据,基于PVC来实现。
- 稳定的网络标志:即pod重新调度后其pod名称和host名称不变,基于Headless Service(即没有cluster ip的service)来实现。
- 有序部署,有序扩展:即pod是由顺序的,在部署或者扩展的时候要依据定义的顺序依次进行(即从0到N-1, 在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现。
- 有序收缩,有序删除:(即N-1到0)
从上面的引用场景可以发现,statefulset由以下几个部分组成:
1)headless Service:无头服务。用来定义pod的网络标识的(域名解析)。
2)statefulSet:定义具体的应用
3)volumeClaimTemplate:该模板自动为每一个pod创建pvc。
对比无状态服务,总结StatefulSet特点:pod的名称不变,每个副本启停有顺序,持久化存储,每个pod中的数据不同,根据定义的模板自动创建pvc。
3,接下来通过例子来实践statefulset资源的使用
部署nginx服务,通过storage class和statefulset实现有状态服务的数据持久化及其他操作。
操作流程如下:1)通过nfs服务部署storage class(创建pv)2)创建statefulset资源对象(创建pvc)3)测试数据持久化4)replicas扩容与缩容5)分区更新
1)通过nfs部署storage class
以下直接进行部署,具体信息及参数解释请参考上章博文 k8s之StorageClass
#开启nfs:
[root@master yaml]# yum -y install nfs-utils[root@master yaml]# vim /etc/exports/nfsdata *(rw,sync,no_root_squash)[root@master yaml]# mkdir /nfsdata[root@master yaml]# systemctl start rpcbind[root@master yaml]# systemctl start nfs-server[root@master yaml]# systemctl enable nfs-server[root@master yaml]# showmount -eExport list for master:/nfsdata *
#创建rbac权限:
[root@master yaml]# vim rbac-rolebind.yaml apiVersion: v1kind: ServiceAccountmetadata: name: nfs-provisioner namespace: default---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: nfs-provisioner-runner namespace: defaultrules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["watch", "create", "update", "patch"] - apiGroups: [""] resources: ["services", "endpoints"] verbs: ["get","create","list", "watch","update"] - apiGroups: ["extensions"] resources: ["podsecuritypolicies"] resourceNames: ["nfs-provisioner"] verbs: ["use"]---kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: run-nfs-provisionersubjects: - kind: ServiceAccount name: nfs-provisioner namespace: defaultroleRef: kind: ClusterRole name: nfs-provisioner-runner apiGroup: rbac.authorization.k8s.io
运行yaml文件。
#创建nfs-Deployment:
[root@master yaml]# vim nfs-deployment.yamlapiVersion: extensions/v1beta1kind: Deploymentmetadata: name: nfs-client-provisioner namespace: defaultspec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: nfs-client-provisioner spec: serviceAccount: nfs-provisioner containers: - name: nfs-client-provisioner image: registry.cn-hangzhou.aliyuncs.com/open-ali/nfs-client-provisioner volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: nfs-deploy #供给方的名称(自定义) - name: NFS_SERVER value: 172.16.1.30 #nfs服务器的ip地址 - name: NFS_PATH value: /nfsdata #nfs共享的目录 volumes: - name: nfs-client-root nfs: server: 172.16.1.30 path: /nfsdata
#创建storage class:
[root@master yaml]# vim sc.yamlapiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: statefu-nfs namespace: defaultprovisioner: nfs-deploy reclaimPolicy: Retain
//运行yaml文件后,查看sc的信息:[root@master yaml]# kubectl get scNAME PROVISIONER AGEstatefu-nfs nfs-deploy 48s
2)创建statefulset资源:
[root@master yaml]# vim statefulset.yamlapiVersion: v1kind: Service metadata: name: headless-svc #定义无头服务,需要定义标签 labels: app: headless-svcspec: ports: - name: testweb port: 80 clusterIP: None #需要将cluster ip定义为none selector: app: nginx #此处指定的标签需要在后边进行定义---apiVersion: apps/v1kind: StatefulSet metadata: name: sfs-web spec: serviceName: headless-svc #此处选择的服务名为上边定义的无头服务名 replicas: 3 selector: matchLabels: app: nginx #选择标签 template: metadata: labels: app: nginx #定义标签 spec: containers: - name: nginx image: nginx ports: - containerPort: 80 volumeMounts: #定义数据持久化 - name: test-nginx mountPath: /usr/share/nginx/html #挂载到容器内的路径 volumeClaimTemplates: #通过该字段(模板)为每个pod创建pvc - metadata: name: test-nginx annotations: volume.beta.kubernetes.io/storage-class: statefu-nfs #此处选择之前创建的storage class,名称要一致 spec: accessModes: - ReadWriteOnce #采用ReadWriteOnce的访问模式 resources: requests: storage: 100Mi #请求100M的空间
[root@master yaml]# kubectl apply -f statefulset.yaml service/headless-svc unchangedstatefulset.apps/sfs-web created
//运行成功后,查看创建的headless service和statefulset的状态:
//查看svc的详细信息:
//查看创建的pod:(确保正常运行)
可以看到每个pod都是有序的,它们的名称(域名)会以0,1,2....依次排序。
#根据上面我们说到的,通过sc会创建pv,以及通过sts中的模板会自动创建pvc,我们进行查看集群中的pv和pvc:
我们可以看到pv和pvc创建成功,状态已经是Bound了,访问模式是RWO(在sc中定义的模式),回收策略Delete ,都是通过sc和sts动态创建的,而并不是我们手动创建。
3)测试数据持久化
#查看挂载到nfs服务器上是否会生成每个pvc的共享目录:
#进入某个pod目录下创建测试网页文件:
[root@master yaml]# cd /nfsdata/default-test-nginx-sfs-web-0-pvc-4ef5e77e-1198-4ccc-81a7-db48d8a75023/[root@master default-test-nginx-sfs-web-0-pvc-4ef5e77e-1198-4ccc-81a7-db48d8a75023]# echo "hello world
" > index.html[root@master default-test-nginx-sfs-web-0-pvc-4ef5e77e-1198-4ccc-81a7-db48d8a75023]# lltotal 4-rw-r--r-- 1 root root 21 Jan 12 17:13 index.html
#我们进入到pod中,查看文件是否挂载成功:
#接下来我们将该pod删除掉:
验证重新生成的pod是否被替换,数据是否丢失?
通过上面的测试结果,我们可以得知有状态的服务不同于无状态服务,重新生成的pod名称不会发生改变,即便删除后也不会被新生成的pod覆盖,且原有pod中的数据也一并会存在。
4)replicas的扩容与缩容:
(1)扩容操作:
[root@master yaml]# vim statefulset.yaml
//重新运行yaml文件,查看新生成的pod:
(2)缩容操作:(以倒序的方式依次缩减)
//重新运行服务,查看pod状态:
通过上面的扩缩容操作,我们可以得知statefulset资源在生产环境中能够实现有序部署,即有序扩展,有序收缩,且pod的启停都是有一定顺序的。
5)分区更新操作:分区更新的主要目的是为pod进行一个分区,为了使我们对某个应用所有pod中的某一部分或者有选择性的pod进行更新操作。
#在更新之前,我们先来查看更新的参数:
[root@master yaml]# kubectl explain sts.spec.updateStrategy.rollingUpdate KIND: StatefulSetVERSION: apps/v1RESOURCE: rollingUpdate
#根据以上参数,我们对pod进行分区,并且对指定分区的pod进行版本升级:
我们将replicas的数量设置为8个,且定义分区更新,表示从第四个pod(分区)开始(包括第四个分区)进行更新,之前的pod是不会进行更新操作的。
注意:如果不指定分区,则默认分区从0开始。
#版本升级就不进行测试了,只需将镜像更改为新的nginx版本,重新运行服务后,可以看到从第四个pod开始(包括第四个)之后的pod镜像版本将会进行升级,而之前分区的pod,版本不会发生改变。
4,StatefulSet限制
1)该资源对象还在beta(测试)状态,需要kubernetes v1.5版本以上才支持。2)所有pod的volume必须使用pv或者是管理员事先创建好的。3)为了保证数据安全,删除statefulset时不会删除volume。4)statefulset需要一个Headless Service服务来定义DNS domin,需要在statefulset之前创建好。5)目前statefulset功能还尚未完善,比如上面的更新操作还需要手动解决。
以上就是statefulset资源的理解与实践,如果应用程序不需要任何稳定的标识符,有序部署,删除和scale,则使用Deployment或RS等无状态控制器来部署。反之在生产中使用statefulset控制器为保证pod与volume的关系不会断开,即使pod挂了后还能使用之前挂载的磁盘