StatefulSet
生产中常用的副本控制器如Deployment、DaemonSet、RS都是适用于无状态服务,其所管理的Pod
的启停顺序、IP、Podname等都是随机的,被管理的Pod被更新掉时,都会变化。
Kubernetes中StatefulSet是专为有状态服务如redis、kafka、consul等集群准备的集合,管理
所有有状态服务。
什么是有状态服务?有状态服务与无状态服务主要有如下区别:
1) 判断服务是否有状态,是指两个来自相同发起者(客户端)的请求在服务器端是否具备上下文关
系;
2) 有状态服务,一般服务器端都要保存与请求的相关信息,每个请求可以默认地使用之前的请求
信息;
3) 相反,无状态服务一般服务器端所能够处理的过程全部来自于请求所携带的信息或者其他服务
器端自身所保存的、并且可以被所有请求所使用的公共信息。
StatefulSet管理的Pod拥有固定的Pod名称(一般是pod.name-0/-1/-2顺延)、启停顺序(从-0开始
依次重启);在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。
与Deployment对应的网络映射为service,而在StatefulSet中与之对应的headless service(无头
服务),相较于services,headless service没有ClusterIP,解析它的名称时将返回该Headless
Service对应的全部Pod的Endpoint列表。
一个完整的StatefulSet应用由三个部分组成: headless service、StatefulSet controller、
volumeClaimTemplate。
kubernetes中选择使用statefullSet的服务所具有如下特点:
1) 稳定且唯一的网络标识符(如kfaka集群zookeeper的myid);
2) 稳定且持久的存储(如redis cluster模式下每个节点的shard);
3) 有序、平滑地部署扩展、终止和删除;
4) 有序的滚动更新;
------------------------------------------------
运行有状态的服务,前提:创建好storageclass。
示例:
```yaml [root@master statefulSet]# vim statefulSet.yaml apiVersion: v1 kind: Service metadata: name: headless-svc labels: app: headless-svc spec: ports: - name: myweb port: 80 selector: app: headless-pod clusterIP: None --- apiVersion: apps/v1 kind: StatefulSet metadata: name: statefulset spec: serviceName: headless-svc replicas: 3 selector: matchLabels: app: headless-pod template: metadata: labels: app: headless-pod spec: containers: - name: myweb image: nginx imagePullPolicy: IfNotPresent volumeMounts: - name: test-storage mountPath: /usr/share/nginx/html volumeClaimTemplates: - metadata: name: test-storage annotations: volume.beta.kubernetes.io/storage-class: storageclass spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Mi
为什么用 headless service 无头服务?
在用Deployment时,每一个Pod名称是没有顺序的,是随机字符串,因此是Pod名称是无序的,但是在statefulset中要求必须是有序 ,每一个pod不能被随意取代,pod重建后pod名称还是一样的。而pod IP是变化的,所以是以Pod名称来识别。pod名称是pod唯一性的标识符,必须持久稳定有效。这时候要用到无头服务,它可以给每个Pod一个唯一的名称 。
为什么用 volumeClaimTemplate?
对于有状态的副本集都会用到持久存储,对于分布式系统来讲,它的最大特点是数据是不一样的,所以各个节点不能使用同一存储卷,每个节点有自已的专用存储,但是如果在Deployment中的Pod template里定义的存储卷,是所有副本集共用一个存储卷,数据是相同的,因为是基于模板来的 ,而statefulset中每个Pod都要自已的专有存储卷,所以statefulset的存储卷就不能再用Pod模板来创建了,于是statefulSet使用volumeClaimTemplate,称为卷申请模板,它会为每个Pod生成不同的pvc,并绑定pv,从而实现各pod有专用存储。这就是为什么要用volumeClaimTemplate的原因。
//写完之后,直接运行,并且,在此之前,我们并没有创建PV,PVC,现在查看集群中的资源,是否有这两种资源?
[root@master statefulSet]# kubectl get pv,pvc NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE persistentvolume/pvc-582374f3-263c-403d-8811-e7043837bae9 100Mi RWO Delete Bound default/test-storage-statefulset-1 storageclass 2m47s persistentvolume/pvc-83b7e7a4-2d57-477b-a79f-7ec6a572cd11 100Mi RWO Delete Bound default/test-storage-statefulset-0 storageclass 3m42s persistentvolume/pvc-dc481cc2-2e28-441c-a4da-4145b7746670 100Mi RWO Delete Bound default/test-storage-statefulset-2 storageclass 113s NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE persistentvolumeclaim/test-storage-statefulset-0 Bound pvc-83b7e7a4-2d57-477b-a79f-7ec6a572cd11 100Mi RWO storageclass 3m42s persistentvolumeclaim/test-storage-statefulset-1 Bound pvc-582374f3-263c-403d-8811-e7043837bae9 100Mi RWO storageclass 2m47s persistentvolumeclaim/test-storage-statefulset-2 Bound pvc-dc481cc2-2e28-441c-a4da-4145b7746670 100Mi RWO storageclass 113s ```
//从上述结果中,我们知道,storageclass为我们自动创建了PV,volumeClaimTemplate为我们自动创建PVC,但是否能够满足我们所说的,每一个Pod都有自己独有的数据持久化目录,也就是说,每一个Pod内的数据都是不一样的。
//分别在对应的PV下,模拟创建不同的数据。
cd /nfsdata/ echo "0000000000000000" > default-test-storage-statefulset-0-pvc-4d7a43ed-bbee-48bd-86fa-a7e744613e80/index.html echo "1111111111111111" > default-test-storage-statefulset-1-pvc-7049d6a2-949c-4ea5-97da-c655a77ad8e3/index.html echo "2222222222222222" > default-test-storage-statefulset-2-pvc-9da968ba-8b49-4f10-bc97-ba4c79c87c6f/index.html //查看对应Pod的数据持久化目录,可以看出,每个Pod的内容都不一样。 kubectl exec statefulset-0 cat /usr/share/nginx/html/index.html 000000000000000 kubectl exec statefulset-1 cat /usr/share/nginx/html/index.html 111111111111111 kubectl exec statefulset-2 cat /usr/share/nginx/html/index.html 222222222222222
//即使删除Pod,然后statefulSet这个Pod控制器会生成一个新的Pod,这里不看Pod的IP,名称肯定和之前的一致,而且,最主要是持久化的数据仍然存在。
[root@master ~]# kubectl delete pod statefulset-2 pod "statefulset-2" deleted [root@master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE mysql-6fccccd487-5q2dt 1/1 Running 0 45h statefulset-0 1/1 Running 0 12m statefulset-1 1/1 Running 0 11m statefulset-2 0/1 ContainerCreating 0 5s [root@master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE mysql-6fccccd487-5q2dt 1/1 Running 0 45h statefulset-0 1/1 Running 0 12m statefulset-1 1/1 Running 0 11m statefulset-2 1/1 Running 0 11s kubectl exec statefulset-2 cat /usr/share/nginx/html/index.html 222222222222222
规律总结
匹配Pod name(网络标识)的模式为:名称 (statefulset名称)−(statefulset名称)-(序号)
StatefulSet为每个Pod副本创建了一个DNS域名,这个域名的格式为: $(podname).(headless server name),也就意味着服务间是通过Pod域名来通信而非Pod IP,因为当Pod所在Node发生故障时,Pod会被飘移到其它Node上,Pod IP会发生变化,但是Pod域名不会有变化。
StatefulSet使用Headless服务来控制Pod的域名,这个域名的FQDN为: (servicename).(service name).(namespace).svc.cluster.local,其中,“cluster.local”指的是集群的域名。
根据volumeClaimTemplates,为每个Pod创建一个pvc,pvc的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name)
删除Pod不会删除其pvc,手动删除pvc将自动释放pv。