内容总结自 万字长文:彻底搞懂容器镜像构建 博文。
Docker
整体上是个 C/S
架构,平时使用的 docker
命令只不过是一个 cli
的客户端,而它的服务端是 dockerd
在 Linux
系统中,通常我们是使用 systemd
进行管理,所以我们可以使用 systemctl start docker
来启动服务。
docker-cli -> rest-api -> dockerd
1. 接口:API
API 即 Application Programming Interface,应用程序接口。
Docker
维护团队在每个版本正式发布之后,都会将 API 文档发布出来,可以通过 Docker Engine API 在线浏览,也可以自行构建 API
文档。
$ docker version |grep API
API version: 1.42
API version: 1.42 (minimum version 1.12)
通过 API
我们也知道了该接口所需的请求体是一个 tar
归档文件(可选择压缩算法进行压缩),同时它的请求头中会携带用户在镜像仓库中的认证信息。这提醒我们, 如果在使用远程 Dockerd
构建时,请注意安全,尽量使用 tls
进行加密,以免数据泄漏。
- 请求地址和方法
- 接口地址是
/v1.41/build
- 方法是
POST
- 接口地址是
- 请求体
- 请求体是一个
tar
归档文件 - 可选择无压缩、
gzip
、bzip2
、xz
压缩等形式
- 请求体是一个
- 请求头
Content-type
默认是application/x-tar
X-Registry-Config
对应auths
信息保持一致- 执行
docker login
后自动写入$HOME/.docker/config.json
文件内 - 拉取镜像的认证信息使用
auths
->$HOME/.docker/config.json
(base64
) - 解密登录信息内容
echo -n "xxx" | base64 -d
- 执行
- 请求参数
- 通过
docker build --help
基本都可以看到对应含义
- 通过
# eg:
$ curl -X POST --unix-socket /var/run/docker.sock localhost/v1.41/build
# eg:
$ curl -X POST --unix-socket \
--host /var/run/docker.sock localhost/v1.41/build
2. 客户端:cli
docker
客户端是docker
生态中最重要的部分,它提供了docker
大部分功能。
docker
是我们所使用的客户端工具,用于与 dockerd
进行交互。docker cli
目前有两个构建系统,一个是 v1
版本的 builder
和 v2
版本的 BuildKit
。
关于构建相关的部分, 我们所熟知的便是 docker build
或者是 docker image build
,在 19.03
中新增的是 docker builder build
,但其实他们都是同一个只是做了个 alias
罢了。
// cmd/docker/docker.go#L237
if v, ok := aliasMap["builder"]; ok {
aliases = append(aliases,
[2][]string{{"build"}, {v, "build"}},
[2][]string{{"image", "build"}, {v, "build"}},
)
}
在 /etc/docker/daemon.json
中添加如下内容,并重启 dockerd
即可,就可以在 dockerd
中开启 buildkit
了。在 docker cli
上也可开启 buildkit
的支持,并且 cli
的配置可覆盖服务端配置。通过 export DOCKER_BUILDKIT=1
即可开启 buildkit
的支持,设置为 0
则关闭。
{
"features": {
"buildkit": true
}
}
从入口函数 runBuild
开始,经过判断是否支持 buildkit
,如果不支持 buildkit
则继续使用 v1
的 builder
。接下来读取各类参数,按照不同的参数执行各类不同的处理逻辑。最后当构建结束后,cli
根据参数决定是否要显示构建进度或者结果。
docker cli -> rest api -> 参数校验 -> BuildKitEnabled -> runBuild
-> 排除文件 .dockerignore
-> 组织 build context
- builder v1
大致经历了执行构建阶段:最开始的部分是一些对参数的处理和校验,然后使用 .dockerignore
忽略不需要加入到镜像内的文件和目录,生成真正的 build context
内容用来打包构建。然后 docker cli
还会去读取 ~/.docker/config.json
中的内容,将认证信息通过 X-Registry-Config
头传递给 dockerd
用于在需要拉取镜像时进行身份校验。当一切所需的校验和信息都准备就绪之后,则开始调用 dockerCli.Client
封装的 API
接口,将请求发送至 dockerd
,进行实际的构建任务。之后,便是按照传递的参数进行进度的输出或是将镜像 ID
写入到文件之类的。
- 对参数的处理和校验
stream
和compress
不可同时使用- 不可同时使用
stdin
读取Dockerfile
和build context
build context
支持四种行为,包括tar
/git
/url
/stdin
- 使用
.dockerignore
忽略不需要的文件 - 读取
auths
中的内容进行拉取镜像时的身份校验 - 调用
API
进行实际构建任务
# 通过stdin传递Dockerfile的方式 OK
$ cat Dockerfile | DOCKER_BUILDKIT=0 docker build -f - .
# 通过stdin传递build context的方式 OK
$ cat x.tar | DOCKER_BUILDKIT=0 docker build -f Dockerfile -
# 报错 Error
$ DOCKER_BUILDKIT=0 docker build -f - -
- buildkit v2
大致经历了执行构建阶段:从入口函数 runBuild
开始,判断是否支持 buildkit
,如果支持 buildkit
则调用 runBuildBuildKit
。与 v1
的 builder
不同的是,开启了 buildkit
后,会首先创建一个长连接的会话并一直保持。其次,与 builder
相同,判断 build context
的来源,格式之类的,校验参数等。当然,buildkit
支持三种不同的输出格式 tar
、local
或正常的存储于 Docker
的目录中。另外是在 buildkit
中新增的高阶特性,可以配置 secrets
和 ssh
密钥等功能。最后,再调用 API
与 dockerd
交互完成镜像的构建。
- 创建长连接会话
- 选择输出模式,
local
、tar
、cacheonly
、dockerd
- 使用
.dockerignore
忽略不需要的文件 - 读取
auths
中的内容进行拉取镜像时的身份校验- 高阶特性:
mount secrets
和ssh
- 高阶特性:
- 调用
API
进行实际构建任务
3. 服务端:dockerd
接收来自客户端的请求,并调用
API
进行构建任务。
当 CLI
通过 /build
接口发送请求后,dockerd
将通过 API
提交过来的参数转换为构建动作实际需要的参数形式,并从请求头拿到认证信息,这个时候需要注意了: 真正的构建过程要开始了。
使用 backend
的 Build
函数来完成真正的构建过程,函数看着比较长,但主要功能就以下三点。到这个函数之后,就分别是 builder
与 buildkit
对 Dockerfile
的解析,以及对 build context
的操作了。
NewTagger
是用于给镜像打标签,也就是我们的-t
参数相关的内容- 通过判断是否使用了
buildkit
来调用不同的构建后端 - 处理构建完成后的动作