重学容器12: 使用buildkit实现容器镜像的远程构建
2021-06-10
本节做一个实战的练习,使用buildkit完成容器镜像的远程构建,即实现本地的buildctl客户端远程请求服务器上的buildkitd完成一个容器镜像的构建并推送到镜像仓库中。
部署buildkitd服务端, 暴露为TCP服务 #
下面将在服务器上以二进制的形式部署服务端buildkitd,并将其gRPC API以TCP服务的形式暴露出来。 此处在部署buildkitd的时候是以具备生产环境可用为目的,为了安全性会以rootless的形式启动buildkitd,同时为gRPC Server和Client生成TLS证书,开启TLS认证以保障服务的安全。
服务端的环境信息如下:
- CentOS 7.9 (linux 3.10.0-1160)
- 本地局域网IP地址: 192.168.100.61
为了以rootless模式运行buildkitd,需要先下载并安装rootlesskit。
然后创建非特权用户如buildkit
,之后使用cfssl或其他ssl工具生成服务端和客户端证书:
1ca.crt
2client.crt
3client.key
4server.crt
5server.key
证书生成成功后,就可以下载和安装buildkit相关的二进制文件,并创建buildkitd的配置文件buildkitd.toml
。
这里把buildkit安装到/urs/local/buildkit
下,前面的各个步骤执行完成后,目录结构如下:
1/usr/local/buildkit/
2├── bin
3│ ├── buildctl
4│ ├── buildkitd
5│ ├── buildkit-qemu-aarch64
6│ ├── buildkit-qemu-arm
7│ ├── buildkit-qemu-i386
8│ ├── buildkit-qemu-ppc64le
9│ ├── buildkit-qemu-riscv64
10│ ├── buildkit-qemu-s390x
11│ ├── buildkit-runc
12│ └── rootlesskit
13├── config
14│ └── buildkitd.toml
15└── ssl
16 ├── ca.crt
17 ├── client.crt
18 ├── client.key
19 ├── server.crt
20 └── server.key
配置文件buildkitd.toml的内容如下:
1debug = false
2root = "/var/lib/buildkit"
3
4[grpc]
5 address = [ "unix:///run/user/997/buildkit/buildkitd.sock", "tcp://192.168.100.61:9090" ]
6 [grpc.tls]
7 cert = "/usr/local/buildkit/ssl/server.crt"
8 key = "/usr/local/buildkit/ssl/server.key"
9 ca = "/usr/local/buildkit/ssl/ca.crt"
10
11[worker.oci]
12 enabled = true
13 snapshotter = "auto"
14 rootless = true # see docs/rootless.md for the details on rootless mode.
15 # Whether run subprocesses in main pid namespace or not, this is useful for
16 # running rootless buildkit inside a container.
17 noProcessSandbox = false
18 binary = "/usr/local/buildkit/bin/buildkit-runc"
19 gc = true
20 gckeepstorage = 9000
21
22[worker.containerd]
23 address = "/run/containerd/containerd.sock"
24 enabled = false
25 namespace = "buildkit"
26 gc = true
27 gckeepstorage = 9000
这个配置文件还有以下内容需要我们关注一下:
- 可以看出buildkitd的gRPC API在
unix:///run/user/997/buildkit/buildkitd.sock
和tcp://192.168.100.61:9090
上可用。 其中/run/user/997/buildkit/buildkitd.sock
的997
是前面创建的buildkit
用户的uid。 root = "/var/lib/buildkit"
指定是buildkitd的数据目录buildkitd
的worker可以是containerd
,也可以是oci
,因为这里需要以rootless模式启动,rootless模式目前还不支持使用containerd作为worker,所以这里将worker containerd停用snapshotter = "auto"
将snapshotter指定为auto,这是因为在rootless模式下,buildkit还不支持overlayfs,这里设置为auto
,snapshotter会自动切换到native
。
为了以非特权用户启动buldkitd服务,还需要一些配置,具体可参考Rootless mode
,这里以Cent OS 7.9为例。
需要添加user.max_user_namespaces=28633
到 /etc/sysctl.conf
(或/etc/sysctl.d下的buildkit.conf),然后运行sysctl -p
。
接下来为buildkit
用户映射下级用户和组:
1usermod --add-subuids 100000-165535 --add-subgids 100000-165535
上面的命令会往/etc/subuid
和/etc/subgid
中添加buildkit:100000:65536
,为buildkit用户分配了从属uid的范围从100000到165535。
uid 100000将会以uid 0(root)映射到名称空间内(例如在容器中时),uid 100001将会以uid 1映射到名称空间,以此类推。也就是说如果某个进程尝试提权的话,如到uid 0(root),实际上只会作为主机上非特权的大编号uid,如此处为100000,大编号的uid甚至在主机上映射不到真实的用户,也就是说该进程不会在主机上取得特权,这样就起到了安全的作用。
接下来就编写如下的systemd unit文件buildkit.service
:
1[Unit]
2Description=BuildKit
3After=network.target
4
5[Service]
6User=buildkit
7ExecStart=/usr/local/buildkit/bin/rootlesskit \
8 /usr/local/buildkit/bin/buildkitd \
9 --config=/usr/local/buildkit/config/buildkitd.toml
10
11[Install]
12WantedBy=multi-user.target
部署完成后systemctl start buildkit
启动buildkitd,systemctl status buildkit
会提示running server on /run/user/997/buildkit/buildkitd.sock
和running server on [::]:9090
,前者本机buildctl客户端使用没有启用TLS,后者暴露给其他机器上buildctl客户端使用启用了TLS。
下面本机验证一下部署是否成功:
1buildctl --addr=unix:///run/user/997/buildkit/buildkitd.sock debug workers
2ID PLATFORMS
3mcr24g17vg9of9zhv6ob0jw8m linux/amd64,linux/386
如果正常打印出workers且没有报错,则buildkitd部署成功。 ps -ef | grep buildkitd
查看一下,buildkitd确实由非特权用户buildkit启动。
buildctl远程调用buildkitd构建镜像 #
下面使用buildctl远程调用服务器上的buildkitd构建镜像。这里在我个人的macbook上下载mac os版本的buildctl,并把服务器端生成的证书ca.crt
, client.crt
, client.key
拷贝到本地:
1├── buildctl
2├── ca.crt
3├── client.crt
4└── client.key
编写一个最简单的Dockerfile:
1mkdir /tmp/myproject
2echo "FROM alpine" > /tmp/myproject/Dockerfile
使用下面的命令构建镜像:
1./buildctl --addr=tcp://192.168.100.61:9090 \
2 --tlscacert=./ca.crt \
3 --tlscert=./client.crt \
4 --tlskey=./client.key \
5 build \
6 --frontend dockerfile.v0 \
7 --local context=/tmp/myproject \
8 --local dockerfile=/tmp/myproject \
9 --output type=image,name=harbor.myorg.com/myproject/myimg:1.0,push=true
上面的命令执行后,当镜像构建成功后将会自动推送镜像到镜像仓库中。
需要注意如果镜像仓库是私有仓库且开启了认证的话,需要在buildctl的执行用户的~/.docker
目录下创建config.json
:
1{
2 "auths": {
3 "harbor.myorg.com": {
4 "auth": "base64(username:password)"
5 }
6 }
7}
因为目前buildctl默认读取这个config.json里的认证信息登录到私有镜像仓库。 这个在https://github.com/moby/buildkit/issues/565这个issue中有讨论。
总结 #
本节我们以二进制的形式rootless的模式部署了buildkitd,并使用buildctl远程调用buildkitd完成了一个容器镜像的构建,在下一节中我们将介绍在k8s集群中以容器的形式部署buildkitd。