做者 | 知谨 阿里云工程师git
本文整理自《CNCF x Alibaba 云原生技术公开课》第 28 讲,点击直达课程页面。github
关注“阿里巴巴云原生”公众号,回复关键词**“入门”**,便可下载从零入门 K8s 系列文章 PPT。docker
导读:CRI 是 Kubernetes 体系中跟容器打交道的一个很是重要的部分。本文做者主要分为三个部分来进行:首先会为你们介绍 CRI 接口的一个由来和它的设计;其次会和你们分享目前有哪些 CRI 的实现;最后会给你们介绍一下相关的工具备哪些。api
在 CRI 出现以前(也就是 Kubernetes v1.5 以前),Docker 做为第一个容器运行时,Kubelet 经过内嵌的 dockershim 操做 Docker API 来操做容器,进而达到一个面向终态的效果。在这以后,又出现了一种新的容器运行时 - rkt,它也想要成为 Kubernetes 支持的一个容器运行时,当时它也合到了 Kubelet 的代码之中。这两个容器运行时的加入使得 Kubernetes 的代码愈来愈复杂、难以维护。以后 hyber.sh 加入社区,也想成为第三个容器运行时。网络
此时就有人站出来讲,咱们能不能对容器运行时的操做抽象出一个接口,将 Kubelet 代码与具体的容器运行时的实现代码解耦开,只要实现了这样一套接口,就能接入到 Kubernetes 的体系中,这就是咱们后来见到的 Container Runtime Interface (CRI)。架构
有一句话说得很好,「软件问题均可以经过加一层来解决」,咱们的 CRI 就是加了这样一层。CRI 接口的通讯协议是 gRPC,这里的一个时代背景就是当时的 gRPC 刚刚开源,此外它的性能也是优于 http/REST 模式的。gRPC 不须要手写客户端代码和服务端代码,可以自动生成通讯协议代码。app
接下来咱们介绍一下 CRI 接口的设计。less
在引入了 CRI 接口以后,Kubelet 的架构如上图所示。异步
跟容器最相关的一个 Manager 是 Generic Runtime Manager,就是一个通用的运行时管理器。咱们能够看到目前 dockershim 仍是存在于 Kubelet 的代码中的,它是当前性能最稳定的一个容器运行时的实现。remote 指的就是 CRI 接口。CRI 接口主要包含两个部分:微服务
这里须要注意的是,咱们的 CNI(容器网络接口)也是在 CRI 进行操做的,由于咱们在建立 Pod 的时候须要同时建立网络资源而后注入到 Pod 中。接下来就是咱们的容器和镜像。咱们经过具体的容器建立引擎来建立一个具体的容器。
给你们介绍一下 CRI 接口的设计。咱们知道 Kubernetes 的一个运做的机制是面向终态的,在每一次调协的循环中,Kubelet 会向 apiserver 获取调度到本 Node 的 Pod 的数据,再作一个面向终态的处理,以达到咱们预期的状态。
循环的第一步,首先经过 List 接口拿到容器的状态,再经过 Sandbox 和 Container 接口来建立容器,另外还有镜像接口用来拉取容器镜像。CRI 描述了 Kubelet 指望的容器运行时行为,主要就是咱们刚刚所说的 3 个部分。
比方说咱们经过 kubectl 命令来运行一个 Pod,那么 Kubelet 就会经过 CRI 执行如下操做:
这里给你们介绍一下 CRI 的流式接口 exec。它能够用来在容器内部执行一个命令,又或者说能够 attach 到容器的 IO 流中作各类交互式的命令。它的特别之处在于,一个是节省资源,另外一个是链接的可靠性。
首先 exec 操做会发送到 apiserver,通过鉴权,apiserver 将对 Kubelet Server 发起 exec 的请求,而后 Kubelet 会调用 CRI 的 exec 接口将具体的请求发至容器的运行时。这个时候,容器运行时不是直接地在 exec 接口上来服务此次请求,而是经过咱们的 streaming server 来异步地返回每一次执行的结果。也就是说 apiserver 其实其实是跟 streaming server 交互来获取咱们的流式数据的。这样一来让咱们的整个 CRI Server 接口更轻量、更可靠。
目前 CRI 的一些实现:
CRI-containerd 是目前社区中比较主流的新一代 CRI 的实现,CRI-O 来自于红帽公司,PouchContainer 是由 alibaba 实现的 CRI,其它的 CRI 实现,这里就不一一介绍了。
下图是 CRI-containerd 的架构。
这套 CRI 接口是基于 containerd 实现的。在早期的实现中,CRI 实际上是做为一个独立进程的,再跟 containerd 进行交互。这样一来又多了一层进程跟进程之间的开销,所以在后来的版本中 CRI 的是直接以插件的形式实现到 containerd 中的,成为了 containerd 的一部分,从而可以可插拔地使用 CRI 接口。
整个架构看起来很是直观。这里的 Meta services、Runtime service 与 Storage service 都是 containerd 提供的接口。它们是通用的容器相关的接口,包括镜像管理、容器运行时管理等。CRI 在这之上包装了一个 gRPC 的服务。右侧就是具体的容器的实现,好比说,建立容器时就要建立具体的 runtime 和它的 shim,它们和 Container 一块儿组成了一个 Pod Sandbox。
CRI-containerd 的一个好处是,containerd 还额外实现了更丰富的容器接口,因此它能够用 containerd 提供的 ctr 工具来调用这些丰富的容器运行时接口,而不仅是 CRI 接口。
下图是 CRI-O 的实现思路。
它是经过直接在 OCI 上包装容器接口来实现的一个 CRI 服务。它对外提供的只有具体的 CRI 接口,没有咱们前面所提到的 containerd 提供的更丰富的接口。它主要包含两个部分,首先是对容器 runtime 的管理,另外一个是对镜像的管理。
下面给你们介绍一下 CRI 相关的工具。这几个工具都在特别兴趣小组的一个项目里面。
它是一个相似 docker 的命令行工具,用来操做 CRI 接口。它可以帮助用户和开发者调试容器问题,而不是经过 apply 一个 yaml 到 apiserver、再经过 Kubelet 操做的方式来调试。这样的链路太长,而这个命令行工具能够直接操做 CRI。
用于验证 CRI 接口行为是不是符合预期的。
还有一些性能工具用来测试接口性能。
CRI 标准的制定是至上而下的,经过 Kubernetes 的一些 feature 反向地要求 CRI 提供这样的功能,进而完善 CRI 规范。
咱们目前的 CRI 确定不能知足全部用户的需求,不少公司可能会对 CRI 接口作一些加强、定制,好比说 alibaba。最简单的方式是经过 annotation 来自定义 runtime 的行为。在每一个接口都设置一个 annotation 的字段,容器运行时经过理解这些字段来去自定义 runtime 的行为。同窗们能够尝试去在各个 CRI 接口中经过识别 annotation 的方式来达到自定义 runtime 行为的目的。
本节课的主要内容就到此为止了,这里为你们简单总结一下:
“阿里巴巴云原生关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,作最懂云原生开发者的公众号。”