---
slug: openkruise-1.3
title: OpenKruise v1.3：新增自定义 Pod Probe 探针能力与大规模集群性能显著提升
authors: [zmberg]
tags: [release]
---

云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise，近期发布了 v1.3 版本。

[OpenKruise](https://openkruise.io) 是针对 Kubernetes 的增强能力套件，聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。
所有的功能都通过 CRD 等标准方式扩展，可以适用于 1.16 以上版本的任意 Kubernetes 集群。单条 helm 命令即可完成 Kruise 的一键部署，无需更多配置。

## 版本解析

在版本v1.3中，OpenKruise提供了新的CRD资源 `PodProbeMarker`，改善了大规模集群的一些性能问题，Advanced DaemonSet支持镜像预热，
以及 CloneSet、WorkloadSpread、Advanced CronJob、SidecarSet一些新的特性。

### 1. 新增 CRD 和 Controller：PodProbeMarker

Kubernetes提供了三种默认的Pod生命周期管理：
- **Readiness Probe** 用来判断业务容器是否已经准备好响应用户请求，如果检查失败，会将该Pod从Service Endpoints中剔除。
- **Liveness Probe** 用来判断容器的健康状态，如果检查失败，kubelet将会重启该容器。
- **Startup Probe** 用来判断容器是否启动完成，如果定义了该Probe，那么Readiness Probe与Liveness Probe将会在它成功之后再执行。

所以Kubernetes中提供的Probe能力都已经限定了特定的语义以及相关的行为。**除此之外，其实还是存在自定义Probe语义以及相关行为的需求**，例如：
- **GameServer定义 Idle Probe 用来判断该Pod当前是否存在游戏对局**，如果没有，从成本优化的角度，可以将该Pod缩容掉。
- **K8S Operator定义 main-secondary Probe 来判断当前Pod的角色（main or secondary）**，升级的时候，可以优先升级 secondary，进而达到升级过程只有一次选主的行为，降低升级过程中服务抖动时间。

OpenKruise提供了自定义Probe的能力，并将结果返回到Pod Status中，用户可以根据该结果决定后续的行为。

PodProbeMarker配置如下：

```yaml
apiVersion: apps.kruise.io/v1alpha1
kind: PodProbeMarker
metadata:
  name: game-server-probe
  namespace: ns
spec:
  selector:
    matchLabels:
      app: game-server
  probes:
  - name: Idle
    containerName: game-server
    probe:
      exec: /home/game/idle.sh
      initialDelaySeconds: 10
      timeoutSeconds: 3
      periodSeconds: 10
      successThreshold: 1
      failureThreshold: 3
    markerPolicy:
    - state: Succeeded
      labels:
        gameserver-idle: 'true'
      annotations:
        controller.kubernetes.io/pod-deletion-cost: '-10'
    - state: Failed
      labels:
        gameserver-idle: 'false'
      annotations:
        controller.kubernetes.io/pod-deletion-cost: '10'
        podConditionType: game.io/idle
```

PodProbeMarker结果可以通过Pod对象查看：

```yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    app: game-server
    gameserver-idle: 'true'
  annotations:
    controller.kubernetes.io/pod-deletion-cost: '-10'
  name: game-server-58cb9f5688-7sbd8
  namespace: ns
spec:
  ...
status:
  conditions:
    # podConditionType
  - type: game.io/idle
    # Probe State 'Succeeded' indicates 'True', and 'Failed' indicates 'False'
    status: "True"
    lastProbeTime: "2022-09-09T07:13:04Z"
    lastTransitionTime: "2022-09-09T07:13:04Z"
    # If the probe fails to execute, the message is stderr
    message: ""
```

### 2. 性能优化：大规模集群性能显著提升

- [#1026](https://github.com/openkruise/kruise/pull/1026) 引入了延迟入队机制，大幅优化了在大规模应用集群下 kruise-manager 拉起时的 CloneSet 控制器工作队列堆积问题，在理想情况下初始化时间减少了 80% 以上。
- [#1027](https://github.com/openkruise/kruise/pull/1027) 优化 PodUnavailableBudget 控制器 Event Handler 逻辑，减少无关 Pod 入队数量。
- [#1011](https://github.com/openkruise/kruise/pull/1011) 通过缓存机制，优化了大规模集群下 Advanced DaemonSet 重复模拟 Pod 调度计算的 CPU、Memory 消耗。
- [#1015](https://github.com/openkruise/kruise/pull/1015), [#1068](https://github.com/openkruise/kruise/pull/1068) 大幅降低了大规模集群下的运行时内存消耗。弥补了 v1.1 版本中 Disable DeepCopy 的一些疏漏点，减少 expressions 类型 label selector 的转换消耗。

### 3. SidecarSet 支持注入特定的历史版本

SidecarSet 通过 ControllerRevision 记录了关于 `containers`、`volumes`、`initContainers`、`imagePullSecrets` 和 `patchPodMetadata` 等字段的历史版本，并允许用户在 Pod 创建时选择特定的历史版本进行注入。
基于这一特性，用户可以规避在 SidecarSet 灰度发布时，因Deployment 等 Workload 扩容、升级等操作带来的 SidecarSet 发布风险。如果不选择注入版本，SidecarSet 将对重建 Pod 默认全都注入最新版本 Sidecar。

SidecarSet 相关 ControllerRevision 资源被放置在了与 Kruise-Manager 相同的命名空间中，用户可以使用 `kubectl get controllerrevisions -n kruise-system -l kruise.io/sidecarset-name=your-sidecarset-name` 来查看。
此外，用户还可以通过 SidecarSet 的 `status.latestRevision` 字段看到当前版本对应的 ControllerRevision 名称，以方便自行记录。

#### 通过 ControllerRevision 名称指定注入的 Sidecar 版本
```yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
spec:
  ...
  injectionStrategy:
    revisionName: specific-controllerRevision-name
```

#### 通过自定义版本标识指定注入的 Sidecar 版本
用户可以通过在发版时，同时给 SidecarSet 打上 `apps.kruise.io/sidecarset-custom-version=your-version-id` 来标记每一个历史版本，SidecarSet 会将这个 label 向下带入到对应的 ControllerRevision 对象，以便用户进行筛选，并且允许用户在选择注入历史版本时，使用该 `your-version-id` 来进行描述。

假设用户只想灰度 `10%` 的 Pods 到 `version-2`，并且对于新创建的 Pod 希望都注入更加稳定的 `version-1` 版本来控制灰度风险：
```yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
  name: sidecarset
  labels:
    apps.kruise.io/sidecarset-custom-version: version-2
spec:
  ...
  updateStrategy:
    partition: 90%
  injectionStrategy:
    customVersion: version-1
```

### 4. SidecarSet 支持注入 Pod Annotations

SidecarSet支持注入Pod Annotations，配置如下：
```yaml
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
spec:
  containers:
  ...
  patchPodMetadata:
  - annotations:
    oom-score: '{"log-agent": 1}'
    custom.example.com/sidecar-configuration: '{"command": "/home/admin/bin/start.sh", "log-level": "3"}'
  patchPolicy: MergePatchJson
  - annotations:
    apps.kruise.io/container-launch-priority: Ordered
    patchPolicy: Overwrite | Retain
```
patchPolicy为注入的策略，如下：
- **Retain：** 默认策略，如果Pod中存在 annotation[key]=value ，则保留Pod原有的value。只有当 Pod中不存在 annotation[key] 时，才注入 annotations[key]=value。
- **Overwrite：** 与 Retain 对应，当 Pod 中存在 annotation[key]=value，将被强制覆盖为 value2。
- **MergePatchJson：** 与 Overwrite 对应，annotations value为 json 字符串。如果 Pod 不存在该 annotations[key]，则直接注入。如果存在，则进行 json value合并。
例如：Pod中存在 annotations[oom-score]='{"main": 2}'，注入后将 value json合并为 annotations[oom-score]='{"log-agent": 1, "main": 2}'。

**注意：** patchPolicy为Overwrite和MergePatchJson时，SidecarSet原地升级 Sidecar Container时，能够同步更新该 annotations。但是，如果只修改annotations则不能生效，只能搭配Sidecar容器镜像一起原地升级。
patchPolicy为Retain时，SidecarSet原地升级 Sidecar Container时，将不会同步更新该 annotations。

上述配置后，sidecarSet在注入sidecar container时，会注入Pod annotations，如下：
```yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    apps.kruise.io/container-launch-priority: Ordered
    oom-score: '{"log-agent": 1, "main": 2}'
    custom.example.com/sidecar-configuration: '{"command": "/home/admin/bin/start.sh", "log-level": "3"}'
  name: test-pod
spec:
  containers:
    ...
```

**注意：** SidecarSet从安全的考虑不应该注入或修改除 sidecar container 之外的 Pod 字段，所以如果想要使用该能力，首先需要配置 SidecarSet_PatchPodMetadata_WhiteList 白名单
或通过 Feature-gate SidecarSetPatchPodMetadataDefaultsAllowed=true 关闭白名单校验。

### 5. Advanced DaemonSet 支持镜像预热

如果你在安装或升级 Kruise 的时候启用了 `PreDownloadImageForDaemonSetUpdate` feature-gate，
DaemonSet 控制器会自动在所有旧版本 pod 所在 node 节点上预热你正在灰度发布的新版本镜像。 这对于应用发布加速很有帮助。

默认情况下 DaemonSet 每个新镜像预热时的并发度都是 `1`，也就是一个个节点拉镜像。
如果需要调整，你可以通过 `apps.kruise.io/image-predownload-parallelism` annotation 来设置并发度。

```yaml
apiVersion: apps.kruise.io/v1alpha1
kind: DaemonSet
metadata:
  annotations:
    apps.kruise.io/image-predownload-parallelism: "10"
```

### 6. CloneSet 扩缩容与 PreparingDelete

默认情况下，CloneSet 将处于 `PreparingDelete` 状态的 Pod 视为正常，意味着这些 Pod 仍然被计算在 `replicas` 数量中。

在这种情况下：

- 如果你将 `replicas` 从 `N` 改为 `N-1`，当一个要删除的 Pod 还在 `PreparingDelete` 状态中时，你重新将 `replicas` 改为 `N`，CloneSet 会将这个 Pod 重新置为 `Normal` 状态。
- 如果你将 `replicas` 从 `N` 改为 `N-1` 的同时在 `podsToDelete` 中设置了一个 Pod，当这个 Pod 还在 `PreparingDelete` 状态中时，你重新将 `replicas` 改为 `N`，CloneSet 会等到这个 Pod 真正进入 terminating 之后再扩容一个 Pod 出来。
- 如果你在不改变 `replicas` 的时候指定删除一个 Pod，当这个 Pod 还在 `PreparingDelete` 状态中时，CloneSet 会等到这个 Pod 真正进入 terminating 之后再扩容一个 Pod 出来。

从 Kruise v1.3.0 版本开始，你可以在 CloneSet 中设置一个 `apps.kruise.io/cloneset-scaling-exclude-preparing-delete: "true"` 标签，它标志着这个 CloneSet 不会将 `PreparingDelete` 状态的 Pod 计算在 `replicas` 数量中。

在这种情况下：

- 如果你将 `replicas` 从 `N` 改为 `N-1`，当一个要删除的 Pod 还在 `PreparingDelete` 状态中时，你重新将 `replicas` 改为 `N`，CloneSet 会将这个 Pod 重新置为 `Normal` 状态。
- 如果你将 `replicas` 从 `N` 改为 `N-1` 的同时在 `podsToDelete` 中设置了一个 Pod，当这个 Pod 还在 `PreparingDelete` 状态中时，你重新将 `replicas` 改为 `N`，CloneSet 会立即创建一个新 Pod。
- 如果你在不改变 `replicas` 的时候指定删除一个 Pod，当这个 Pod 还在 `PreparingDelete` 状态中时，CloneSet 会立即创建一个新 Pod。

### 7. Advanced CronJob Time zones

默认情况下，所有 AdvancedCronJob schedule 调度时，都是基于 kruise-controller-manager 容器本地的时区所计算的。

不过，在 v1.3.0 版本中我们引入了 `spec.timeZone` 字段，你可以将它设置为任意合法时区的名字。例如，设置 `spec.timeZone: "Asia/Shanghai"` 则 Kruise 会根据国内的时区计算 schedule 任务触发时间。

Go 标准库中内置了时区数据库，作为在容器的系统环境中没有外置数据库时的 fallback 选择。

### 8. 其他改动

你可以通过 [Github release](https://github.com/openkruise/kruise/releases) 页面，来查看更多的改动以及它们的作者与提交记录。

## 社区参与

非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。
你是否已经有一些希望与我们社区交流的内容呢？
可以在我们的[社区双周会](https://shimo.im/docs/gXqmeQOYBehZ4vqo)上分享你的声音，或通过以下渠道参与讨论：

- 加入社区 [Slack channel](https://kubernetes.slack.com/channels/openkruise) (English)
- 加入社区钉钉群：搜索群号 `23330762` (Chinese)
- 加入社区微信群（新）：添加用户 `openkruise` 并让机器人拉你入群 (Chinese)
