下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

聊聊Kubernetes有状态应用管理

作者:匿名     来源: 云计算点击数:271发布时间: 2022-11-02 23:18:27

标签: 数据恢复Kubernetes

  StatefulSet这个控制器的主要作用之一,就是使用Pod模板创建Pod的时候,对它们进行编号,并且按照编号顺序完成作业,当StatefulSet的控制循环发现Pod的实际状态和期望状态不一致的时候,也会按着顺序对Pod进行操作。

  ​我们在《Kubernetes工作负载管理》中主要介绍了无状态应用的管理,当时也有提到有状态应用,但是由于那时候还没有解释数据如何持久化就没有做深度的介绍,而在这章,我们会着重介绍如何进行有状态应用的管理。

  什么是有状态应用

  实例之间的不等关系以及实例对外数据有依赖关系的应用,就被称为"有状态应用"。

  所谓实例之间的不等关系即对分布式应用来说,各实例,各应用之间往往有比较大的依赖关系,比如某个应用必须先于其他应用启动,否则其他应用将不能启动等。

  对外数据有依赖关系的应用,最显著的就是数据库应用,对于数据库应用,我们是需要持久化保存其数据的,如果是无状态应用,在数据库重启数据和应用就失去了联系,这显然是违背我们的初衷,不能投入生产的。

  所以,为了解决Kubernetes中有状态应用的有效支持,Kubernetes使用StatefulSet来编排管理有状态应用。 StatefulSet类似于ReplicaSet,不同之处在于它可以控制Pod的启动顺序,它为每个Pod设置唯一的标识。其具有一下功能:

  稳定的,唯一的网络标识符

  稳定的,持久化存储

  有序的,优雅部署和缩放

  有序的,自动滚动更新

  StatefulSet的设计很容易理解,它把现实世界抽象为以下两种情况:(1)、拓扑状态。这就意味着应用之间是不对等关系,应用要按某种顺序启动,即使应用重启,也必须按其规定的顺序重启,并且重启后其网络标识必须和原来的一样,这样才能保证原访问者能通过同样的方法访问新的Pod;(2)、存储状态 。这就意味着应用绑定了存储数据,不论什么时候,不论什么情况,对应用来说,只要存储里的数据没有变化,读取到的数据应该是同一份;

  所以StatefulSet的核心功能就是以某种方式记录Pod的状态,然后在Pod被重新创建时,通过某种方法恢复其状态。

  如何使用StatefulSet

  在《Kubernetes应用访问管理》中,我们介绍了Service,它是为一组Pod提供外部访问的一种方式。通常,我们使用 Service访问Pod有一下两种方式:(1)、通过Cluster IP,这个Clustre IP就相当于VIP,我们访问这个IP,就会将请求转发到后端Pod上;(2)、通过DNS方式,通过这种方式首先得确保Kubernetes集群中有DNS服务。这个时候我们只要访问"my-service.my-namespace.svc,cluster.local",就可以访问到名为my-service的Service所代理的后端Pod;

  而对于第二种方式,有下面两种处理方法:(1)、Normal Service,即解析域名,得到的是Cluster IP,然后再按照方式一访问;(2)、Headless Service,即解析域名,得到的是后端某个Pod的IP地址,这样就可以直接访问;

  而在使用StatefulSet的时候,主要用到Headless Service,还记得Headless Service怎么定义的吗?

  我们只需要把ClusterIP设置为None即可,如下:

  复制

  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4. name: nginx-headless-service

  5. labels:

  6. name: nginx-headless-service

  7. spec:

  8. clusterIP: None

  9. selector:

  10. name: nginx

  11. ports:

  12. - port: 8000

  13. targetPort: 80

  了解了Headless Service,还需要了解PV、PVC是怎么使用的,如果忘记了,可以移步《Kubernetes数据持久化管理》回顾,这里就不再赘述了。

  下面,我们开始使用StatefulSet。

  首先,我们创建两个个PV,因为准备为有状态应用创建两个副本,如下:

  复制

  1. apiVersion: v1

  2. kind: PersistentVolume

  3. metadata:

  4. name: nginx-pv01

  5. labels:

  6. storage: pv

  7. spec:

  8. accessModes:

  9. - ReadWriteOnce

  10. capacity:

  11. storage: 1Gi

  12. persistentVolumeReclaimPolicy: Recycle

  13. nfs:

  14. path: /data/k8s

  15. server: 192.168.205.128

  16. ---

  17. apiVersion: v1

  18. kind: PersistentVolume

  19. metadata:

  20. name: nginx-pv02

  21. labels:

  22. storage: pv

  23. spec:

  24. accessModes:

  25. - ReadWriteOnce

  26. capacity:

  27. storage: 1Gi

  28. persistentVolumeReclaimPolicy: Recycle

  29. nfs:

  30. path: /data/k8s

  31. server: 192.168.205.128

  然后编写StatefulSet需要的YAML文件,如下:

  复制

  1. apiVersion: v1

  2. kind: Service

  3. metadata:

  4. name: nginx

  5. spec:

  6. ports:

  7. - port: 80

  8. name: web

  9. clusterIP: None

  10. selector:

  11. app: nginx

  12. role: stateful

  13.

      14. ---

  15. apiVersion: apps/v1

  16. kind: StatefulSet

  17. metadata:

  18. name: web

  19. spec:

  20. serviceName: "nginx"

  21. replicas: 2

  22. selector:

  23. matchLabels:

  24. app: nginx

  25. template:

  26. metadata:

  27. labels:

  28. app: nginx

  29. role: stateful

  30. spec:

  31. containers:

  32. - name: nginx

  33. image: nginx

  34. ports:

  35. - containerPort: 80

  36. name: web

  37. volumeMounts:

  38. - name: www

  39. mountPath: /usr/share/nginx/html

  40. volumeClaimTemplates:

  41. - metadata:

  42. name: www

  43. spec:

  44. accessModes: [ "ReadWriteOnce" ]

  45. resources:

  46. requests:

  47. storage: 1Gi

  注意上面的 YAML 文件中和volumeMounts进行关联的是一个新的属性:volumeClaimTemplates,该属性会自动声明一个 pvc 对象和 pv 进行管理,而serviceName: "nginx"表示在执行控制循环的时候,用nginx这个Headless Service来保存Pod的可解析身份。

  创建完成后,可以看到会起两个Pod:

  复制

  1. $ kubectl get pod | grep web

  2. web-0 1/1 Running 0 2m45s

  3. web-1 1/1 Running 0 2m41s

  从这两个Pod的命令可以看到,它们的名字不像Deployment那样随机生成的字符串,而是0,1这样的序号。这是因为StatefulSet要保证每个Pod顺序,确保每次重启或者更新,每个Pod依然保持以前的数据,不会错乱。所以StatefulSet会以[statefulset-name]-[index]规则进行命名,其中index从0开始。而且每个Pod的创建是有顺序的,如上只有web-0进入running状态后,web-1才创建。

  当两个Pod都进入running状态后,就可以查看其各自的网络身份了,我们通过kubectl exec来查看,如下:

  复制

  1. $ kubectl exec web-0 -- sh -c 'hostname'

  2. web-0

  3. $ kubectl exec web-1 -- sh -c 'hostname'

  4. web-1

  可以看到这两个pod的hostname和pod的名字是一致的,都被分配为对应的编号,接下来我们用DNS的方式来访问Headless Service。

  我们先启动一个调试Pod,如下:

  复制

  1. apiVersion: v1

  2. kind: Pod

  3. metadata:

  4. name: dnsutils

  5. namespace: default

  6. spec:

  7. containers:

  8. - name: dnsutils

  9. image: lansible/dnstools

  10. command:

  11. - sleep

  12. - "3600"

  13. imagePullPolicy: IfNotPresent

  14. restartPolicy: Always

  然后进入dnsutils容器进行解析,如下:

  复制

  1. $ kubectl exec -it dnsutils -- /bin/sh

  2. / # nslookup web-0.nginx

  3. Server: 10.96.0.10

  4. Address: 10.96.0.10#53

  5.

      6. Name: web-0.nginx.default.svc.cluster.local

  7. Address: 172.16.51.247

      8.

  9. / # nslookup web-1.nginx

  10. Server: 10.96.0.10

  11. Address: 10.96.0.10#53

  12.

      13.Name: web-1.nginx.default.svc.cluster.local

  14. Address: 172.16.51.251

      15.

  16.  / #

  从nslookup的结果分析,在访问web-0.nginx的时候解析的是web-0这个Pod的IP,另一个亦然。这表示,如果我们在应用中配置web-0.nginx,则只会调用web-0这个Pod,在配置有状态应用,比如Zookeeper的时候,我们需要在配置文件里指定zkServer,这时候就可以指定类似:zk-0.zookeeper,zk-1.zookeeper。

  如果我们现在更新StatefuleSet,起更新顺序是怎么样的呢?

  首先,我们新开一个终端,输入以下命令用以观察:

  复制

  1. $ kubectl get pods -w -l role=stateful

  2. NAME READY STATUS RESTARTS AGE

  3. web-0 1/1 Running 0 67m

  4. web-1 1/1 Running 0 67m

  然后使用以下命令更新应用的镜像,如下:

  复制

  1. $ kubectl set image statefulset/web nginx=nginx:1.8

  然后观察web应用的更新顺序,如下:

  复制

  1. $ kubectl get pods -w -l role=stateful

  2. NAME READY STATUS RESTARTS AGE

  3. web-0 1/1 Running 0 67m

  4. web-1 1/1 Running 0 67m

  5. web-1 1/1 Terminating 0 68m

  6. web-1 1/1 Terminating 0 68m

  7. web-1 0/1 Terminating 0 68m

  8. web-1 0/1 Terminating 0 68m

  9. web-1 0/1 Terminating 0 68m

  10. web-1 0/1 Pending 0 0s

  11. web-1 0/1 Pending 0 0s

  12. web-1 0/1 ContainerCreating 0 0s

  13. web-1 0/1 ContainerCreating 0 1s

  14. web-1 1/1 Running 0 10s

  15. web-0 1/1 Terminating 0 69m

  16. web-0 1/1 Terminating 0 69m

  17. web-0 0/1 Terminating 0 69m

  18. web-0 0/1 Terminating 0 69m

  19. web-0 0/1 Terminating 0 69m

  20. web-0 0/1 Pending 0 0s

  21. web-0 0/1 Pending 0 0s

  22. web-0 0/1 ContainerCreating 0 0s

  23. web-0 0/1 ContainerCreating 0 1s

  24. web-0 1/1 Running 0 9s

  从整个顺序可以看到,起更新是从后往前进行更新的,也就是先更新web-1的pod,再更新web-0的pod。通过这种严格的对应规则,StatefulSet就保证了Pod的网络标识的稳定性,通过这个方法,就可以把Pod的拓扑状态按照Pod的名字+编号的方式固定起来。此外,Kubernetes还为每一个Pod提供了一个固定并且唯一的访问入口,即这个Pod的DNS记录。

  由此,我们对StatefulSet梳理如下:(1)、StatefulSet直接管理的是Pod。这是因为StatefulSet里的Pod实例不像ReplicaSet中的Pod实例完全一样,它们是有细微的区别,比如每个Pod的名字、hostname等是不同的,而且StatefulSet区分这些实例的方式就是为Pod加上编号;(2)、Kubernetes通过Headless Service为这个编号的Pod在DNS服务器中生成带同样编号的记录。只要StatefulSet能保证这个Pod的编号不变,那么Service中类似于web-0.nginx.default.svc.cluster.local这样的DNS记录就不会变,而这条记录所解析的Pod IP地址会随着Pod的重新创建自动更新;(3)、StatefulSet还可以为每个Pod分配并创建一个和Pod同样编号的PVC。这样Kubernetes就可以通过Persitent Volume机制为这个PVC绑定对应的PV,从而保证每一个Pod都拥有独立的Volume。这种情况下即使Pod被删除,它所对应的PVC和PV依然会保留下来,所以当这个Pod被重新创建出来过后,Kubernetes会为它找到同样编号的PVC,挂载这个PVC对应的Volume,从而获取到以前Volume以前的数据;

  总结

  StatefulSet这个控制器的主要作用之一,就是使用Pod模板创建Pod的时候,对它们进行编号,并且按照编号顺序完成作业,当StatefulSet的控制循环发现Pod的实际状态和期望状态不一致的时候,也会按着顺序对Pod进行操作。

  当然 StatefulSet 还拥有其他特性,在实际的项目中,我们还是很少回去直接通过 StatefulSet 来部署我们的有状态服务的,除非你自己能够完全能够 hold 住,对于一些特定的服务,我们可能会使用更加高级的 Operator 来部署,比如 etcd-operator、prometheus-operator 等等,这些应用都能够很好的来管理有状态的服务,而不是单纯的使用一个 StatefulSet 来部署一个 Pod就行,因为对于有状态的应用最重要的还是数据恢复、故障转移等等。​

  来源: 运维开发故事

    >>>>>>点击进入计算专题

赞(8)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程