Docker 组件之间的关系

参考文献:
https://www.cnblogs.com/sparkdev/p/9129334.html
https://segmentfault.com/a/1190000009309297
https://www.codercto.com/a/47652.html

1、Docker的主要组件 有哪些

  • docker cli , 就是 docker命令

  • dockerd, 俗称,docker引擎

  • docker-init

  • docker-proxy

  • docker-containerd, 就是containerd

  • docker-containerd-shim, 就是containerd-shim

  • docker-containerd-ctr, 就是ctr

  • docker-runc, 就是 runc

像docker cli, dockerd, docker-init, docker-proxy 应该是docker 公司 专属的,并非标准;

image

2、Docker CLI (docker)

docker 程序是一个客户端工具,用来把用户的请求发送给 docker daemon(dockerd)。该程序的安装路径为:

/usr/bin/docker

3、Dockerd

docker daemon(dockerd),一般也会被称为 docker engine。该程序的安装路径为:

/usr/bin/dockerd

4、Containerd

详情请参考《Containerd到底是干啥的?》(https://www.jianshu.com/p/5ca02db248ee)。该程序的安装路径为:

/usr/bin/docker-containerd

5、Containerd-shim

它是 containerd 的组件,是容器的运行时载体,
我们在 docker 宿主机上看到的 shim 也正是代表着一个个通过调用 containerd 启动的 docker 容器。
该程序的安装路径为:

/usr/bin/docker-containerd-shim

6、RunC

详情请参考《RunC到底是干啥的》(https://www.jianshu.com/p/2f6296190049)。
该程序的安装路径为:

/usr/bin/docker-runc

7、从hello world 开始

Docker 很贴心的为我们提供了 hello-world 镜像来验证安装是否成功,但是透过这个镜像我们还能看到更多的信息:

docker run hello-world
image

上面的输出信息指出,hello-world 容器的运行经历了如下四步:

  • 1.Docker 客户端向docker daemon发送请求

  • 2.Docker daemon 从 Docker Hub 上拉取镜像

  • 3.Docker daemon 使用镜像运行了一个容器并产生了输出

  • 4.Docker daemon 把输出的内容发送给了 docker 客户端

这是一个很抽象也很容器理解的过程,但是我们还想知道更多:
docker daemon 是如何创建并运行容器的
其实容器部分的操作管理都被 dockerd 外包给 containerd 了,
下图描述了运行一个容器时各个组件之间的关系:

image

8、Docker Engine API

https://github.com/moby/moby/tree/master/api

从本质上说,docker 是一个客户端/服务器架构的应用。
Dockerd 以 Engine API (REST)的方式对外提供服务,Engine API 里描述了 dockerd 支持的所有请求。
Docker 客户端与 dockerd 之间就是通过 REST 的方式通信的。
在 centos7中,dockerd 默认是不监听 tcp 端口的,为了方便演示,我们让 dockerd 监听 tcp 端口。
这样就可以使用 curl 代替 docker 客户端向 dockerd 发送请求了。
具体的操作为,先修改 /lib/systemd/system/docker.service 文件,
注释掉默认的 ExecStart 并添加新的 ExecStart 配置:

# ExecStart=/usr/bin/dockerd -H fd://
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock`

image

然后重启 docker.service:

systemctl daemon-reload
systemctl restart docker.service
image

9、Docker 与 Dockerd的交互

Docker 客户端与 dockerd 之间就是通过 REST的方式通信的。
前面我们已经让 dockerd 监听 tcp 端口了,所以我们可以使用 curl 来代替 docker 客户端。
这里我们简单的演示如何请求 dockerd 从 docker hub 上下载 hello-world 镜像:

curl '127.0.0.1:2375/v1.37/images/create?fromImage=hello-world&tag=latest' -X POST
image

10、创建容器

  • 容器镜像的下载是由 dockerd 完成的,但容器的创建和运行就需要 containerd(docker-containerd) 来完成了。 //容器镜像的下载,难道不是由containerd完成的么

  • Dockerd 与 docker-containerd 之间是通过grpc协议通信的。
    当 docker-containerd 收到 dockerd 启动容器的请求之后,会做一些初始化工作,然后启动 docker-containerd-shim进程,并将相关配置作为参数传给它。

  • docker-containerd 负责管理所有本机正在运行的容器,而一个 docker-containerd-shim 进程只负责管理一个运行的容器,它相当于 docker-runc 的一个封装,充当 docker-containerd 和 docker-runc 之间的桥梁,docker-runc 能干的就交给 docker-runc 来做,docker-runc 做不了的就放到这里来做。

下面我们用 ubuntu 镜像运行一个容器:

docker run -id busybox bash

image

上图中黄线框起来的是几个主要的进程,它们之间是有父子关系的(systemd 没有出现在上图):

systemd---dockerd---docker-containerd---docker-containerd-shim---bash

上图中没有出现 docker-runc 进程,这是为什么呢?

实际上,在容器启动的过程中,docker-runc 进程是作为 docker-containerd-shim 的子进程存在的。
docker-runc 进程根据配置找到容器的 rootfs 并创建子进程 bash 作为容器中的第一个进程。
当这一切都完成后 docker-runc 进程退出,然后容器进程 bash 由 docker-runc 的父进程 docker-containerd-shim 接管。

11、为啥需要docker-containerd-shim?

也许大家会问,为什么在容器的启动运行过程中需要一个 docker-containerd-shim 进程呢?把它移除掉整个架构会更简洁也更优美一些!事实上 docker-containerd-shim 的存在是非常有必要的,其目的有如下几点:

  • 它允许容器运行时(即 runC)启动容器之后退出,简单说就是不必为每个容器一直运行一个容器运行时(runC);
  • 即使在 containerd 和 dockerd 都挂掉的情况下,容器的标准 IO 和其它的文件描述符也都是可用的
  • 向 containerd 报告`容器的退出状态``

前两点尤其重要,有了它们就可以在不中断容器运行的情况下升级重启 dockerd(这对于生产环境来说意义重大)。
从这里可以看到对 containerd-shim 的一些解释。

或者按照下面的方式

  • 允许runc在创建&运行容器之后退出

  • 用shim作为容器的父进程,而不是直接用containerd作为容器的父进程,是为了防止这种情况:当containerd挂掉的时候,shim还在,因此可以保证容器打开的文件描述符不会被关掉

  • 依靠shim来收集&报告容器的退出状态,这样就不需要containerd来wait子进程

使用shim的主要作用?
就是将containerd和真实的容器(里的进程)解耦,这是第二点和第三点所描述的。
而第一点,为什么要允许runc退出呢?
因为,Go编译出来的二进制文件,默认是静态链接,因此,如果一个机器上起N个容器,那么就会占用M*N的内存,其中M是一个runc所消耗的内存
但是出于上面描述的原因又不想直接让containerd来做容器的父进程,因此,就需要一个比runc占内存更小的东西来作父进程,也就是shim。
但实际上, shim仍然比较占内存( 参考这里 ),因此,比较好的方式是:

  • 用C重写并且默认使用动态链接库
  • 打开 Go 的动态链接支持然后重新编译

12、docker-init

我们都知道UNIX系统中,1号进程是init进程,也是所有孤儿进程的父进程。
而使用docker时,如果不加 --init 参数,容器中的1号进程 就是所给的ENTRYPOINT,例如下面例子中的 sh 。
而加上 --init 之后,1号进程就会是 tini :

[email protected]:~$ docker run -it busybox sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    6 root      0:00 ps aux
/ # exit
[email protected]:~$ docker run -it --init busybox sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /dev/init -- sh
    6 root      0:00 sh
    7 root      0:00 ps aux
/ # exit

13、docker-proxy

这个是用来做端口映射的, 从名称中就可以看出来,下面验证一下:

[email protected]:~$ docker run -d -p 10010:10010 busybox sleep 10000
be88279118ad7f8cfd3d418db00872aa4f3b1753278b67c28727f16d68f37ae5
[email protected]:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                      NAMES
be88279118ad        busybox             "sleep 10000"       2 seconds ago       Up 1 second         0.0.0.0:10010->10010/tcp   awesome_jackson
[email protected]:~$ ps aux | grep docker
root        897  0.1  3.8 736592 78444 ?        Ssl  06:20   0:33 /usr/bin/dockerd -H fd://
root       1188  0.0  1.8 665876 37964 ?        Ssl  06:20   0:25 docker-containerd --config /var/run/docker/containerd/containerd.toml
root       5579  0.0  0.1 378868  3076 ?        Sl   14:57   0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10010 -container-ip 172.17.0.2 -container-port 10010
root       5585  0.0  0.1   7376  3808 ?        Sl   14:57   0:00 docker-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/be88279118ad7f8cfd3d418db00872aa4f3b1753278b67c28727f16d68f37ae5 -address /var/run/docker/containerd/docker-containerd.sock -containerd-binary /usr/bin/docker-containerd -runtime-root /var/run/docker/runtime-runc
jiajun     5666  0.0  0.0  13136  1076 pts/0    S+   14:57   0:00 grep --color=auto docker

可以看到这么一行

/usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 10010 -container-ip 172.17.0.2 -container-port 10010

其底层是使用iptables来完成的,参考:
https://windsock.io/the-docker-proxy/

12、总结

本文则通过 demo 演示了在创建、运行容器的过程中这些组件如何配合 docker engine 完成相关的任务,以及相关进程之间的关系和作用。
希望本文可以帮助大家理解 docker 的整体架构及其组件间的协作方式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342