作者:王思宇(酒祝)

云原生使用主动化办理套件、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 开源社区。你是否已经有一些期望与咱们社区交流的内容呢?能够在咱们的社区双周会

(shimo.im/docs/gXqmeQ…)上共享你的声响,或经过以下途径参加讨论:

  • 参加社区Slack channel(English)
    kubernetes.slack.com/?redir=%2Fa…\

  • 参加社区钉钉群:查找群号 23330762 (Chinese)

  • 参加社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)

参阅链接:

[1]OpenKruise:

*https: *//openkruise.io/**

[2]host-local:

https: //github.com/openkruise/samples

[3]Github release :

github.com/openkruise/…

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