作者:立衡
OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的孵化项目。它来自阿里巴巴多年来容器化、云原生的技术沉淀,是阿里内部生产环境大规模应用的基于 Kubernetes 之上的标准扩展组件,也是紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。
OpenKruise:
OpenKruise 在 2023.3.31 发布了最新的 v1.4 版本(ChangeLog [ 1] ),新增 Job Sidecar Terminator 重磅功能,本文将对新版本做整体的概览介绍。
在 Kubernetes 中对于 Job 类型 Workload,人们通常希望当主容器完成任务并退出后,Pod 进入已完成状态。然而,当这些 Pod 拥有 Long-Running Sidecar 容器时,由于 Sidecar 容器在主容器退出后无法自行退出,导致 Pod 一直无法进入已完成状态。
面对这个问题,社区的常见解决方案一般都需要对 Main 和 Sidecar 进行改造,两者通过 Volume 共享来实现 Main 容器退出之后,Sidecar 容器完成退出的效果。
社区的解决方案可以解决这个问题,但是需要对容器进行改造,尤其对于社区通用的 Sidecar 容器,改造和维护的成本太高了。
为此,我们在 Kruise 中加入了一个名为 SidecarTerminator 的控制器,专门用于在此类场景下,监听主容器的完成状态,并选择合适的时机终止掉 Pod 中的 sidecar 容器,并且无需对 Main 和 Sidecar 容器进行侵入式改造。
对于运行于普通节点的 Pod(常规 Kubelet),使用该特性非常简单,用户只需要在要在目标 sidecar 容器中添加一个特殊的 env 对其进行标识,控制器会在恰当的时机利用 Kruise Daemon 提供的 CRR 的能力,将这些 sidecar 容器终止:
kind: Job
spec:
template:
spec:
containers:
- name: sidecar
env:
- name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT
value: "true"
- name: main
... ...
对于一些提供 Serverless 容器的平台,例如 **ECI [ 2] ** 或者 **Fargate [ 3] **,其 Pods 只能运行于 **Virtual-Kubelet [ 4] ** 之类的虚拟节点。然而,Kruise Daemon 无法部署和工作在这些虚拟节点之上,导致无法使用 CRR 能力将容器终止。但幸运地是,我们可以借助原生 Kubernetes 提供的 Pod 原地升级机制来达到同样的目的:只需要构造一个特殊镜像,这个镜像的唯一作用就是当被拉起后,会快速地主动退出,这样一来,只需要在退出 sidecar 时,将原本的 sidecar 镜像替换为快速退出镜像,即可达到退出 sidecar 的目的。
kind: Job
spec:
template:
spec:
containers:
- name: sidecar
env:
- name: KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT_WITH_IMAGE
value: "example/quick-exit:v1.0.0"
- name: main
... ...
使用你自己准备的快速退出镜像来替换上述 “example/quick-exit:v1.0.0”.
当前,无论是 Pod 的状态变化还是 Metadata 变化,Pod Update 事件都会触发 CloneSet reconcile 逻辑。CloneSet Reconcile 默认配置了三个 worker,对于集群规模较小的场景,这种情况并不会造成问题。
但对于集群规模较大或 Pod Update 事件较多的情况,这些无效的 reconcile 将会阻塞真正的 CloneSet reconcile,进而导致 CloneSet 的滚动升级等变更延迟。为了解决这个问题,可以打开 feature-gate CloneSetEventHandlerOptimization 来减少一些不必要的 reconcile 入队。
如果一个 Pod 被外部直接调用删除或驱逐时,这个 Pod 关联的 PVCs 还都存在;并且 CloneSet controller 发现数量不足重新扩容时,新扩出来的 Pod 会复用原 Pod 的 instance-id 并关联原来的 PVCs。
然而,如果 Pod 所在的 Node 出现异常,复用可能会导致新 Pod 启动失败,详情参考 **issue 1099 [ 5] **。为了解决这个问题,您可以设置字段 disablePVCReuse=true,当 Pod 被驱逐或者删除后,与 Pod 相关的 PVCs 将被自动删除,不再被复用。
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
...
replicas: 4
scaleStrategy:
disablePVCReuse: true
CloneSet 已经支持了 PreparingUpdate、PreparingDelete 两种生命周期钩子,用于应用的优雅下线,详情参考**社区文档 [ 6] **。为了支持优雅上线的场景,本次新增加了 PreNormal 状态,具体如下:
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
spec:
# define with finalizer
lifecycle:
preNormal:
finalizersHandler:
- example.io/unready-blocker
# or define with label
lifecycle:
preNormal:
labelsHandler:
example.io/block-unready: "true"
当 CloneSet 创建一个 Pod(包括正常扩容和重建升级)时:
PreNormal
hook 的定义,才会被认为是 Available
,并且才会进入 Normal
状态这对于一些 Pod 创建时的后置检查很有用,比如你可以检查 Pod 是否已经挂载到 SLB 后端,从而避免滚动升级时,旧实例销毁后,新实例挂载失败导致的流量损失。
当创建 CRR 资源时,如果容器正在启动过程中,CRR 将不会再重启容器。如果您想要强制重启容器,可以使用以下字段开启:
apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
spec:
...
strategy:
forceRecreate: true
当 Kubelet 创建 Pod 时,Kubelet 将会 attach metadata 到 container runtime cri 接口。镜像仓库可以根据这些 metadata 信息来确定拉镜像的来源业务,如果发生了仓库过载、压力过大的情况,可以对具体的业务进行降级处理。OpenKruise 镜像预热同样支持类似的能力,如下:
apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
spec:
...
image: nginx:1.9.1
sandboxConfig:
annotations:
io.kubernetes.image.metrics.tags: "cluster=cn-shanghai"
labels:
io.kubernetes.image.app: "foo"
非常欢迎你通过 Github/Slack/钉钉/微信 等方式加入我们来参与 OpenKruise 开源社区。你是否已经有一些希望与我们社区交流的内容呢?可以在我们的**社区双周会 [7 ] **上分享你的声音,或通过以下渠道参与讨论:
相关链接:
[1] ChangeLog
https://github.com/openkruise/kruise/blob/master/CHANGELOG.md
[2] ECI
https://www.aliyun.com/product/eci
[3] Fargate
https://aws.amazon.com/cn/fargate/
[4] Virtual-Kubelet
[5] issue 1099
https://github.com/openkruise/kruise/issues/1099
[6] 社区文档
https://openkruise.io/docs/user-manuals/cloneset/#lifecycle-hook
[7] 社区双周会
https://browser.alibaba-inc.com/?Url=https://shimo.im/docs/gXqmeQOYBehZ4vqo
[8] Slack channel
https://kubernetes.slack.com/?redir=%2Farchives%2Fopenkruise
点击此处,查看 OpenKruise 项目官方主页与文档