docker各进程的交互

docker各进程的交互

                           +------------+
                              |            |
                              | Docker Hub |
                              |            |
                              +------------+
                                    
                                    |
                                  2 | REST
                                    |
                                    
                               +---------+
+--------+       REST          |         |    grpc      +-------------------+
| docker |<------------------->| dockerd |<------------>| docker-containerd |
+--------+         1           |         |      3       +-------------------+
                               +---------+                       
                                                                 |
                                                                 | 4
                                                                 
                                                      +------------------------+  5   +-------------+
                                                      | docker-containerd-shim |<---->| docker-runc |
                                                      +------------------------+      +-------------+
                                                                                             
                                                                                             | 6
                                                                                             
                                                                                         +-------+
                                                                                         | hello |
                                                                                         +-------+

步骤过程
1. docker <–> dockerd
docker进程是docker客户端,dockerd进程是docker服务器端,它们的代码都在moby项目里面。

当在shell里面运行docker run hello-world后,docker程序被启动,这个程序就是docker的客户端,它的任务就是解析命令行参数,然后构造相应的启动容器请求,通过rest的方式发给dockerd。

Engine API里描述了dockerd支持的所有请求,docker v17.03.0对应的API版本为v1.26,而docker v17.03.1对应的版本为v1.27,API版本之间的差别可以参考version-history。

  1. dockerd <–> “docker hub”
    当dockerd收到客户端的运行容器请求后,发现本地没有相应的镜像(image),就会从docker hub取相应image。(实际过程要比这个步骤多,这里为了简单直观,省略掉了其它的步骤,后面有详细的说明)

docker hub是docker官方存放镜像(image)的服务器,dockerd和它之间也是使用rest接口,协议为Registry HTTP API V2。

取image的大概过程如下:

首先获取image的manifests,manifests里面包含两部分内容,一是image的配置文件的digest(sha256),另一个是image包含的所有filesystem layer的digest(sha256)

根据上一步得到的image的配置文件的digest,在本地找是否已经存在对应的image,如果已经存在的话,就不用再往下走了,用现成的就可以了,如果没有,则继续

遍历manifests里面的所有layer,根据其digest在本地找,如果找到对应的layer,则跳过当前layer,否则从服务器取相应layer的压缩包

等上面的所有步骤完成后,就会拼出完整的image

这里的每个layer都是相对于上一个文件系统layer的变化情况。
从上面的过程可以看出,docker只会拉取本地没有的layer。

  1. dockerd <–> docker-containerd
    dockerd拿到image后,就会在本地创建相应的容器,然后再做一些初始化工作后,最后通过grpc的方式通知docker-containerd进程启动指定的容器

docker-containerd是和dockerd一起启动的后台进程,他们之间使用unix socket通信,协议是grpc.

  1. docker-containerd <–> docker-containerd-shim
    docker-containerd和docker-containerd-shim都属于containerd项目,当docker-containerd收到dockerd的启动容器请求之后,会做一些初始化工作,然后启动docker-containerd-shim进程,并将相关配置所在的目录作为参数传给它。

可以简单的理解成docker-containerd管理所有本机正在运行的容器,而docker-containerd-shim只负责管理一个运行的容器,相当于是对runc的一个包装,充当containerd和runc之间的桥梁,runc能干的就交给runc来做,runc做不了的就放到这里来做。

  1. docker-containerd-shim <–> docker-runc
    docker-containerd-shim进程启动后,就会按照runtime的标准准备好相关运行时环境,然后启动docker-runc进程。

docker-runc就是runc程序的重命名,它们是相等的,若无特殊情况,后面介绍中不区分docker-runc和runc

image和runtime标准都由Open Container Initiative(OCI)负责定义维护,而runc就是docker贡献给OCI的一个标准runtime实现。

如何启动runc是公开的标准,大概过程就是准备好rootfs和配置文件,然后使用合适的参数启动runc进程就可以了。

  1. docker-runc <–> hello
    runc进程打开容器的配置文件,找到rootfs的位置,并启动配置文件中指定的相应进程,在hello-world的这个例子中,runc会启动容器中的hello程序。

到此为止,容器启动成功。

进程间的关系
等runc将容器启动起来后,runc进程就退出了,于是容器里面的第一个进程(hello)的父进程就变成了docker-containerd-shim,在pstree的输出里面,进程树的关系大概如下:

systemd───dockerd───docker-containerd───docker-containerd-shim───hello
实际操作过程中可能看不到这样的输出,因为hello很快就运行退出了,接着docker-containerd-shim也退出了。

其中dockerd和docker-containerd是后台常驻进程,而docker-containerd-shim则由docker-containerd按需启动。

runc退出后其子进程hello不是应该由init进程接管吗?怎么就变成了docker-containerd-shim的子进程了呢?这是因为从Linux 3.4开始,prctl增加了对PR_SET_CHILD_SUBREAPER的支持,这样就可以控制孤儿进程可以被谁接管,而不是像以前一样只能由init进程接管。

输出
hello进程启动之后,往标准输出打印一段话后就退出了,那这个标准输出输出到哪里去了呢?docker客户端是怎么得到这段话的呢?这就取决于docker将这个标准输出重定向到哪里去了,以及它是怎么管理容器的这些输出的,这涉及到docker的日志管理方式,该部分内容会在后续做详细介绍,这里只需要知道容器的标准输出的内容能被docker的这些进程一层一层的转发给客户端就行了。

详细步骤
上面介绍的是一个精简版的hello world运行步骤,实际过程要多几个回合,下面用curl命令来模拟一下实际的操作流程:

这里假设已经配置了dockerd监听本地tcp端口2375

请求dockerd创建容器,但由于dockerd在本地找不到相应的image,于是返回失败
dev@debian:~$ curl 127.0.0.1:2375/v1.27/containers/create -X POST -H “Content-Type: application/json” -d ‘{“Image”: “hello-world”}’
{“message”:”No such image: hello-world:latest”}

请求dockerd去找registry服务器拿image
这里的http应答中的内容包含了很多跟进度条相关的内容
dev@debian:~$ curl ‘127.0.0.1:2375/v1.27/images/create?fromImage=hello-world&tag=latest’ -X POST
{“status”:”Pulling from library/hello-world”,”id”:”latest”}
{“status”:”Pulling fs layer”,”progressDetail”:{},”id”:”78445dd45222”}
{“status”:”Downloading”,”progressDetail”:{“current”:971,”total”:971},”progress”:”[==================================================\u003e] 971 B/971 B”,”id”:”78445dd45222”}
{“status”:”Verifying Checksum”,”progressDetail”:{},”id”:”78445dd45222”}
{“status”:”Download complete”,”progressDetail”:{},”id”:”78445dd45222”}
{“status”:”Extracting”,”progressDetail”:{“current”:971,”total”:971},”progress”:”[==================================================\u003e] 971 B/971 B”,”id”:”78445dd45222”}
{“status”:”Extracting”,”progressDetail”:{“current”:971,”total”:971},”progress”:”[==================================================\u003e] 971 B/971 B”,”id”:”78445dd45222”}
{“status”:”Pull complete”,”progressDetail”:{},”id”:”78445dd45222”}
{“status”:”Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7”}
{“status”:”Status: Downloaded newer image for hello-world:latest”}

再次创建容器成功,得到容器ID
dev@debian:~$ curl 127.0.0.1:2375/v1.27/containers/create -X POST -H “Content-Type: application/json” -d ‘{“Image”: “hello-world”}’
{“Id”:”2a4717ffb830bf4cff12ef6e6f1e93129970df273387797fd023e10292e3e928”,”Warnings”:null}

attach到容器的标准输出,curl程序会暂停在这里,等待容器的输出
dev@debian:~$ curl ‘127.0.0.1:2375/v1.27/containers/2a4717ffb830bf4cff12ef6e6f1e93129970df273387797fd023e10292e3e928/attach?stderr=1&stdout=1&stream=1’ -d ‘{“Connection”: “Upgrade”, “Upgrade”:”tcp”}’
等下一步容器启动后,在这里可以看到容器的输出

另外打开一个shell窗口,启动容器
dev@debian:~$ curl 127.0.0.1:2375/v1.27/containers/2a4717ffb830bf4cff12ef6e6f1e93129970df273387797fd023e10292e3e928/start -X POST
从上面的curl命令可以看出,在hello-world这个场景中,docker客户端主要发送了四个请求给dockerd,首先创建image,然后创建容器,接着attach标准输出,最后启动容器。(attach涉及到标准输入输出重定向,这里不细说)

这里是它们之间的交互流程:

                           +---------+

+——–+ | |
| | 1.create container | |
| |——————–>| |
| | | |
| | 2.image not found | |
| |<——————–| |
| | | |
| | 3.create image | |
| |——————–>| |
| | | |
| | 4.image created | |
| |<——————–| |
| docker | | dockerd |
| | 5.create container | |
| |——————–>| |
| | | |
| | 6.container id | |
| |<——————–| |
| | | |
| | 7.start container | |
| |——————–>| |
| | | |
| | 8.container started | |
| |<——————–| |
+——–+ | |
+———+
流程对应的文字描述如下:

客户端发送创建容器请求给dockerd,dockerd收到请求后,发现本地没有相应的额image,于是返回失败。

客户端收到失败的响应后,立即发送创建image的请求过来,dockerd收到后,会去docker hub上拿相应的image,拿到后返回成功。

客户端再次发送创建容器请求给dockerd,dockerd收到会根据拿到的image创建一个新容器,并初始化容器运行时要用到的相关目录和配置文件,里面就包含了rootfs,容器创建完成后,dockerd返回容器的ID给客户端。

客户端发启动容器请求给dockerd,dockerd收到请求后,会通知docker-containerd启动容器,启动成功后返回成功给客户端。

dockerd去docker hub上取image发生在这里的3~4步之间,而docker-containerd───docker-containerd-shim───hello这些事发生在这里的7~8步之间。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值