作者:王思宇,阿里云技能专家,OpenKruise 社区负责人

通常情况下,人们只能运用一般旧数据作为 Kubernetes 中最小的操作单元。一些公司在他们的集群中侵略了 Kubelet 的代码,以便他们能够对容器做更多的作业。但是,为运转时扩展操作确实是一种错误的方法,因为它不利于开源和社区的合作。现在,云原生核算基金会沙箱项目之一 OpenKruise 供给了高级功用,能够在每个原始 Kubernetes 集群中操作容器运转时。在本次讲演中,咱们将介绍 OpenKruise 中一些功用的用法,以及它怎么与 Kubelet 和 CRI 合作。

本次共享首要分为以下几个部分,首先咱们介绍在 Kubernetes 中,针关于对容器 runtime 的操作约束有哪些,也便是说咱们在 Kubernetes 中,它的机制约束了咱们哪些操作,其实是对 controller runtime 是做不到的;第二点是 OpenKruise 是怎样拓宽对 controller runtime 的这些操作;第三点是咱们简略做一个 demo,咱们怎么经过 OpenKruise 来完结这些操作的;第四点是简略介绍一下咱们后续的一些规划。

Kubernetes 中针对容器运转时的操作有哪些约束?

Kubernetes 中的容器运转时

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

如上图所示,这是一个 Kubernetes 的基本结构,它的结构在每个节点( Node)上,其实是 Kubelet 在 API server 里面收到它的。比方 Pod 的改变,当 Kubelet 收到一个 Pod 创立之后,经过 CRI(Container Runtime Interface) , CNI 以及相似的公共接口(例如 CSI)来调用底层真实的接口完结者去完结操作。关于容器运转时来说,是经过 CRI 接口调用底层真实的 Runtime 运转时来完结对容器的创立和发动镜像拉取这些操作。

其间 CRI 是 Kubernetes1.5 之后参加的一个新功用,由协议缓冲区和 gRPC API 组成,供给了一个明确界说的抽象层,它的目的是关于 Kubelet 能屏蔽底下 Runtime 完结的细节而只显示所需的接口。 (github.com/kubernetes/…

在 Kubernetes1.5 之前,Kubelet 与 Docker 是相耦合的,Kubelet 其实是引入了 Docker 的 client,由它们直接对 Docker 操作。有了 CRI ,关于 Kubelet 来说就不必关心底层真实的 Runtime 完结是什么,而只需求调用这层接口,接口背面的完结可能是 Containerd-d ,可能是 CRI-O,也可能是 Docker。

CRI 的职责是对容器运转时以及对镜像做相关的办理,包含对容器的启停操作,对 Sandbox 容器的操作,容器 States 的数据采集,以及镜像的拉取和查询等操作。因此,CRI 供给了比较完善的容器接口,如下图。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

Kubernetes 中容器运转时的操作约束

Kubernetes API 并没有供给对容器运转时的接口操作,它仅有供给的是对 v1 版别的 Pod 操作(Pod API CRUD,Pod Subresources API)。除了 Pod 创立和更新之外,仅有能跟 Runtime 做比较相对应操作的是 Exec subresource 和 Log subresource。

Kubernetes 的 API 层面约束了用户只能创立或删去 Pod ,除此之外,里面的容器只能做 Exec, Log 这样的操作。在 Kubernetes 接口层面,用户无法进行比方拉取镜像、重启容器等操作。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

那有没有可能去拓宽这个 API 呢?

咱们发现 Kubelet 目前没有供给任何 hook 处理 plugin 的这个操作,来让外层能去动态拓宽 Kubelet 所做的作业。( Kubelet 的接口是不供给这样的插件机制的)那是否能够参加一个与 Kubelet 相似的新组件,能够连接到 CRI API,来拓宽 Kubernetes 容器进行时的操作呢?

咱们同样会调用 CRI 这一层,比方它能够拉镜像,能够重启容器,它的上层也能够接收一个关于 Kubernetes API 上界说的一个 CRD 资源,这个 CRD 资源界说了让用户能够声明对 CRI 接口做一些操作。比方它能够界说指定用户去拉镜像,去重启容器,能够做更多的作业。

这种方法是咱们能想到的,关于这个 Kubernetes 容器运转时 operations 的拓宽思路。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

OpenKruise 是什么?

OpenKruise 概念

Openkruise 是 Kubernetes 的一个拓宽套件,它弥补了 Kubernetes 许多功用缺乏,例如关于运用作业负载(运用布置发布相关功用)的缺乏,关于 container runtime 操作的缺乏等。它能够配合原生 Kubernetes 运用,并为办理运用容器、sidecar、镜像分发等方面供给更加强大和高效的才能。

2020 年 11 月,OpenKruise 作为 Sandbox 项目参加 CNCF。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

Openkruise 自身并不是一个 PasS 渠道。但 PasS 渠道能够经过运用 Openkruise 供给的拓宽才能更好的办理,运维云原生的运用。感兴趣的朋友能够经过以下网址了解更多 OpenKruise 相关信息。

Github: github.com/openkruise/…

WebSite: openkruise.io

OpenKruise 的功用

OpenKruise 是依据 CRD 的拓宽,其功用大致可分为五部分:

(1)运用作业负载:包含针对无状况运用,有状况运用的灰度发布、流量控制、它的原地晋级等相关功用;

(2)Sidecar 容器办理:供给更多增强的独立界说以及独立布置;

(3)运用多分区办理(Multi-domain management):一个运用假如要布置在多个分区,会进行打散和分片的办理。

(4)运用可用性防护:保护云原生运用在 Kubernetes 上运转时的高可用性;

(5)拓宽增强操作:经过这种方法来完结对 container runtime(运转时)增强的操作才能。其间拓宽增强操作是本文首要介绍的功用,后续咱们会具体打开。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

OpenKruise 功用图

OpenKruise 的架构

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

如图所示,OpenKruise 首要分成中心端(Kruise-manager)和节点端(Node)两个组件。中心端的 Kruise-manager 包含 controllers 和 webhooks ,经过 Kruise-manager 中心端的人物和每个节点上 kruise-daemon 功用结合,能够完结许多 Kubernetes 自身不供给的才能。Kruise-daemon 是用来防止对 Kubelet 做改造,经过拓宽的方法对 CRI runtime 进行操作。

Runtime 的拓宽功用

Runtime 有三个核心拓宽功用。

原地晋级功用

原地晋级是一种能够防止删去、新建 Pod 的晋级镜像才能。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

如上图所示:榜首部分并不是直接经过 kruise-daemon 拓宽,而是运用 Kubelet 的原生机制,叫做原地晋级。

怎么理解原地晋级?咱们举一个简略的例子:比方本来有一个 pod-a ,此时的 pod-a 是经过 deployment 的或者 Openkruise 的 cloneset 扩容出来的。假如咱们想要晋级 app容器的镜像版别,比方从 Fv1 晋级到 Fv2,正常情况下咱们运用 development 布置是采用 Recreatte Update 也便是重建 Pod 晋级,重建完结后咱们会看到 Pod 的姓名,Pod UID, (镜像也会晋级为 V2) 很大程度上都会发生改动。

再看后边两者,前者 Pod 的姓名和 UID 一定会发生改变,因为它现已不是同一个 Pod 的目标了。相关于咱们这次介绍的原地晋级,Pod 的目标其实仍是本来的目标,Pod 的姓名,Pod UID 都不变。其次,Pod 地点节点的 IP 也都不变,仅有改变的是镜像从 v1 级到了 v2,因为 Pod 节点没有发生任何改变,Pod 目标就不需求经过调度器从头调度,IP 分配,volume 分配,挂载这些耗时也都消除掉了,因此一个很明显好处便是节约了调度的耗时。

咱们都知道,当运用镜像从 v1 升到 v2 的过程中,可能仅仅最顶上的 layers 发生了改变,底下绝大部分的这个 base 镜像,公共 layers 是没有发生改变的。

当咱们在同一个节点上面做原地晋级的时候,能够复用原有 v1 镜像的大部分 layers ,只用下载小部分的 layers 镜像。

在晋级 app 容器的过程中,Pod 中的其他容器。如 sidecar 容器,是一直正常运转的,没有受到影响。反过来说,当咱们晋级 sidecar 容器时,容器也是正常运转的。这样能够很大程度上防止在晋级一个旁路(比方运维)容器的过程中,也要对事务才能形成影响。

1.1 优势

• 节约操作耗时,包含:Pod 调度、IP 分配、volume 分配、挂载等; • 复用大部分镜像层; • 当一个容器进行晋级时,不会对 Pod 中的其它容器形成影响;

1.2 作业原理

原地晋级的原理能够简略理解为 Kubelet 在创立每个容器时,会为容器核算一个 hash 值,当上层修改了比方 app 容器的 image 之后,Kubelet 就以为容器的 hash 值发生了改变。当 Kubelet 发现 Pod spec 中 app 容器的 hash 值和实际的,如 container d 对应的 hash 值不一致时,就会把旧的 app 容器停掉,用新的镜像再重建新的 app 容器,然后完结容器的原地晋级的才能。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

容器重启功用

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

容器重启的功用是许多事务,包含运维渠道都很依赖的功用。咱们可能会问,在 Kubernetes 中,一个 Pod 既然是无状况的,那么想重启时就直接删去 Pod,再新建一个 Pod 不就能够了吗?

这是当然能够的,但关于事务来说可能还存在许多 debug 场景,并不仅仅重建一个新的 Pod 就能够,而是要从原地把容器进行重启,相当于把里面的事务进程重启。比方想保留一些 volume 中的数据,一些网络、仓库信息等,这些场景都导致事务需求有 Kubernetes Pod 的容器原地重启才能。

Kubernetes 原生是不具备容器重启才能的,关于 Kubernetes 来说,假如想要重启容器,只能手动进入容器,把容器里的运用进程杀掉,这时当容器退出,Kubernetes 会再把它拉起。当然这种方法其实都比较 hack 的这么一种方法, Openkruise 所供给的容器重启才能关于 API 来说,只需求创立一个 CR。

CR 里写的东西很明确,name spacesl 只需界说跟 pod 在同一个 name spacesl 里,其间 name 是自界说的姓名,经过指定需求重启的 Pod 是哪个,需求重启 pod 出来哪些 containers,当界说了这些信息之后,提交 CR,当 kruise 收到 CR ,kruise – manager 会先经过 webhooks,对它注入一些信息,接下来 kruise-daemon 拿到 CR 会依据 CR 中界说的信息(比方会找到对应 Pod 的容器)履行 preStop hook,再经过 CRI 接口,经过 EXTC 履行 preStop,当 preStop 履行完结之后再调用这个 CRI 的 stop 。

其实这个中止方法和自身 Kubelet 在删去 Pod 时对容器的中止方法是一致的。当 kruise-daemon 对旧容器,比方对零号的 app(app_0)容器停掉之后,Kubelet 感知到 app 容器停掉了,接着就会新建一个 F1 容器并把它拉起,经过这种方法来完结高雅的容器原地重启才能。

代码示例:

apiVersion: apps.kruise.io/v1alpha1
kind: ContainerRecreateRequest
metadata:
namespace: pod-namespace
name: xxx
spec:
podName: pod-name
containers:
- name: app
strategy:
# ...
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 1800
status:
containerRecreateStates:
- name: app
phase: Succeeded
phase: Completed
# ..

镜像预热功用

提前在节点上包含新建的 Node 上预热,就能够大幅度减少后续 Pod 扩容的耗时。

如何基于 OpenKruise 打破原生 Kubernetes 中的容器运行时操作局限?

从上图咱们能够看到,关于上层用户来说 Openkruise 供给了一个 CRD 叫 ImagePullJob,用户能够界说需求预热哪个镜像,也能够挑选性的配置 selector(selector 能够是节点的标签挑选器也能够依据 Pod 进行挑选),以上都能够在 Pod 地点节点上进行预热。

当用户树立 ImagePullJob 后,关于 kruise 内部逻辑来说,kruise 会把 ImagePullJob 拆分到每个 node 对应 node image 的 CR 上,当同步上去后,节点上的 kruise-daemon 会拿到这个节点所对应node image 的 CR ,在节点上预热 CR 中界说的多个镜像。

换句话说,每个节点所对应的 node image 中的镜像列表,其实就表示了上层一切 ImagePullJob 指定在这个节点上要拉取镜像全集。kruise-daemon 底层拿到 node image 后,相当于也是调用 CRI 的 Pod image 接口来完结镜像的预热。

代码示例:

apiVersion: apps.kruise.io/v1alpha1
kind: ImagePullJob
metadata:
name: test-job
spec:
image: nginx:latest
parallelism: 10
selector:
# ...
podSelector:
# ...
completionPolicy:
# ...

未来项目规划

2021 年 12 月,OpenKruise 完结了首个正式版别 v1.0 的发布,使云原生运用自动化到达新的高峰。OpenKruise 从 2019 年发布 0.1 版别到现在现已有 2 年多的时间,已有超越 70 位奉献者参加奉献,star 数量现已超越 3000。2022,咱们将推进 OpenKruise 成为 CNCF Incubation 项目,推进云原生运用自动化领域进一步成熟。

运用用户:

• 阿里巴巴集团, 蚂蚁集团, 斗鱼TV, 申通, Boss 直聘

• 杭银消费, 万翼科技, 多点, Bringg, 佐疆科技

• Lyft, 携程, 享住智慧, VIPKID, 掌门 1 对 1

• 小红书, 比心, 永辉科技中心, 跟谁学, 哈啰出行

• Spectro Cloud, 艾佳生活, Arkane Systems, 滴普科技, 火花思维

• OPPO, 苏宁, 相聚时代, 汇量科技, 深圳凤凰木网络有限公司

• 小米, 网易,美团金融, Shopee, LinkedIn 发布云原生技能最新资讯、聚集云原生技能最全内容,定时举办云原生活动、直播,阿里产品及用户最佳实践发布。与你并肩探索云原生技能点滴,共享你需求的云原生内容。

关注【阿里巴巴云原生】公众号,获取更多云原生实时资讯!