k8s之docker

Docker介绍

最近听闻 K8s 弃用dockershim,一个从事k8s开发的工作人员不懂这是什么意思是不行的,所以好好梳理梳理下。

一、docker原理
关于docker1.12.x,该版本的docker由docker-client,dockerd,containerd,docker-shim,runc组成
dockerd:本身实属是对容器相关操作的api的最上层封装,直接面向操作用户。(http访问)
containerd:dockerd实际真实调用的还是containerd的api接口(rpc方式实现),containerd是dockerd   

和runc之间的一个中间交流组件。
docker-shim(注意不是dockershim):一个真实运行的容器的真实垫片载体,每启动一个容器都会起一    

个新的docker-shim的一个进程。他直接通过指定的三个参数:容器id,boundle目录

(containerd的对应某个容器生成的目录,一般位于:

/var/run/docker/libcontainerd/containerID),运行是二进制(默认为runc)来调用runc

的api创建一个容器(比如创建容器:最后拼装的命令如下:runc create 。。。。。)
runc一个命令行工具端,他根据oci(开放容器组织)的标准来创建和运行容器。

runtime是容器真正运行的地方。runtime 需要跟操作系统kernel紧密协作,为容器提供运

行环境,lxc、runc 和 rkt 是目前主流的三种容器 runtime。
他们之间的关系如下图:

 k8s之docker

 

 

 

二、k8s如何使用docker

k8s之docker

 

本次改动主要内容是准备删除 kubelet 中的 dockershim,当然这种做法也是符合预期的。在早期 rkt 和 docker 争霸时,kubelet 中需要维护两坨代码分别来适配docker和rkt,这使得 kubelet 每次发布新功能都需要考虑对运行时组件的适配问题,严重拖慢了新版本发布速度。另外虚拟化已经是一个普遍的需求,如果出现了类型的运行时,SIG-Node 小组可能还需要把和新运行时适配的代码添加到 kubelet 中。这种做法并不是长久之计,于是在 2016 年,SIG-Node提出了容器操作接口 CRI(Container Runtime Interface)。 CRI 是对容器操作的一组抽象,只要每种容器运行时都实现这组接口,kubelet 就能通过这组接口来适配所有的运行时。但 Docker 当时并没有(也不打算)实现这组接口, kubelet 只能在内部维护一个称之为“dockershim”组件,这个组件充当了 docker 的 CRI 转接器,kubelet 在创建容器时通过 CRI 接口调用 dockershim ,而 dockershim 在通过 http 请求把请求交给 docker 。

在使用实现了 CRI 接口的组件作为容器运行时的情况下,kubelet 创建容器的调用链如图中红色箭头所示,kubelet 中的 ContainerManager (其实我觉得这里是runtimeManager)可以直接通过 CRI 调用到容器运行时,这过程中只需要一次 grpc 请求;而在使用docker时,runtimeManager会走图中蓝色的调用链,CRI 的请求通过 unix:///var/run/dockershim.sock 流向 dockershim,dockershim 做转换后把请求转发给 docker。这种做法让调用链变长且不稳定性,不优雅,还给 kubelet 的维护添加了额外工作,把这部分内容从 kubelet 删掉就是时间问题了。

containerd 才是 docker 被抛弃后的 CRI 运行时的最佳人选,cri-o适配工作量大。对于开发同学来说整个迁移过程应该是无感知的,只需要修改kubelet的配置--container-shim,将docker换成conntainerd.

 

三、containerd 的今生前世

前面知道了docker运行的原理,k8s通过docker-shim调用docker和直接调用containerd,为什么最开始不直接调用containerd呢?讲一下历史。

2016年,docker 把负责容器生命周期的模块拆分出来,并将其捐赠给了社区,也就是现在的 containerd。docker拆分后结构如下图所示(当然 docker 公司还在 docker 中添加了部分编排的代码)

 k8s之docker

在我们调用docker命令创建容器后,docker daemon 会通过 Image 模块下载镜像并保存到 Graph Driver 模块中,之后通过 client 调用containerd 创建并运行容器。我们在使用 docker 创建容器时可能需要使用--volume给容器添加持久化存储;还有可能通过--network连接我们用 docker 命令创建的几个容器,当然,这些功能是 docker 中的 Storage 模块Networking 模块提供给我们的。但 K8s 提供了更强的卷挂载能力和集群级别的网络能力,在集群中 kubelet 只会使用到 docker 提供的镜像下载和容器管理功能,而编排、网络、存储等功能都不会用到。下图中可以看出当前的模式下各模块的调用链,同时图中被红框标注出的几个模块就是 kubelet 创建 Pod 时所依赖的几个运行时的模块。

 k8s之docker

containerd 被捐赠给CNCF社区后,社区给其添加了镜像管理模块和 CRI 模块,这样 containerd 不只可以管理容器的生命周期,还可以直接作为 K8s 的运行时使用。于是 containerd 在 2019年2月从 CNCF 社区毕业,正式进入生产环境。下图中能看出以 containerd 作为容器运行时,可以给 kubelet 带来创建 Pod 所需的全部功能,同时还得到了更纯粹的功能模块以及更短的调用链。

 k8s之docker

从上面的对比可以看出从 containerd 被捐赠给社区开始,就一直以成为简单、稳定且可靠的容器运行时为目标;而docker则是希望能成为一个完整的产品。官方文档中也提到了这一点,docker为了给用户更好的交互和使用体验以及更多的功能,提供了很多开发人员所需要的特性,同时为了给 swarm 做基础,提供了网络和卷的功能。而这些功能其实都是是 K8s 用不上的;containerd 则相反,仅提供了 kubelet 创建 Pod 所需要的基础功能,当然这换来的就是更高的鲁棒性以及更好的性能。在一定程度上讲,即使在 kubelet 1.23 版本之后 docker 提供了CRI接口,containerd仍然是更好的选择。

 

参考:https://www.cnblogs.com/tencent-cloud-native/p/14134164.html 

上一篇:k8s二进制安装-5,安装kube-apiserver


下一篇:Oracle根据时间恢复已删除提交的数据