1. 背景
在产品开发中构建docker镜像,随着时间的推移,会变得越来越大,构建时间也越来越长。我的目标是构建时间不超过 5 分钟。
2. 遵循Dockerfile的最佳实践
我们首先确保Dockerfile文件遵循Docker官方的最佳实践,具体做法有:
- 尽量使用官方的基础镜像,Docker推荐使用Alpine的镜像。
- 使用多阶段构建
- 使用.dockerignore去除无关的文件
- 创建临时容器
- 不要安装不用的包
- 解耦应用程序
- 利用缓存构建镜像
- 拆分复杂的RUN命令为多行,并用 / 分割
更多详细的最佳实践可以参考官方文档:docs.docker.com/develop/dev…
我们在此基础上做了2个修改,极大的缩短了构建时间。
首先介绍下两个工具:Buildkit 与 Buildx
3. Buildkit 与 Buildx
3.1 Buildit
Buildit是一个改进过的用来取代传统的Docker构建器的工具,从Docker 23.0开始成为默认构建器。它包含以下一些功能:
- 改进的缓存能力
- 并行构建不同的层
- 基础镜像的懒加载
使用buildit的时候,会发现比docker build的输出更简洁。
Docker 23.0前的版本要使用buildit,可以设置参数,如下:
DOCKER_BUILDKIT=1 docker build --platform linux/amd64 . -t someImage:someVersion
DOCKER_BUILDKIT=1 docker push someImage:someVersion
3.2 Buildx
Buildx是Docker的一个插件,使得buildit在构建的时候充分发挥其能力。它的创建是因为 Buildkit 支持许多新的配置选项,但这些选项无法以向后兼容的方式全部集成到docker build命令中。
除了构建镜像之外,Buildx还支持管理多个构建器。
可以按如下方式开始使用 Buildx:
docker buildx create --bootstrap --name builder
docker buildx use builder
4. 技巧1:利用远程缓存
加快构建速度的第一种方法是将镜像缓存在远程上。这样,即使构建是在不同的机器上执行的,也可以利用远程构建缓存加速。
可以使用以下命令:
docker pull someImage:latest || true
docker build --platform linux/amd64 . \
-t someImage:someVersion \
-f Dockerfile \
--cache-from someImage:latest
使用 Buildx,可以将缓存信息存储在远程位置(例如容器注册表、blob 存储等)。构建器检查给定层是否已存在,如果存在,它将重用它而不是再次创建它。这甚至可以在不本地拉动层的情况下完成。
我们把之前的命令修改一下:
docker buildx build --platform linux/amd64 . \
-t someImage:someVersion - push \
--cache-to type=registry,ref=someCachedImage:someVersion,mode=max
--cache-from type=registry,ref=someCachedImage:someVersion
mode=max 意味着我们将存储每个层的构建信息,包括结果镜像中未使用的层(例如,当使用多阶段构建时)。
默认情况下使用 “min”模式,它仅存储有关最终镜像中存在的层的构建信息。
5. 技巧2:在ADD和COPY时使用额外链接
新版本的 dockerfile 语法中,支持COPY和ADD命令的额外链接选项。
以前,当您使用COPY或ADD命令时,构建器会创建一个新快照,它将新文件与现有文件系统合并。结果是,在执行此操作之前,父层都需要存在,否则目标目录可能尚不存在。最后,构建的镜像将由每层的tarball组成,其中包含各个快照之间的差异。
比如,如下命令:
FROM baseImage:version
COPY binary /opt/
使用额外链接时,新文件将放置在自己的快照中,而不依赖于先前的层。链接的文件存储在它们自己的 tarball 中,并且不同的 tarball 链接在一起,而不依赖于现有文件系统,如下图所示。
可以使用如下命令替换之前的命令
# syntax=docker/dockerfile:1.4
FROM baseImage:version
COPY [--chown=<user>:<group>] [--chmod=<perms>] --link binary /opt/
这样做的好处是文件不再依赖于先前的层。只要文件没有更改,即使父图层发生更改,该图层也可以重复使用。同时,还可以提高构建速度,因为现在可以并行执行多层复制数据。
6. 总结
通过利用远程缓存和在ADD和COPY时使用链接,能把原来的docker镜像构建时间提高40%,这是2个非常有效的技巧。