PersistentPodState

FEATURE STATE: Kruise v1.2.0

随着云原生的发展,越来越多的公司开始将有状态服务(如:Etcd、MQ)进行Kubernetes部署。K8S StatefulSet是管理有状态服务的工作负载,它在很多方面考虑了有状态服务的部署特征。 然而,StatefulSet只能保持有限的Pod状态,如:Pod Name有序且不变,PVC持久化,并不能满足其它Pod状态的保持需求,例如:固定IP调度,优先调度到之前部署的Node等。典型案例有:

  • 服务发现中间件服务对部署之后的Pod IP异常敏感,要求IP不能随意改变

  • 数据库服务将数据持久化到宿主机磁盘,所属Node改变将导致数据丢失

针对上述描述,Kruise通过自定义PersistentPodState CRD,能够保持Pod其它相关状态,例如:”固定IP调度”。 详细设计请参考:PPS Proposal

Usage

Annotations自动生成PersistentPodState

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: StatefulSet
  3. metadata:
  4. annotations:
  5. # 自动生成PersistentPodState对象
  6. kruise.io/auto-generate-persistent-pod-state: "true"
  7. # preferred node affinity,如下:Pod重建后将尽量部署到同Node
  8. kruise.io/preferred-persistent-topology: kubernetes.io/hostname[,other node labels]
  9. # required node affinity,如下:Pod重建后将强制部署到同Zone
  10. kruise.io/required-persistent-topology: failure-domain.beta.kubernetes.io/zone[,other node labels]

通过 annotation 的方式能生成一些常见的PersistentPodState,来满足绝大多数的场景。而对于一些复杂场景而言,可以直接使用 PersistentPodState CRD 的方式定义。

定义PersistentPodState CRD

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: PersistentPodState
  3. metadata:
  4. name: echoserver
  5. namespace: echoserver
  6. spec:
  7. targetRef:
  8. # 原生k8s 或 kruise StatefulSet
  9. # 只支持 StatefulSet 类型
  10. apiVersion: apps.kruise.io/v1beta1
  11. kind: StatefulSet
  12. name: echoserver
  13. # required node affinity,如下:Pod重建后将强制部署到同Zone
  14. requiredPersistentTopology:
  15. nodeTopologyKeys:
  16. failure-domain.beta.kubernetes.io/zone[,other node labels]
  17. # preferred node affinity,如下:Pod重建后将尽量部署到同Node
  18. preferredPersistentTopology:
  19. - preference:
  20. nodeTopologyKeys:
  21. kubernetes.io/hostname[,other node labels]
  22. # int, [1 - 100]
  23. weight: 100

固定IP调度实践

“固定IP调度”应该是比较常见的有状态服务的K8S部署要求,它的含义不是”指定Pod IP部署”,而是要求Pod在第一次部署之后,业务发布或机器驱逐等常规性运维操作都不会导致Pod IP发生变化。 达到上述效果,首先就需要K8S网络组件能够支持Pod IP保留以及尽量保持IP不变的能力,本文将flannel网络组件中的Host-local插件做了一些代码改造, 使之能够达到同Node下保持Pod IP不变的效果,相关原理就不在此陈述,代码请参考:host-local

“固定IP调度”好像有网络组件支持就好了,这跟PersistentPodState有什么关系呢?因为,网络组件实现”Pod IP保持不变”都有一定的限制, 例如:flannel只能支持同Node保持Pod IP不变。但是,K8S调度的最大特性就是”不确定性”,所以”如何保证Pod重建后调度到同Node上”就是PersistentPodState解决的问题。

1. 部署有状态服务echoserver,通过annotations声明”固定IP调度”:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: StatefulSet
  3. metadata:
  4. name: echoserver
  5. labels:
  6. app: echoserver
  7. annotations:
  8. kruise.io/auto-generate-persistent-pod-state: "true"
  9. kruise.io/preferred-persistent-topology: kubernetes.io/hostname
  10. spec:
  11. serviceName: echoserver
  12. replicas: 2
  13. selector:
  14. matchLabels:
  15. app: echoserver
  16. template:
  17. metadata:
  18. labels:
  19. app: echoserver
  20. annotations:
  21. # 通知host-local插件,Pod重建保持IP不变,"10" 代表Pod删除到下次调度成功,中间最多保留10分钟
  22. # 主要考虑到 删除、缩容等场景
  23. io.kubernetes.cri/reserved-ip-duration: "10"
  24. spec:
  25. terminationGracePeriodSeconds: 5
  26. containers:
  27. - name: echoserver
  28. image: cilium/echoserver:latest
  29. imagePullPolicy: IfNotPresent

2. 根据上述配置,kruise自动生成PersistentPodState资源,且status中记录Pod第一次部署的节点状态:

  1. apiVersion: apps.kruise.io/v1alpha1
  2. kind: PersistentPodState
  3. metadata:
  4. name: configserver
  5. namespace: configserver
  6. spec:
  7. targetRef:
  8. apiVersion: apps.kruise.io/v1beta1
  9. kind: StatefulSet
  10. name: configserver
  11. preferredPersistentTopology:
  12. - preference:
  13. nodeTopologyKeys:
  14. kubernetes.io/hostname
  15. weight: 100
  16. status:
  17. podStates:
  18. # status记录pod-0部署在worker2节点,pod-1部署在worker1节点
  19. configserver-0:
  20. nodeName: worker2
  21. nodeTopologyLabels:
  22. kubernetes.io/hostname: worker2
  23. configserver-1:
  24. nodeName: worker1
  25. nodeTopologyLabels:
  26. kubernetes.io/hostname: worker1

3. 业务发布或Node驱逐等导致Pod重建后,kruise将记录的Pod节点信息注入到Pod NodeAffinity中,进而实现Pod IP保持不变,如下:

  1. apiVersion: v1
  2. kind: Pod
  3. metadata:
  4. name: configserver-0
  5. namespace: configserver
  6. annotations:
  7. io.kubernetes.cri/reserved-ip-duration: 10
  8. spec:
  9. # kruise注入
  10. affinity:
  11. nodeAffinity:
  12. preferredDuringSchedulingIgnoredDuringExecution:
  13. - preference:
  14. matchExpressions:
  15. - key: kubernetes.io/hostname
  16. operator: In
  17. values:
  18. - worker2
  19. weight: 100
  20. containers:
  21. ...

staticIP