简介:OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 项目。它来自阿里巴巴多年来容器化、云原生的技术沉淀,是阿里内部生产环境大规模应用的基于 Kubernetes 之上的标准扩展组件,也是紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。
做者 | 王思宇(酒祝)
Photo Creidt@ 王思宇(酒祝)html
OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 项目。它来自阿里巴巴多年来容器化、云原生的技术沉淀,是阿里内部生产环境大规模应用的基于 Kubernetes 之上的标准扩展组件,也是紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。
OpenKruise 在 2021 年 5 月 20 日发布了最新的 v0.9.0 版本(ChangeLog),新增了 Pod 容器重启、资源级联删除防御等重磅功能,本文如下对新版本作总体的概览介绍。
node
“重启” 是一个很朴素的需求,即便平常运维的诉求,也是技术领域较为常见的 “恢复手段”。而在原生的 Kubernetes 中,并无提供任何对容器粒度的操做能力,Pod 做为最小操做单元也只有建立、删除两种操做方式。
有的同窗可能会问,在云原生时代,为何用户还要关注容器重启这种运维操做呢?在理想的 Serverless 模式下,业务只须要关心服务自身就好吧?
这来自于云原生架构和过去传统基础基础设施的差别性。在传统的物理机、虚拟机时代,一台机器上每每会部署和运行多个应用的实例,而且机器和应用的生命周期是不一样的;在这种状况下,应用实例的重启可能仅仅是一条 systemctl 或 supervisor 之类的指令,而无需将整个机器重启。然而,在容器与云原生模式下,应用的生命周期是和 Pod 容器绑定的;即常规状况下,一个容器只运行一个应用进程,一个 Pod 也只提供一个应用实例的服务。
基于上述的限制,目前原生 Kubernetes 之下是没有 API 来为上层业务提供容器(应用)重启能力的。而 Kruise v0.9.0 版本提供了一种单 Pod 维度的容器重启能力,兼容 1.16 及以上版本的标准 Kubernetes 集群。在安装或升级 Kruise 以后,只须要建立 ContainerRecreateRequest(简称 CRR) 对象来指定重启,最简单的 YAML 以下:nginx
apiVersion: apps.kruise.io/v1alpha1 kind: ContainerRecreateRequest metadata: namespace: pod-namespace name: xxx spec: podName: pod-name containers: - name: app - name: sidecar
其中,namespace 须要与要操做的 Pod 在同一个命名空间,name 可自选。spec 中 podName 是 Pod 名字,containers 列表则能够指定 Pod 中一个或多个容器名来执行重启。
除了上述必选字段外,CRR 还提供了多种可选的重启策略:
git
spec: # ... strategy: failurePolicy: Fail orderedRecreate: false terminationGracePeriodSeconds: 30 unreadyGracePeriodSeconds: 3 minStartedSeconds: 10 activeDeadlineSeconds: 300 ttlSecondsAfterFinished: 1800
unreadyGracePeriodSeconds:在重建以前先把 Pod 设为 not ready,并等待这段时间后再开始执行重建。github
实现原理:当用户建立了 CRR 后,通过了 kruise-manager 中心端的初步处理,会被 Pod 所在节点上的 kruise-daemon 收到并开始执行。执行的过程以下:
api
上述的容器 “序号” 其实就对应了 Pod status 中 kubelet 上报的 restartCount。所以,在容器重启后会看到 Pod 的 restartCount 增长。另外,由于容器发生了重建,以前临时写到旧容器 rootfs 中的文件会丢失,可是 volume mount 挂载卷中的数据仍然存在。
bash
Kubernetes 的面向终态自动化是一把 “双刃剑”,它既为应用带来了声明式的部署能力,同时也潜在地会将一些误操做行为被终态化放大。例如它的 “级联删除” 机制,即正常状况(非 orphan 删除)下一旦父类资源被删除,则全部子类资源都会被关联删除:
架构
相似这种 “级联删除” 带来的故障,咱们已经听到很多社区 K8s 用户和开发者带来的抱怨。对于任何一家企业来讲,其生产环境发生这种规模误删除都是不可承受之痛,阿里巴巴也不例外。
所以,在 Kruise v0.9.0 版本中,咱们将阿里内部所作的防级联删除能力输出到社区,指望能为更多的用户带来稳定性保障。在当前版本中若是须要使用该功能,则在安装或升级 Kruise 的时候须要显式打开 ResourcesDeletionProtection
这个 feature-gate。
对于须要防御删除的资源对象,用户能够给其打上 policy.kruise.io/delete-protection
标签,value 能够有两种:
并发
目前支持的资源类型、以及 cascading 级联关系以下:
app
controller.kubernetes.io/pod-deletion-cost 是从 Kubernetes 1.21 版本后加入的 annotation,ReplicaSet 在缩容时会参考这个 cost 数值来排序。CloneSet 从 Kruise v0.9.0 版本后也一样支持了这个功能。
用户能够把这个 annotation 配置到 pod 上,它的 value 数值是 int 类型,表示这个 pod 相较于同个 CloneSet 下其余 pod 的 "删除代价",代价越小的 pod 删除优先级相对越高。没有设置这个 annotation 的 pod 默认 deletion cost 是 0。
注意这个删除顺序并非强制保证的,由于真实的 pod 的删除相似于下述顺序:
当使用 CloneSet 作应用原地升级时,只会升级容器镜像、而 Pod 不会发生重建。这就保证了 Pod 升级先后所在 node 不会发生变化,从而在原地升级的过程当中,若是 CloneSet 提早在全部 Pod 节点上先把新版本镜像拉取好,则在后续的发布批次中 Pod 原地升级速度会获得大幅度提升。
在当前版本中若是须要使用该功能,则在安装或升级 Kruise 的时候须要显式打开 PreDownloadImageForInPlaceUpdate
这个 feature-gate。打开后,当用户更新了 CloneSet template 中的镜像、且发布策略支持原地升级,则 CloneSet 会自动为这个新镜像建立 ImagePullJob 对象(OpenKruise 提供的批量镜像预热功能),来提早在 Pod 所在节点上预热新镜像。
默认状况下 CloneSet 给 ImagePullJob 配置的并发度是 1,也就是一个个节点拉镜像。若是须要调整,你能够在 CloneSet annotation 上设置其镜像预热时的并发度:
apiVersion: apps.kruise.io/v1alpha1 kind: CloneSet metadata: annotations: apps.kruise.io/image-predownload-parallelism: "5"
在过去版本中,CloneSet 的 maxUnavailable、maxSurge 策略只对应用发布过程生效。而从 Kruise v0.9.0 版本开始,这两个策略一样会对 Pod 指定删除生效。
也就是说,当用户经过 podsToDelete
或 apps.kruise.io/specified-delete: true
方式(具体见官网文档)来指定一个或多个 Pod 指望删除时,CloneSet 只会在当前不可用 Pod 数量(相对于 replicas 总数)小于 maxUnavailable 的时候才执行删除。同时,若是用户配置了 maxSurge 策略,则 CloneSet 有可能会先建立一个新 Pod、等待新 Pod ready、再删除指定的旧 Pod。
具体采用什么样的置换方式,取决于当时的 maxUnavailable 和实际不可用 Pod 数量。好比:
maxUnavailable=2, maxSurge=1
且有一个 pod-a
处于不可用状态, 若是你对另外一个 pod-b
指定删除, 那么 CloneSet 会当即删除它,而后建立一个新 Pod。maxUnavailable=1, maxSurge=1
且有一个 pod-a
处于不可用状态, 若是你对另外一个 pod-b
指定删除, 那么 CloneSet 会先新建一个 Pod、等待它 ready,最后再删除 pod-b
。maxUnavailable=1, maxSurge=1
且有一个 pod-a
处于不可用状态, 若是你对这个 pod-a
指定删除, 那么 CloneSet 会当即删除它,而后建立一个新 Pod。
在原生的 workload 中,Deployment 自身发布不支持灰度发布,StatefulSet 有 partition 语义来容许用户控制灰度升级的数量;而 Kruise workload 如 CloneSet、Advanced StatefulSet,也都提供了 partition 来支持灰度分批。
对于 CloneSet,Partition 的语义是保留旧版本 Pod 的数量或百分比。好比说一个 100 个副本的 CloneSet,在升级镜像时将 partition 数值阶段性改成 80 -> 60 -> 40 -> 20 -> 0,则完成了分 5 批次发布。
但过去,不论是 Deployment、StatefulSet 仍是 CloneSet,在发布的过程当中若是想要回滚,都必须将 template 信息(镜像)从新改回老版本。后二者在灰度的过程当中,将 partition 调小会触发旧版本升级为新版本,但再次 partition 调大则不会处理。
从 v0.9.0 版本开始,CloneSet 的 partition 支持了 “终态回滚” 功能。若是在安装或升级 Kruise 的时候打开了 CloneSetPartitionRollback
这个 feature-gate,则当用户将 partition 调大时,CloneSet 会将对应数量的新版本 Pod 从新回滚到老版本。
这样带来的好处是显而易见的:在灰度发布的过程当中,只须要先后调节 partition 数值,就能灵活得控制新旧版本的比例数量。但须要注意的是,CloneSet 所依据的 “新旧版本” 对应的是其 status 中的 updateRevision 和 currentRevision:
默认状况下,CloneSet 在 Pod label 中设置的 controller-revision-hash
值为 ControllerRevision 的完整名字,好比:
apiVersion: v1 kind: Pod metadata: labels: controller-revision-hash: demo-cloneset-956df7994
它是经过 CloneSet 名字和 ControllerRevision hash 值拼接而成。一般 hash 值长度为 8~10 个字符,而 Kubernetes 中的 label 值不能超过 63 个字符。所以 CloneSet 的名字通常是不能超过 52 个字符的,若是超过了,则没法成功建立出 Pod。
在 v0.9.0 版本引入了 CloneSetShortHash
新的 feature-gate。若是它被打开,CloneSet 只会将 Pod 中的 controller-revision-hash
的值只设置为 hash 值,好比 956df7994,所以 CloneSet 名字的长度不会有任何限制了。(即便启用该功能,CloneSet 仍然会识别和管理过去存量的 revision label 为完整格式的 Pod。)
SidecarSet 是 Kruise 提供的独立管理 sidecar 容器的 workload。用户能够经过 SidecarSet,来在必定范围的 Pod 中注入和升级指定的 sidecar 容器。
默认状况下,sidecar 的独立原地升级是先中止旧版本的容器,而后建立新版本的容器。这种方式更加适合不影响Pod服务可用性的sidecar容器,好比说日志收集 agent,可是对于不少代理或运行时的 sidecar 容器,例如 Istio Envoy,这种升级方法就有问题了。Envoy 做为 Pod 中的一个代理容器,代理了全部的流量,若是直接重启升级,Pod 服务的可用性会受到影响。若是须要单独升级 envoy sidecar,就须要复杂的 grace 终止和协调机制。因此咱们为这种 sidecar 容器的升级提供了一种新的解决方案,即热升级(hot upgrade)。
apiVersion: apps.kruise.io/v1alpha1 kind: SidecarSet spec: # ... containers: - name: nginx-sidecar image: nginx:1.18 lifecycle: postStart: exec: command: - /bin/bash - -c - /usr/local/bin/nginx-agent migrate upgradeStrategy: upgradeType: HotUpgrade hotUpgradeEmptyImage: empty:1.0.0
具体 sidecar 注入和热升级流程,请参考官网文档。
了解上述能力的更多信息,能够访问官网文档。对 OpenKruise 感兴趣的同窗欢迎参与咱们的社区建设,已经使用了 OpenKruise 项目的用户请在 issue 中登记。
钉钉搜索群号 23330762 加入钉钉交流群!
本文内容由阿里云实名注册用户自发贡献,版权归原做者全部,阿里云开发者社区不拥有其著做权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。若是您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将马上删除涉嫌侵权内容。