乐趣区

关于阿里云:OpenKruise-v12新增-PersistentPodState-实现有状态-Pod-拓扑固定与-IP-复用

作者:王思宇(酒祝)

云原生利用自动化治理套件、CNCF Sandbox 我的项目 — OpenKruise,近期公布了 v1.2 版本。

OpenKruise [ 1] 是针对 Kubernetes 的加强能力套件,聚焦于云原生利用的部署、降级、运维、稳定性防护等畛域。所有的性能都通过 CRD 等规范形式扩大,能够实用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可实现 Kruise 的一键部署,无需更多配置。

版本解析

在 v1.2 版本中,OpenKruise 提供了一个名为 PersistentPodState 的新 CRD 和控制器,在 CloneSet status 和 lifecycle hook 中新增字段,并对 PodUnavailableBudget 做了多重优化。

1. 新增 CRD 和 Controller-PersistentPodState

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

  • 服务发现中间件服务对部署之后的 Pod IP 异样敏感,要求 IP 不能随便扭转
  • 数据库服务将数据长久化到宿主机磁盘,所属 Node 扭转将导致数据失落

针对上述形容,Kruise 通过自定义 PersistentPodState CRD,可能放弃 Pod 其它相干状态,例如:“固定 IP 调度”。

一个 PersistentPodState 资源对象 YAML 如下:

apiVersion: apps.kruise.io/v1alpha1
kind: PersistentPodState
metadata:
  name: echoserver
  namespace: echoserver
spec:
  targetRef:
    # 原生 k8s 或 kruise StatefulSet
    # 只反对 StatefulSet 类型
    apiVersion: apps.kruise.io/v1beta1
    kind: StatefulSet
    name: echoserver
  # required node affinity,如下:Pod 重建后将强制部署到同 Zone
  requiredPersistentTopology:
    nodeTopologyKeys:
      failure-domain.beta.kubernetes.io/zone[,other node labels]
  # preferred node affinity,如下:Pod 重建后将尽量部署到同 Node
  preferredPersistentTopology:
    - preference:
        nodeTopologyKeys:
          kubernetes.io/hostname[,other node labels]
      # int, [1 - 100]
      weight: 100

“固定 IP 调度”应该是比拟常见的有状态服务的 K8s 部署要求,它的含意不是“指定 Pod IP 部署”,而是要求 Pod 在第一次部署之后,业务公布或机器驱赶等常规性运维操作都不会导致 Pod IP 发生变化。达到上述成果,首先就须要 K8s 网络组件可能反对 Pod IP 保留以及尽量放弃 IP 不变的能力,本文将 flannel 网络组件中的 Host-local 插件做了一些代码革新,使之可能达到同 Node 下放弃 Pod IP 不变的成果,相干原理就不在此陈说,代码请参考:host-local [ 2]

“固定 IP 调度”如同有网络组件反对就好了,这跟 PersistentPodState 有什么关系呢?因为,网络组件实现 ”Pod IP 放弃不变 ” 都有肯定的限度,例如:flannel 只能反对同 Node 放弃 Pod IP 不变。然而,K8s 调度的最大个性就是“不确定性”,所以“如何保障 Pod 重建后调度到同 Node 上”就是 PersistentPodState 解决的问题。

另外,你能够通过在 StatefulSet 或 Advanced StatefulSet 下面新增如下的 annotations,来让 Kruise 主动为你的 StatefulSet 创立 PersistentPodState 对象,从而防止了手动创立所有 PersistentPodState 的累赘。

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

2. CloneSet 针对百分比模式 partition 计算逻辑变动,新增 status 字段

过来,CloneSet 通过“向上取整”的形式来计算它的 partition 数值(当它是百分比模式的数值时),这意味着即便你将 partition 设置为一个小于 100% 的百分比,CloneSet 也有可能不会降级任何一个 Pod 到新版本。比方,对于一个 replicas=8 和 partition=90% 的 CloneSet 对象,它所计算出的理论 partition 数值是 8(来自 8 * 90% 向上取整),因而它临时不会执行降级动作。这有时候会为用户来带困惑,尤其是对于应用了一些 rollout 滚动降级组件的场景,比方 Kruise Rollout 或 Argo。

因而,从 v1.2 版本开始,CloneSet 会保障在 partition 是小于 100% 的百分比数值时,至多有 1 个 Pod 会被降级,除非 CloneSet 处于 replicas <= 1 的状况。

不过,这样会导致用户难以了解其中的计算逻辑,同时又须要在 partition 降级的时候晓得冀望降级的 Pod 数量,来判断该批次降级是否实现。

所以咱们另外又在 CloneSet status 中新增了 expectedUpdatedReplicas 字段,它能够很间接地展现基于以后的 partition 数值,冀望有多少 Pod 会被降级。对于用户来说:

只须要比对 status.updatedReplicas>= status.expectedUpdatedReplicas 以及另外的 updatedReadyReplicas 来判断以后公布阶段是否达到实现状态。

apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
  replicas: 8
  updateStrategy:
    partition: 90%
status:
  replicas: 8
  expectedUpdatedReplicas: 1
  updatedReplicas: 1
  updatedReadyReplicas: 1

3. 在 lifecycle hook 阶段设置 Pod not-ready

Kruise 在新近的版本中提供了 lifecycle hook 性能,其中 CloneSet 和 Advanced StatefulSet 都反对了 PreDelete、InPlaceUpdate 两种 hook,Advanced DaemonSet 目前只反对 PreDelete hook。

过来,这些 hook 只会将以后的操作卡住,并容许用户在 Pod 删除之前或者原地降级的前起初做一些自定义的事件(比方将 Pod 从服务端点中摘除)。然而,Pod 在这些阶段中很可能还处于 Ready 状态,此时将它从一些自定义的 service 实现中摘除,其实肯定水平上有点违反 Kubernetes 的常理,一般来说它只会将处于 NotReady 状态的 Pod 从服务端点中摘除。

因而,这个版本咱们在 lifecycle hook 中新增了 markPodNotReady 字段,它管制了 Pod 在处于 hook 阶段的时候是否会被强制设为 NotReady 状态。

type LifecycleStateType string
// Lifecycle contains the hooks for Pod lifecycle.
type Lifecycle struct 
    // PreDelete is the hook before Pod to be deleted. 
    PreDelete *LifecycleHook `json:"preDelete,omitempty"` 
    // InPlaceUpdate is the hook before Pod to update and after Pod has been updated. 
    InPlaceUpdate *LifecycleHook `json:"inPlaceUpdate,omitempty"`
}
type LifecycleHook struct {LabelsHandler     map[string]string `json:"labelsHandler,omitempty"`
    FinalizersHandler []string          `json:"finalizersHandler,omitempty"`

    /**********************  FEATURE STATE: 1.2.0 ************************/
    // MarkPodNotReady = true means:
    // - Pod will be set to 'NotReady' at preparingDelete/preparingUpdate state.
    // - Pod will be restored to 'Ready' at Updated state if it was set to 'NotReady' at preparingUpdate state.
    // Default to false.
    MarkPodNotReady bool `json:"markPodNotReady,omitempty"`
    /*********************************************************************/ 
}

对于配置了 markPodNotReady: true 的 PreDelete hook,它会在 PreparingDelete 阶段的时候将 Pod 设置为 NotReady,并且这种 Pod 在咱们从新调大 replicas 数值的时候无奈从新回到 normal 状态。

对于配置了 markPodNotReady: true 的 InPlaceUpdate hook,它会在 PreparingUpdate 阶段将 Pod 设置为 NotReady,并在 Updated 阶段将强制 NotReady 的状态去掉。

4. PodUnavailableBudget 反对自定义 workload 与性能优化

Kubernetes 本身提供了 PodDisruptionBudget 来帮忙用户爱护高可用的利用,但它只能防护 eviction 驱赶一种场景。对于多种多样的不可用操作,PodUnavailableBudget 可能更加全面地防护利用的高可用和 SLA,它不仅可能防护 Pod 驱赶,还反对其余如删除、原地降级等会导致 Pod 不可用的操作。

过来,PodUnavailableBudget 仅仅反对一些特定的 workload,比方 CloneSet、Deployment 等,但它不可能辨认用户本人定义的一些未知工作负载。

从 v1.2 版本开始,PodUnavailableBudget 反对了爱护任意自定义工作负载的 Pod,只有这些工作负载申明了 scale subresource 子资源。

在 CRD 中,scale 子资源的申明形式如下:

    subresources:
      scale:
        labelSelectorPath: .status.labelSelector
        specReplicasPath: .spec.replicas
        statusReplicasPath: .status.replicas

不过,如果你的我的项目是通过 kubebuilder 或 operator-sdk 生成的,那么只须要在你的 workload 定义构造上加一行注解并从新 make manifests 即可:

// +kubebuilder:subresource:scale:specpath=.spec.replicas,statuspath=.status.replicas,selectorpath=.status.labelSelector

另外,PodUnavailableBudget 还通过敞开 client list 时候的默认 DeepCopy 操作,来晋升了在大规模集群中的运行时性能。

5. 其余改变

你能够通过 Github release [ 3] 页面,来查看更多的改变以及它们的作者与提交记录。

社区参加

十分欢送你通过 Github/Slack/ 钉钉 / 微信 等形式退出咱们来参加 OpenKruise 开源社区。你是否曾经有一些心愿与咱们社区交换的内容呢?能够在咱们的社区双周会

(https://shimo.im/docs/gXqmeQO…)上分享你的声音,或通过以下渠道参加探讨:

  • 退出社区 Slack channel (English)\
    https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise\
  • 退出社区钉钉群:搜寻群号 23330762 (Chinese)
  • 退出社区微信群(新):增加用户 openkruise 并让机器人拉你入群 (Chinese)

参考链接:

[1] OpenKruise:

https: //openkruise.io/**

[2] host-local:

https: //github.com/openkruise/samples**

[3] Github release : 

https://github.com/openkruise…

戳此处,查看 OpenKruise 我的项目 github 主页!!

退出移动版