Docker Run的背后流程
如果你想启动一个nginx容器,你可能会在命令行输入以下命令
➜ ~ docker run -d nginx
然后满怀期望等待奇迹发生,结果看到输出
f021105c2da60f1f3930070e917668c7ffcabc45e2cf64c622a2d68f216d0c2b
注意
-d, --detach Run container in background and print container ID
然后我们发现系统里面多了一个nginx容器,这个过程的背后究竟发生了什么?我们来一探究竟
docker
docker - Docker image and container command line interface
docker是镜像和命令行接口
docker本身其实是一个C/S模型的架构
它主要由三部分组成
- 一个Server服务端作为后台守护进程
- 跟Server端通信的REST API
- 一个Client客户端工具也就是我们上面用到的
docker
run
这个命令是告诉docker
: 我要新创建一个容器, nginx
代表这个容器的启动镜像
第一个接口请求
HEAD /_ping HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
请求响应
HTTP/1.1 200 OK
Api-Version: 1.40
Cache-Control: no-cache, no-store, must-revalidate
Content-Length: 0
Content-Type: text/plain; charset=utf-8
Date: Fri, 08 Nov 2019 05:40:58 GMT
Docker-Experimental: false
Ostype: linux
Pragma: no-cache
Server: Do
从功能上来讲,是一个探活接口,看看服务端是否正在运行
创建镜像
服务端运行正常,接下来可以发起创建镜像的请求
POST /v1.40/containers/create HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
Content-Length: 1514
Content-Type: application/json
{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false"
...
事实上我是使用以下dtruss
命令来跟踪docker的执行进程
dtruss -saL docker run -d nginx 2> ~/Desktop/dockerrun.log
是的,没错,就是大名鼎鼎的DTrace的Mac版,(但是打印出来的POST请求内容不全,目前还不知道如何完整打印,猜测应该是参数过多,尴尬。)
HTTP/1.1 404 Not Found
Api-Version: 1.40
Content-Length: 42
Content-Type: application/json
Date: Fri, 08 Nov 2019 05:40:58 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
{"message":"No such image: nginx:latest"}
从响应结果看客户端向服务器查询是否有nginx:latest
的镜像存在,然而并没有
系统信息
接下来有一次系统信息的接口调用
GET /v1.40/info HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
响应如下
HTTP/1.1 200 OK
Api-Version: 1.40
Content-Type: application/json
Date: Fri, 08 Nov 2019 05:40:58 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
Transfer-Encoding: chunked
914
{"ID":"A2GJ:TQBK:V4H5:GFXT:KGB7:QRV5:GWJ7
...
这一步是获取系统信息,比如docker镜像的根目录这些,为镜像下载做准备(虽然多数情况下docker的C/S是在一台机器上,但是这种设计本身就具有比较好的扩展性,C、S两端可以分离,这跟我们本地使用MySQL的客户端服务端都是在本地一样)
镜像下载
HTTP/1.1 200 OK
Api-Version: 1.40
Content-Type: application/json
Date: Fri, 08 Nov 2019 05:41:02 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
Transfer-Encoding: chunked
37
{"status":"Pulling from library/nginx","id
...
当然这之前自然少不了读取本地docker的配置文件,获取镜像仓库地址(默认为:index.docker.io/v1),然后开始下载,直到下载完成 再次发起创建容器请求,比较奇怪的是只拿到响应信息,尴尬了
再次创建容器
POST /v1.40/containers/create HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
Content-Length: 1514
Content-Type: application/json
{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false
...
镜像已存在
HTTP/1.1 201 Created
Api-Version: 1.40
Content-Length: 88
Content-Type: application/json
Date: Fri, 08 Nov 2019 05:41:03 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
容器同名ID检查
接下来这一步比较奇怪
POST /v1.40/containers/f021105c2da60f1f3930070e917668c7ffcabc45e2cf64c622a2d68f216d0c2b/wait?condition=next-exit HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
Content-Length: 0
Content-Type: text/plain
这个接口的功能是等待id
为f021105c2da60f1f3930070e917668c7ffcabc45e2cf64c622a2d68f216d0c2b
的容器退出
这一步应该是为了避免重复创建同名的容器
容器的ID生成代码GenerateRandomID
的实现如下,是一段64位的随机字符串,有可能会重复?
func GenerateRandomID() string {
b := make([]byte, 32)
for {
if _, err := rand.Read(b); err != nil {
panic(err) // This shouldn't happen
}
id := hex.EncodeToString(b)
// if we try to parse the truncated for as an int and we don't have
// an error then the value is all numeric and causes issues when
// used as a hostname. ref #3869
if _, err := strconv.ParseInt(TruncateID(id), 10, 64); err == nil {
continue
}
return id
}
}
因为没有同名的容器,所以返回正常
HTTP/1.1 200 OK
Api-Version: 1.40
Content-Type: application/json
Date: Fri, 08 Nov 2019 05:41:03 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
Transfer-Encoding: chunked
启动容器
此时可以启动容器了
POST /v1.40/containers/f021105c2da60f1f3930070e917668c7ffcabc45e2cf64c622a2d68f216d0c2b/start HTTP/1.1
Host: docker
User-Agent: Docker-Client/19.03.4 (darwin)
Content-Length: 0
Content-Type: text/plain
返回启动成功的响应
HTTP/1.1 204 No Content
Api-Version: 1.40
Date: Fri, 08 Nov 2019 05:41:04 GMT
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.4 (linux)
至此,容器启动完成
总结
以下是我整理的接口调用流程
可以看到,整个流程是非常简洁清晰而且严谨的,尤其是REST API的接口设计,非常规范,实在是很有借鉴意义,另外,第一次使用dtruss
,真的是神器。
Refer