OpenKruise v1.3:新增自定义 Pod Probe 探针能力与大规模集群性能显著提升
作者:赵明山(立衡)
云原生应用自动化管理套件、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 一些新的特性。
新增 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: /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: ""
性能优化:大规模集群性能显著提升
-
1026 [ 1] 引入了延迟入队机制,大幅优化了在大规模应用集群下 kruise-manager 拉起时的 CloneSet 控制器工作队列堆积问题,在理想情况下初始化时间减少了 80% 以上。
-
1027 [ 2] 优化 PodUnavailableBudget 控制器 Event Handler 逻辑,减少无关 Pod 入队数量。
-
1011 [ 3] 通过缓存机制,优化了大规模集群下 Advanced DaemonSet 重复模拟 Pod 调度计算的 CPU、Memory 消耗。
-
1015 [ 4] , #1068 [ 5] 大幅降低了大规模集群下的运行时内存消耗。弥补了 v1.1 版本中 Disable DeepCopy 的一些疏漏点,减少 expressions 类型 label selector 的转换消耗。
SidecarSet 支持注入特定的历史版本
SidecarSet 通过 ControllerRevision 记录了关于 containers、volumes、initContainers、imagePullSecrets 和 patchPodMetadata 等字段的历史版本,并允许用户在 Pod 创建时选择特定的历史版本进行注入。
基于这一特性,用户可以规避在 SidecarSet 灰度发布时,因 Deployment 等 Workload 扩容、升级等操作带来的 SidecarSet 发布风险。如果不选择注入版本,SidecarSet 将对重建 Pod 默认全都注入最新版本 Sidecar。
SidecarSet 相关 ControllerRevision 资源被放置在了与 Kruise-Manager 相同的命名空间中,用户可以使用以下命令来查看:
kubectl get controllerrvisions -n kruise-system -l kruise.io/sidecarset-name=your-sidecarset-name
此外,用户还可以通过 SidecarSet 的 status.latestRevision 字段看到当前版本对应的 ControllerRevision 名称,以方便自行记录。
1. 通过 ControllerRevision 名称指定注入的 Sidecar 版本
apiVersion: apps.kruise.io/v1alpha1
kind: SidecarSet
metadata:
name: sidecarset
spec:
...
injectionStrategy:
revisionName: specific-controllerRevision-name
2. 通过自定义版本标识指定注入的 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:
customVersion: version-1
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 白名单或通过如下方式关闭白名单校验:
helm install kruise https://... --set featureGates="SidecarSetPatchPodMetadataDefaultsAllowed =true"
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"
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。
Advanced CronJob Time zones
默认情况下,所有 AdvancedCronJob schedule 调度时,都是基于 kruise-controller-manager 容器本地的时区所计算的。
不过,在 v1.3.0 版本中我们引入了 spec.timeZone 字段,你可以将它设置为任意合法时区的名字。例如,设置 spec.timeZone: "Asia/Shanghai" 则 Kruise 会根据国内的时区计算 schedule 任务触发时间。
Go 标准库中内置了时区数据库,作为在容器的系统环境中没有外置数据库时的 fallback 选择。
其他改动
你可以通过 Github release [ 6] 页面,来查看更多的改动以及它们的作者与提交记录。
社区参与
非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。
你是否已经有一些希望与我们社区交流的内容呢?
可以在我们的社区双周会上分享你的声音,或通过以下渠道参与讨论:
- 加入社区 Slack channel [ 7] (English)
- 加入社区钉钉群:搜索群号 23330762 (Chinese)
- 加入社区微信群(新):添加用户 openkruise 并让机器人拉你入群 (Chinese)
参考链接
[1] #1026
https://github.com/openkruise/kruise/pull/1026
[2] #1027
https://github.com/openkruise/kruise/pull/1027
[3] #1011
https://github.com/openkruise/kruise/pull/1011
[4] #1015
https://github.com/openkruise/kruise/pull/1015
[5] #1068
https://github.com/openkruise/kruise/pull/1068
[6] Github release:
https://github.com/openkruise /kruise/releases
[7] Slack channel:
https://kubernetes.slack.com/channels/openkruise
戳此处,查看 OpenKruise 项目官方主页与文档!