Skip to main content

OpenKruise v1.3:新增自定义 Pod Probe 探针能力与大规模集群性能显著提升

· 阅读需要 1 分钟
Mingshan Zhao

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

OpenKruise 是针对 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配置如下:

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:
command:
- /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对象查看:

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

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

SidecarSet 通过 ControllerRevision 记录了关于 containersvolumesinitContainersimagePullSecretspatchPodMetadata 等字段的历史版本,并允许用户在 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 版本

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: sidecarset
spec:
...
injectionStrategy:
revision:
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 版本来控制灰度风险:

apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: sidecarset
labels:
apps.kruise.io/sidecarset-custom-version: version-2
spec:
...
updateStrategy:
partition: 90%
injectionStrategy:
revision:
customVersion: version-1

4. SidecarSet 支持注入 Pod Annotations

SidecarSet支持注入Pod Annotations,配置如下:

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,如下:

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 来设置并发度。

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

6. CloneSet 扩缩容与 PreparingDelete

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

在这种情况下:

  • 如果你将 replicasN 改为 N-1,当一个要删除的 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会将这个 Pod 重新置为 Normal 状态。
  • 如果你将 replicasN 改为 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 数量中。

在这种情况下:

  • 如果你将 replicasN 改为 N-1,当一个要删除的 Pod 还在 PreparingDelete 状态中时,你重新将 replicas 改为 N,CloneSet 会将这个 Pod 重新置为 Normal 状态。
  • 如果你将 replicasN 改为 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 页面,来查看更多的改动以及它们的作者与提交记录。

社区参与

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

  • 加入社区 Slack channel (English)
  • 加入社区钉钉群:搜索群号 23330762 (Chinese)
  • 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)