docker,containerd,runc,docker-shim 之间的关系
2020-10-09 tech docker 7 mins 7 图 2480 字
在看系统进程的时候,发现了这样的进程关系:
systemd 里调用了
- containerd -> containerd-shim
- dockerd -> docker-proxy
引起了我的兴趣。
ps: 另外上边只是在 docker 19.03 上发现了这样的调用关系,在 docker 17.06 上调用的关系实际上是这样:
-dockerd --registry-mirror=http:/xxx | -docker-containe -l unix:///xxx | -docker-containe | -docker-proxy -proto xxx
在 19.03 版本中,docker相关的可执行文件如下:
其中以docker开头的,docker, dockerd, docker-init, docker-proxy 是 docker 公司专属的,并非标准。
因为 docker 一直在开发中,网上很多资料都比较陈旧,不过八九不离十,无非是几个组件变了个名字:
- docker,是一个客户端工具,用来把用户的请求发送给 docker daemon(dockerd)。
- dockerd, docker daemon,一般也会被称为 docker engine。dockerd 启动时会启动 containerd 子进程。
- Containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性,几乎囊括了单机运行一个容器运行时所需要的一切:执行,分发,监控,网络,构建,日志等。主要作用是:
- 管理容器的生命周期(从创建容器到销毁容器)
- 拉取/推送容器镜像
- 存储管理(管理镜像及容器数据的存储)
- 调用 runC 运行容器(与 runC 等容器运行时交互)
- 管理容器网络接口及网络
- ctr 是 containerd 的 cli。
- 为了能够支持多种 OCI Runtime,containerd 内部使用
containerd-shim
,每启动一个容器都会创建一个新的containerd-shim
进程,指定容器 ID,Bundle 目录,运行时的二进制(比如 runc)。 - RunC 是一个轻量级的工具,用来运行容器的,我们可以不用通过 docker 引擎,直接运行容器。事实上,runC 是标准化的产物,它根据 OCI 标准来创建和运行容器。
调用链主要就是下面这张图:
docker历史
至于容器架构为什么做的这么复杂,个人认为是技术、政治和历史等因素共同的结果。这里简单记录一下docker的历史:
- 自从 2013 年 docker 发布。
- 为了能够降低项目维护的成本,内部代码能够回馈社区,docker 公司提出了 “基础设施管道宣言” (Infrastructure Plumbing Manifesto),自行拆分自己项目中的管道代码并形成一个个新的开源项目:libcontainer, libnetwork, notary, hyperkit。
- 2015年 OCI 项目启动(开放容器标准),OCI 的技术委员会成员包括 Red Hat,Microsoft,Docker,Cruise,IBM,Google,Red Hat 和 SUSE,其中 Docker 公司有两名成员,且其中的一位是现任主席,具体的细节可以查看 OCI Technical Oversight Board。
- docker 公司将
libcontainer
的实现移动到 runC 并捐赠给了 OCI。runC 包括了所有之前 docker 所使用的容器相关的与系统特性的代码。 - 2016 年,docker 开源并将 containerd 捐赠给了 CNCF。
与k8s相关
在容器标准的大战中,docker公司围绕docker swarm推出了CNM,Google等以屠龙者的姿态围绕 k8s 推出了CNI,目前来看,k8s已经奠定了在 PaaS 事实的地位。
CRI 是一套通过 protocol buffers 定义的 API,如下图:
kubelet 实现了 client 端,CRI shim 实现 server 端。只要实现CRI对应的接口,就能接入 k8s 作为 Container Runtime。
-
k8s 1.5 中自己实现了 docker CRI shim,此时启动容器的流程如下:
-
从 containerd 1.0 开始,为了能够减少一层调用的开销(废掉docker,也就是把上边的docker cri shim和docker踢掉),containerd 开发了一个新的 daemon,叫做 CRI-Containerd,直接与 containerd 通信,从而取代了 dockershim:
-
但是这仍然多了一个独立的 daemon,从 containerd 1.1 开始,社区选择在 containerd 中直接内建 CRI plugin,通过方法调用来进行交互,从而减少一层 gRPC 的开销,最终的容器启动流程如下:
最终的结果是 k8s 的 Pod 启动延迟得到了降低,CPU 和内存占用率都有不同程度的降低。
-
但是这还不是终点,为了能够直接对接 OCI 的 runtime 而不是 containerd,社区孵化了 CRI-O 并加入了 CNCF。CRI-O 的目标是让 kubelet 与运行时直接对接,减少任何不必要的中间层开销。CRI-O 运行时可以替换为任意 OCI 兼容的 Runtime,镜像管理,存储管理和网络均使用标准化的实现。
@xuxinkun 的文章中有个图将他们之间的关系描绘的很清楚:
以下更新于2021年4月9日
关于Mesos
mesos于前天发起了关闭