简介:OpenKruise 是阿里云开源的云原生利用自动化治理套件,也是以后托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 我的项目。它来自阿里巴巴多年来容器化、云原生的技术积淀,是阿里外部生产环境大规模利用的基于 Kubernetes 之上的规范扩大组件,也是紧贴上游社区规范、适应互联网规模化场景的技术理念与最佳实际。
作者 | 王思宇(酒祝)
Photo Creidt@ 王思宇(酒祝)
背景
OpenKruise 是阿里云开源的云原生利用自动化治理套件,也是以后托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 我的项目。它来自阿里巴巴多年来容器化、云原生的技术积淀,是阿里外部生产环境大规模利用的基于 Kubernetes 之上的规范扩大组件,也是紧贴上游社区规范、适应互联网规模化场景的技术理念与最佳实际。
OpenKruise 在 2021 年 5 月 20 日公布了最新的 v0.9.0 版本(ChangeLog),新增了 Pod 容器重启、资源级联删除防护等重磅性能,本文以下对新版本做整体的概览介绍。
Pod 容器重启/重建
“重启” 是一个很奢侈的需要,即便日常运维的诉求,也是技术畛域较为常见的 “复原伎俩”。而在原生的 Kubernetes 中,并没有提供任何对容器粒度的操作能力,Pod 作为最小操作单元也只有创立、删除两种操作形式。
有的同学可能会问,在云原生时代,为什么用户还要关注容器重启这种运维操作呢?在现实的 Serverless 模式下,业务只须要关怀服务本身就好吧?
这来自于云原生架构和过来传统根底基础设施的差异性。在传统的物理机、虚拟机时代,一台机器上往往会部署和运行多个利用的实例,并且机器和利用的生命周期是不同的;在这种状况下,利用实例的重启可能仅仅是一条 systemctl 或 supervisor 之类的指令,而无需将整个机器重启。然而,在容器与云原生模式下,利用的生命周期是和 Pod 容器绑定的;即惯例状况下,一个容器只运行一个利用过程,一个 Pod 也只提供一个利用实例的服务。
基于上述的限度,目前原生 Kubernetes 之下是没有 API 来为下层业务提供容器(利用)重启能力的。而 Kruise v0.9.0 版本提供了一种单 Pod 维度的容器重启能力,兼容 1.16 及以上版本的规范 Kubernetes 集群。在装置或降级 Kruise 之后,只须要创立 ContainerRecreateRequest(简称 CRR) 对象来指定重启,最简略的 YAML 如下:
apiVersion: apps.kruise.io/v1alpha1kind: ContainerRecreateRequestmetadata: namespace: pod-namespace name: xxxspec: podName: pod-name containers: - name: app - name: sidecar
其中,namespace 须要与要操作的 Pod 在同一个命名空间,name 可自选。spec 中 podName 是 Pod 名字,containers 列表则能够指定 Pod 中一个或多个容器名来执行重启。
除了上述必选字段外,CRR 还提供了多种可选的重启策略:
spec: # ... strategy: failurePolicy: Fail orderedRecreate: false terminationGracePeriodSeconds: 30 unreadyGracePeriodSeconds: 3 minStartedSeconds: 10 activeDeadlineSeconds: 300 ttlSecondsAfterFinished: 1800
- failurePolicy:Fail 或 Ignore,默认 Fail;示意一旦有某个容器进行或重建失败,CRR 立刻完结。
- orderedRecreate:默认 false;true 示意列表有多个容器时,等前一个容器重建实现了,再开始重建下一个。
- terminationGracePeriodSeconds:期待容器优雅退出的工夫,不填默认用 Pod 中定义的工夫。
unreadyGracePeriodSeconds:在重建之前先把 Pod 设为 not ready,并期待这段时间后再开始执行重建。
- 注:该性能依赖于 KruisePodReadinessGate 这个 feature-gate 要关上,后者会在每个 Pod 创立的时候注入一个 readinessGate。否则,默认只会给 Kruise workload 创立的 Pod 注入 readinessGate,也就是说只有这些 Pod 能力在 CRR 重建时应用 unreadyGracePeriodSeconds。
- minStartedSeconds:重建后新容器至多放弃运行这段时间,才认为该容器重建胜利。
- activeDeadlineSeconds:如果 CRR 执行超过这个工夫,则间接标记为完结(未实现的容器标记为失败)。
- ttlSecondsAfterFinished:CRR 完结后,过了这段时间主动被删除掉。
实现原理:当用户创立了 CRR 后,通过了 kruise-manager 核心端的初步解决,会被 Pod 所在节点上的 kruise-daemon 收到并开始执行。执行的过程如下:
- 如果 Pod 容器定义了 preStop,kruise-daemon 会先走 CRI 运行时 exec 到容器中执行 preStop。
- 如果没有 preStop 或执行实现,kruise-daemon 调用 CRI 接口将容器进行。
- kubelet 感知到容器退出,则会新建一个 “序号” 递增的新容器,并开始启动(以及执行 postStart)。
- kruise-daemon 感知到新容器启动胜利,上报 CRR 重启实现。
上述的容器 “序号” 其实就对应了 Pod status 中 kubelet 上报的 restartCount。因而,在容器重启后会看到 Pod 的 restartCount 减少。另外,因为容器产生了重建,之前长期写到旧容器 rootfs 中的文件会失落,然而 volume mount 挂载卷中的数据依然存在。
级联删除防护
Kubernetes 的面向终态自动化是一把 “双刃剑”,它既为利用带来了申明式的部署能力,同时也潜在地会将一些误操作行为被终态化放大。例如它的 “级联删除” 机制,即失常状况(非 orphan 删除)下一旦父类资源被删除,则所有子类资源都会被关联删除:
- 删除一个 CRD,其所有对应的 CR 都被清理掉。
- 删除一个 namespace,这个命名空间下包含 Pod 在内所有资源都被一起删除。
- 删除一个 workload(Deployment/StatefulSet/...),则上司所有 Pod 被删除。
相似这种 “级联删除” 带来的故障,咱们曾经听到不少社区 K8s 用户和开发者带来的埋怨。对于任何一家企业来说,其生产环境产生这种规模误删除都是不可接受之痛,阿里巴巴也不例外。
因而,在 Kruise v0.9.0 版本中,咱们将阿里外部所做的防级联删除能力输入到社区,冀望能为更多的用户带来稳定性保障。在以后版本中如果须要应用该性能,则在装置或降级 Kruise 的时候须要显式关上 ResourcesDeletionProtection
这个 feature-gate。
对于须要防护删除的资源对象,用户能够给其打上 policy.kruise.io/delete-protection
标签,value 能够有两种:
- Always: 示意这个对象禁止被删除,除非上述 label 被去掉。
- Cascading:这个对象如果还有可用的上司资源,则禁止被删除。
目前反对的资源类型、以及 cascading 级联关系如下:
CloneSet 新增性能
1. 删除优先级
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 的删除相似于下述程序:
- 未调度 < 已调度
- PodPending < PodUnknown < PodRunning
- Not ready < ready
- 较小 pod-deletion cost < 较大 pod-deletion cost
- 处于 Ready 工夫较短 < 较长
- 容器重启次数较多 < 较少
- 创立工夫较短 < 较长
2. 配合原地降级的镜像预热
当应用 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/v1alpha1kind: CloneSetmetadata: annotations: apps.kruise.io/image-predownload-parallelism: "5"
3. 先扩再缩的 Pod 置换形式
在过来版本中,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 数量。比方:
- 对于一个 CloneSet
maxUnavailable=2, maxSurge=1
且有一个pod-a
处于不可用状态, 如果你对另一个pod-b
指定删除, 那么 CloneSet 会立刻删除它,而后创立一个新 Pod。 - 对于一个 CloneSet
maxUnavailable=1, maxSurge=1
且有一个pod-a
处于不可用状态, 如果你对另一个pod-b
指定删除, 那么 CloneSet 会先新建一个 Pod、期待它 ready,最初再删除pod-b
。 - 对于一个 CloneSet
maxUnavailable=1, maxSurge=1
且有一个pod-a
处于不可用状态, 如果你对这个pod-a
指定删除, 那么 CloneSet 会立刻删除它,而后创立一个新 Pod。 - ...
4. 基于 partition 终态的高效回滚
在原生的 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:
- updateRevision:对应以后 CloneSet 所定义的 template 版本。
- currentRevision:该 CloneSet 前一次全量公布胜利的 template 版本。
5. 短 hash
默认状况下,CloneSet 在 Pod label 中设置的 controller-revision-hash
值为 ControllerRevision 的残缺名字,比方:
apiVersion: v1kind: Podmetadata: 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
sidecar 热降级性能
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/v1alpha1kind: SidecarSetspec: # ... 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
- upgradeType: HotUpgrade代表该sidecar容器的类型是hot upgrade,将执行热降级计划hotUpgradeEmptyImage: 当热降级sidecar容器时,业务必须要提供一个empty容器用于热降级过程中的容器切换。empty容器同sidecar容器具备雷同的配置(除了镜像地址),例如:command, lifecycle, probe等,然而它不做任何工作。
- lifecycle.postStart: 状态迁徙,该过程实现热降级过程中的状态迁徙,该脚本须要由业务依据本身的特点自行实现,例如:nginx热降级须要实现Listen FD共享以及流量排水(reload)。
具体 sidecar 注入和热降级流程,请参考官网文档。
最初
理解上述能力的更多信息,能够拜访官网文档。对 OpenKruise 感兴趣的同学欢送参加咱们的社区建设,曾经应用了 OpenKruise 我的项目的用户请在 issue 中注销。
钉钉搜寻群号 23330762 退出钉钉交换群!
版权申明:本文内容由阿里云实名注册用户自发奉献,版权归原作者所有,阿里云开发者社区不领有其著作权,亦不承当相应法律责任。具体规定请查看《阿里云开发者社区用户服务协定》和《阿里云开发者社区知识产权爱护指引》。如果您发现本社区中有涉嫌剽窃的内容,填写侵权投诉表单进行举报,一经查实,本社区将立即删除涉嫌侵权内容。