Kubernetes架构和核心组件组成 Kubernetes节点“容器运行时”技术分析

kubernetes架构简介
kubernetes架构如下图所示:
在这张系统架构图中,我们把服务分为运行在工作节点上的服务和组成集群级别控制板的服务。kubernetes节点有运行应用容器必备的服务,而这些都是受master的控制。
每次个节点上当然都要运行docker。docker来负责所有具体的映像下载和容器运行。
kubernetes主要由以下几个核心组件组成:
1)etcd保存了整个集群的状态;
2)apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、api注册和发现等机制;
3)controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
4)scheduler负责资源的调度,按照预定的调度策略将pod调度到相应的机器上;
5)kubelet负责维护容器的生命周期,同时也负责volume(cvi)和网络(cni)的管理;
6)container runtime负责镜像管理以及pod和容器的真正运行(cri);
7)kube-proxy负责为service提供cluster内部的服务发现和负载均衡;
而和运行时紧密相关的就是kubelet。
kubelet架构
kubelet架构如下图所示:
kubelet是运行在每个节点上的主要的“节点代理”,每个节点都会启动kubelet进程,用来处理master节点下发到本节点的任务,按照podspec描述来管理pod和其中的容器(podspec是用来描述一个pod的yaml或者json对象)。kubelet通过各种机制(主要通过apiserver)获取一组podspec并保证在这些podspec中描述的容器健康运行。
容器运行时接口(cri)
kubernetes节点的底层由一个叫做“容器运行时”的软件进行支撑,它负责比如启停容器这样的事情。最广为人知的容器运行时当属docker,但它不是唯一的。例如最近比较火热的安全容器katacontainer。所以也就很自然会与有一个需求,就是我们怎么去把katacontainer run在kubernetes里?
那么这个时候我们还是先来看kubelet在做什么事情,所以kubelet要想办法像call docker一样去call katacontainer,然后由katacontainer负责帮忙把hypervisor这些东西set up起来,帮我把这个小vm运行起来。所以这个时候就要需要想怎么让kubernetes能合理的操作katacontainers。
对于这个诉求,就关系到container runtime interface,我们叫它cri。cri的作用其实只有一个:就是它描述了对于kubernetes来说,一个container应该有哪些操作,每个操作有哪些参数,这就是cri的一个设计原理(本质上是一堆ops)。
kubelet与容器运行时通信(或者是cri插件填充了容器运行时)时,kubelet就像是客户端,而cri插件就像对应的服务器。它们之间可以通过unix套接字或者grpc框架进行通信。
protocol buffers api包含了两个grpc服务:imageservice和runtimeservice。imageservice提供了从镜像仓库拉取、查看、和移除镜像的rpc。runtimeserivce包含了pods和容器生命周期管理的rpc,以及跟容器交互的调用(exec/attach/port-forward)。一个单块的容器运行时能够管理镜像和容器(例如:docker和rkt),并且通过同一个套接字同时提供这两种服务。这个套接字可以在kubelet里通过标识–container-runtime-endpoint和–image-service-endpoint进行设置。
下图显示了imageservice和runtimeservice具体需要实现哪些接口。
cri shim
cri shim可以做什么?它可以把cri请求 翻译成runtime api。我举个例子,比如说现在有个pod里有一个a容器和有个b容器,这时候我们把这件事提交给kubernetes之后,在kubelet那一端发起的cri code大概是这样的序列:首先它会run sandbox foo,如果是docker它会起一个infra容器,就是一个很小的容器叫foo,如果是kata它会给你起一个虚拟机叫foo,这是不一样的。
所以接下来你creat start container a和b的时候,在docker里面是起两个容器,但在kata里面是在我这个小虚拟机里面,在这sandbox里面起两个小namespace,这是不一样的。所以你把这一切东西总结一下,你会发现ok,我现在要把kata run在kubernetes里头,所以我要做工作,在这一步要需要去做这个cri shim,我就想办法给kata作一个cri shim。
而我们能够想到一个方式,我能不能重用现在的这些cri shim。重用现在哪些?比如说cri containerd这个项目它就是一个containerd的cri shim,它可以去响应cri的请求过来,所以接下来我能不能把这些情况翻译成对kata这些操作,所以这个是可以的,这也是我们将用一种方式,就是把katacontainers接到我的containerd后面。这时候它的工作原理大概这样这个样子,containerd它有一个独特设计,就是他会为每一个contaner起个叫做contained shim。你run一下之后你会看他那个宿主机里面,会run一片这个containerd shim一个一个对上去。
而这时候由于kata是一个有sandbox概念的这样一个container runtime,所以kata需要去match这些shim与kata之间的关系,所以kata做一个katashim。把这些东西对起来,就把你的contained的处理的方式翻译成对kata的request,这是我们之前的一个方式。
但是你能看到这其实有些问题的,最明显的一个问题在于对kata或gvisor来说,他们都是有实体的sandbox概念的,而有了sandbox概念后,它就不应该去再去给他的每一个container启动有一个shim match起来,因为这给我们带来很大的额外性能损耗。我们不希望每一个容器都去match一个shim,我们希望一个sandbox match一个shim。
另外,就是你会发现cri是服务于kubernetes的,而且它呈现向上汇报的状态,它是帮助kubernetes的,但是它不帮助container runtime。所以说当你去做这个集成时候,你会发现尤其对于vm gvisorkatacontainer来说,它与cri的很多假设或者是api的写法上是不对应的。所以你的集成工作会比较费劲,这是一个不match的状态。
最后一个就是我们维护起来非常困难,因为由于有了cri之后,比如redhat拥有自己的cri实现叫cri-o(基于open container initiative的kubernetes container runtime interface的实现),他们和containerd在本质上没有任何区别,跑到最后都是靠runc起容器,为什么要这种东西?
我们不知道,但是我作为kata maintainer,我需要给他们两个分别写两部分的integration把kata集成进去。这就很麻烦,者就意味着我有100种这种cri我就要写100个集成,而且他们的功能全部都是重复的。
containerd shimv2
为了解决以上的shim问题,引入了shimv2。前面我们说过cri,cri决定的是runtime和kubernetes之间的关系,那么我们现在能不能再有一层更细致的api来决定我的cri shim跟下面的runtime之间真正的接口是什么样的?
这就是shimv2出现的原因,它是一层cri shim到containerd runtime之间的标准接口,所以前面我直接从cri到containerd到runc,现在不是。我们是从cri到containerd到shimv2,然后shimv2再到runc再到katacontainer。这么做有什么好处?
最大的区别在于:在这种方式下,你可以为每一个pod指定一个shim。因为在最开始的时候,containerd是直接启动了一个containerd shim来去做响应,但我们新的api是这样写的,是containerd shim start或者stop。所以这个start和stop操作怎么去实现是你要做的事情。
例如katacontainers项目可以这么实现:在created sandbox的时候call这个start的时候,我启动一个containerd shim。但是当我下一步是call api的时候,就前面那个cri里面,container api时候,我就不再起了,我是reuse,我重用为你创建好的这个sandbox,这就位你的实现提供了很大的自由度。
所以这时候你会发现整个实现的方式变了,这时候containerd用过来之后,它不再去care每个容器起containerd shim,而是由你自己去实现。我的实现方式是我只在sandbox时候,去创建containerd-shim-v2,而接下来整个后面的container level操作,我会全部走到这个containerd-shim-v2里面,我去重用这个sandbox,所以这个跟前面的时间就出现很大的不同。如下图所示是kata1.5中采用shim v2的实现。
首先,你还是用原来的cri containerd,只不过现在装的是runc,你现在再装一个katacontainer放在那机器上面。接下来我们kata那边会给你写一个实现叫kata-containerd-shimv2。所以前面要写一大坨cri的东西,现在不用了。现在,我们只focus在怎么去把containerd对接在kata container上面,就是所谓的实现shimv2 api,这是我们要做的工作。而具体到我们这要做的事情上,其实它就是这样一系列与run一个容器相关的api。
比如说我可以去create、start,这些操作全部映射在我shimv2上面去实现,而不是说我现在考虑怎么去映射,去实现cri,这个自由度由于之前太大,造成了我们现在的一个局面,就有一堆cri shim可以用。这其实是一个不好的事情。有很多政治原因,有很多非技术原因,这都不是我们作为技术人员应该关心的事情,你现在只需要想我怎么去跟shimv2对接就好了。
容器运行时总结
下图显示了当前主要的容器运行时和主要维护者。

车联网应用凸显汽车防盗功能
三星大中华区换帅 或因不满电视机市场表现
人机界面编程用什么语言 怎么用c++编写人机界面程序
一般在设计中双面板是先走信号线还是先走地线?
乐普医疗在心血管医疗器械领域国际化创新发展宏图之路
Kubernetes架构和核心组件组成 Kubernetes节点“容器运行时”技术分析
【设计技巧】你掌握了吗?在PCB设计中,又快又准地放置元件
FPGA有什么优势,可以让FPGA替代GPU吗
3dtouch技术原理_3dtouch是怎么实现的?
想做好电路板,一定要知道的软硬结合板设计要点
电感线圈是什么,它的用途都有哪些
思歌锁业科技A412-DAC/DAB/CG门锁介绍
新型全能电源的特点和应用
TI推出业内首款集成四通道和双通道射频采样收发器,实现多天线宽带系统
CMOS型单片机时钟电路图
H.264/AVC中量化的Verilog方法介绍及实现
小米集团公布新一轮组织架构调整
半导体企业兆易创新发布2021年报
已有两款iPhone被测出辐射超标
Agilent N7744A多端口光功率计的性能技术指标