CRI

作者: 乙腾 | 来源:发表于2021-10-24 20:03 被阅读0次

介绍

  归根结底,Kubernetes Node(kubelet)的主要功能就是启动和停止容器的组件,我们称之为容器运行时(Container Runtime),其中最知名的就是Docker了。为了更具扩展性,Kubernetes从1.5版本开始就加入了容器运行时插件API,即Container Runtime Interface,简称CRI。

CRI概述

 每个容器运行时都有特点,因此不少用户希望Kubernetes能够支持更多的容器运行时。Kubernetes从1.5版本开始引入了CRI接口规范,通过插件接口模式,Kubernetes无须重新编译就可以使用更多的容器运行时。

CRI的组成

  • Protocol Buffers
  • gRPC API
  • 运行库支持
  • 开发中的标准规范和工具

CRI的出现的意义

  • 可替代的容器运行时支持是Kubernetes中的新概念,Docker的CRI实现在Kubernetes 1.6中被更新为Beta版本,并在kubelet启动时默认启动。

  • CRI出现之前,容器引擎最大的诟病是:和kubelet源码高度耦合

  • 在Kubernetes 1.3发布时,rktnetes项目同时发布,让rkt容器引擎成为除Docker外的又一选择。然而,不管是Docker还是rkt,都用到了kubelet的内部接口,同kubelet源码纠缠不清。这种程度的集成需要对kubelet的内部机制有非常深入的了解,还会给社区带来管理压力,这就给新生代容器运行时造成了难于跨越的集成壁垒。

  • CRI接口规范试图用定义清晰的抽象层清除这一壁垒,让开发者能够专注于容器运行时本身。在通向插件式容器支持及建设健康生态环境的路上,这是一小步,也是很重要的一步。

CRI的主要组件

  kubelet使用gRPC框架通过UNIX Socket与容器运行时(或CRI代理)进行通信。在这个过程中kubelet是客户端,CRI代理(shim)是服务端,如图

image.png
  • Protocol Buffers API包含两个gRPC服务:
  • ImageService:提供了从仓库拉取镜像、查看和移除镜像的功能
  • RuntimeService:负责Pod和容器的生命周期管理,以及与容器的交互(exec/attach/port-forward)

rkt和Docker这样的容器运行时可以使用一个Socket同时提供两个服务,在kubelet中可以用--container-runtime-endpoint和--image-service-endpoint参数设置这个Socket。

Pod和容器的生命周期管理

Pod由一组应用容器组成,其中包含

  • 共有的环境

    • 在CRI里,这个环境被称为PodSandbox。

      • Kubernetes有意为容器运行时留下一些发挥空间,它们可以根据自己的内部实现来解释PodSandbox。对于Hypervisor类的运行时,PodSandbox会具体化为一个虚拟机。
      • 在v1alpha1 API中,kubelet会创建Pod级别的cgroup传递给容器运行时,并以此运行所有进程来满足PodSandbox对Pod的资源保障。
    • 其他例如Docker,会是一个Linux命名空间。

  • 资源约束

在启动Pod之前,kubelet调用RuntimeService.RunPodSandbox来创建环境

这一过程包括为Pod设置网络资源(分配IP等操作)。

PodSandbox被激活之后,就可以独立地创建、启动、停止和删除不同的容器了。

kubelet会在停止和删除PodSandbox之前首先停止和删除其中的容器。

kubelet的职责

在于通过RPC管理容器的生命周期

  • 实现容器生命周期的钩子
  • 存活和健康监测
  • 以及执行Pod的重启策略等。

RuntimeService服务

包括对Sandbox和Container操作的方法,下面的伪代码展示了主要的RPC方法:

[图片上传失败...(image-4b5574-1635076459785)]

面向容器级别的设计思路

  • 最初的思路

    • 众所周知,Kubernetes的最小调度单元是Pod,它曾经可能采用的一个CRI设计就是复用Pod对象,使得容器运行时可以自行实现控制逻辑和状态转换,这样一来,就能极大地简化API,让CRI能够更广泛地适用于多种容器运行时。但是经过深入讨论之后,Kubernetes放弃了这一想法。

      • 首先,kubelet有很多Pod级别的功能和机制(例如crash-loop backoff机制),如果交给容器运行时去实现,则会造成很重的负担;
      • 其次且更重要的是,Pod标准还在快速演进中。很多新功能(如初始化容器)都是由kubelet完成管理的,无须交给容器运行时实现。
  • CRI的设计思路

    • CRI选择了在容器级别进行实现,使得容器运行时能够共享这些通用特性,以获得更快的开发速度。
    • 这并不意味着设计哲学的改变—kubelet要负责、保证容器应用的实际状态和声明状态的一致性。

Kubernetes为用户提供了与Pod及其中的容器进行交互的功能(kubectl exec/attach/port- forward)

kubelet目前提供了两种方式来支持这些功能:

  • 调用容器的本地方法

    • 在CRI中显式定义了这些调用方法,让容器运行时进行具体实现。
    • 下面的伪代码显示了Exec、Attach、PortForward这几个调用需要实现的RuntimeService方法:
image.png
  • 使用Node上的工具(例如nsenter及socat)

    • 因为多数工具都假设Pod用Linux namespace做了隔离,因此使用Node上的工具并不是一种容易移植的方案。

kubelet处理所有的请求连接的潜在问题

  kubelet处理所有的请求连接,使其有成为Node通信瓶颈的可能。

如何解决

  • 在设计CRI时,要让容器运行时能够跳过中间过程。
  • 容器运行时可以启动一个单独的流式服务来处理请求(还能对Pod的资源使用情况进行记录),并将服务地址返回给kubelet。
  • 这样kubelet就能反馈信息给API Server,使之可以直接连接到容器运行时提供的服务,并连接到客户端。

尝试使用新的Docker-CRI来创建容器

  要尝试新的Kubelet-CRI-Docker集成,只需为kubelet启动参数加上--enable-cri=true开关来启动CRI。这个选项从Kubernetes 1.6开始已经作为kubelet的默认选项了。如果不希望使用CRI,则可以设置--enable-cri=false来关闭这个功能。

  查看kubelet的日志,可以看到启用CRI和创建gRPC Server的日志:

image.png

创建一个Deployment:

image.png

查看Pod的详细信息,可以看到将会创建沙箱(Sandbox)的Event:

image.png

这表明kubelet使用了CRI接口来创建容器。

CRI的进展

  目前已经有多款开源CRI项目可用于Kubernetes:Docker、CRI-O、Containerd、frakti(基于Hypervisor的容器运行时),各CRI运行时的安装手册可参考官网https://kubernetes.io/docs/setup/cri/的说明。

相关文章

网友评论

    本文标题:CRI

    本文链接:https://www.haomeiwen.com/subject/zbkdaltx.html