深入理解SIG- Node与CRI

CRI和CNI是K8S里两个不同的概念。可以记为:CNI的N是net,与网络有关。CRI的R是rong和容器有关。

CRI和CNI是Kubernetes容器运行时(CRI)和容器网络接口(CNI)的缩写,它们是Kubernetes中两个不同的组件,分别用于管理容器的运行时和网络。

CRI是Kubernetes中的一个接口规范,它定义了容器运行时与Kubernetes API Server之间的交互方式。通过实现CRI接口规范,Kubernetes可以支持多种不同的容器运行时,如Docker、containerd、CRI-O等。CRI负责管理容器的生命周期,包括创建、启动、停止、销毁等操作。

CNI是Kubernetes中的另一个接口规范,它定义了容器网络插件与Kubernetes CNI插件之间的交互方式。通过实现CNI接口规范,Kubernetes可以支持多种不同的容器网络插件,如Flannel、Calico、Weave Net等。CNI负责为Pod提供网络连接,包括为每个Pod分配IP地址、配置网络路由等操作。

因此,CRI和CNI是Kubernetes中两个不同的组件,它们分别用于管理容器的运行时和网络。CRI负责管理容器的生命周期,而CNI负责为Pod提供网络连接。两者之间没有直接关系,但它们协同工作可以为Kubernetes提供完整的容器管理能力。


在 Kubernetes 社区里,与 kubelet 以及容器运行时管理相关的内容,都属于 SIG-Node 的范畴。而kubelet的核心功能就是将他所定义的容器运气起来。所以kubelet是对容器的控制器,不要和我们之前学习的对CRD自定义的控制器混搅。

kubelet的工作原理

可以看到,kubelet 的工作核心,就是一个控制循环,即:SyncLoop(图中的大圆圈)。 而驱动这个控制循环运行的事件,包括四种:

1. Pod 更新事件;
2. Pod 生命周期变化;
3. kubelet 本身设置的执行周期;

4. 定时的清理事件

所以根我们自定义控制器类似,kubelet 启动的时候,要做的第一件事情,就是设置 Listers, 也就是注册它所关心的各种事件的 Informer。这些 Informer,就是 SyncLoop 需要处理 的数据的来源。 

正如图所示,kubelet控制循环中有很多其他的子控制循环(图中的小圈圈)这些控制循环的责任,就是通过控制器模式,完成 kubelet 的某项具体职责。 比如 Node Status Manager,就负责响应 Node 的状态变化,然后将 Node 的状态收集 起来,并通过 Heartbeat 的方式上报给 APIServer。再比如 CPU Manager,就负责维护 该 Node 的 CPU 核的信息,以便在 Pod 通过 cpuset 的方式请求 CPU 核的时候,能够正 确地管理 CPU 核的使用量和可用量。

那么这个 SyncLoop,又是如何根据 Pod 对象的变化,来进行容器操作的呢?

首先注意一下,kubelet和Deployment这些是Kubernetes中两个不同的概念,它们分别用于管理节点上的容器和应用程序的部署。kubelet负责管理节点上的容器,而Deployment负责定义应用程序的部署方式。两者之间没有直接关系。

kubelet 也是通过 Watch 机制,监听了与自己相关的 Pod 对象的变化。当然,这 个 Watch 的过滤条件是该 Pod 的 nodeName 字段与自己相同。kubelet 会把这些 Pod 的信息缓存在自己的内存里。

而当一个 Pod 完成调度、与一个 Node 绑定起来之后, 这个 Pod 的变化就会触发 kubelet 在控制循环里注册的 Handler,也就是上图中的 HandlePods 部分。此时,通过 检查该 Pod 在 kubelet 内存里的状态,kubelet 就能够判断出这是一个新调度过来的 Pod,从而触发 Handler 里 ADD 事件对应的处理逻辑。

具体地 ,kubelet 就会为这个新的 Pod 生成对应的 Pod Status,例如检查 Pod 所声明使用的 Volume 是不是已经准备好。(凡是在 kubelet 里有可能会耗费大量时间的操作,比如准备 Pod 的 Volume、拉取 镜像等,SyncLoop 都会开启单独的 Goroutine 来进行操作。)然后,调用下层的容器运行时(比如Docker),开始创建这个 Pod 所定义的容器。

kubelet 调用下层容器运行时的执行过程,并不会直接调用 Docker 的 API,而是通过一组叫作 CRI(Container Runtime Interface,容器运行时接口)的 gRPC 接口来间接执行的。

之所以使用CRI,实际上是为了屏蔽底层不同容器技术的实现,而kubelet只需要调用CRI接口。具体如下:

当 Kubernetes 通过编排能力创建了一个 Pod 之后,调度器会为这个 Pod 选择 一个具体的节点来运行。这时候,kubelet 当然就会通过前面讲解过的 SyncLoop 来判断 需要执行的具体操作,比如创建一个 Pod。那么此时,kubelet 实际上就会调用一个叫作 GenericRuntime 的通用组件来发起创建 Pod 的 CRI 请求。

 那么,这个 CRI 请求,又该由谁来响应呢?

如果你使用的容器项目是 Docker 的话,那么负责响应这个请求的就是一个叫作 dockershim 的组件。它会把 CRI 请求里的内容拿出来,然后组装成 Docker API 请求发给 Docker Daemon。当然,在将来,dockershim 肯定会被从 kubelet 里移出来,甚至直接被废弃掉。

而更普遍的场景,就是你需要在每台宿主机上单独安装一个负责响应 CRI 的组件,这个组 件,一般被称作 CRI shim。顾名思义,CRI shim 的工作,就是扮演 kubelet 与容器项目 之间的“垫片”(shim)。所以它的作用非常单一,那就是实现 CRI 规定的每个接口,然 后把具体的 CRI 请求“翻译”成对后端容器项目的请求或者操作。

因此,CRI 机制能够发挥作用的核心,就在于每一种容器项目现在都 可以自己实现一个 CRI shim,自行对 CRI 请求进行处理。这样,Kubernetes 就有了一个 统一的容器抽象层,使得下层容器运行时可以自由地对接进入 Kubernetes 当中。

所以说,这里的 CRI shim,就是容器项目的维护者们自由发挥的“场地”了。而除了 dockershim 之外,其他容器运行时的 CRI shim,都是需要额外部署在宿主机上的。

具体的运行过程,我们可以来看看下面这个项目。

当我们执行 kubectl run 创建了一个名叫 foo 的、包括了 A、B 两个容器的 Pod 之 后。这个 Pod 的信息最后来到 kubelet,kubelet 就会按照图中所示的顺序来调用 CRI 接 口。在具体的 CRI shim 中,这些接口的实现是可以完全不同的。比如,如果是 Docker 项目, dockershim 就会创建出一个名叫 foo 的 Infra 容器(pause 容器),用来“hold”住整 个 Pod 的 Network Namespace。接下来,kubelet 继续调用 CreateContainer 和 StartContainer 接口来创建和启动容器 A、B。对应到 dockershim 里,就是直接启动 A,B 两个 Docker 容器。所以最后,宿主 机上会出现三个 Docker 容器组成这一个 Pod。

除此之外,CRI shim还有个重要的功能,那就是实现exec,log等接口,和上述的操作不用,这些gRPC调用接口期间需要建立长链接。这种API我们称为Streaming API 。

CRI shim中Stream API的实现,如下图:

1,当我们执行exec的时候,请求首先发往APIserver。然后APIserver就会调用kubelete的Exec API。

2, kubelete会调用CRI的exec接口,而CRIshim负责相应这个接口。

3,这是CRI shim还不会直接调用后端具体容器项目来处理,而是返回URL的kubelete,这个URL就是CRI shim对应的streaming Server的地址和端口。

4, 而kubelet拿到这个URL后,就会以Redirect的方式返回apisever。所以这时候,apisever就会以重定向的方是向Stream Server 发起真正的请求。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值