多阶段Dockerfile如何实现全阶段构建缓存的充分复用
Docker多阶段构建Buildkit中间阶段缓存复用问题
现有配置
Dockerfile
FROM debian:10 as builder RUN sleep 10 COPY input input # worlds most trivial build pipeline RUN cat input > artifact FROM debian:10 COPY --from=builder artifact artifact RUN cat artifact COPY input2 input2
docker-compose配置文件
%YAML 1.1 --- version: '3.7' services: sdn-controller: build: context: . cache_from: - hansbogert/test1:latest args: - BUILDKIT_INLINE_CACHE=1 image: hansbogert/test1:latest
初始化操作
创建两个空文件:
$ touch input input2
初始构建并推送镜像:
export DOCKER_BUILDKIT=1 docker-compose build docker-compose push
测试场景
场景1:清空本地缓存后重新构建
操作:
docker rmi hansbogert/test1 ; docker image prune -f; docker builder prune -af docker-compose build
结果:所有步骤均命中缓存,符合预期。
场景2:修改input2后清空缓存重新构建
操作:
$ echo 1 > input2 $ docker rmi hansbogert/test1 ; docker image prune -f; docker builder prune -af $ docker-compose build
结果:仅COPY input2之后的步骤重新执行,符合预期,截断输出如下:
=> CACHED [stage-1 3/4] RUN cat artifact 0.7s => => pulling sha256:f33c84f9d3c6505acdda2a6d1c7238c853e07f3723e4a5d4c9eb65a163710ffd 0.3s => => pulling sha256:0b00a0a96175fa32a06c3741ac1fb655aafc2ed1584eebfd2e213561998f7bea 0.4s => [stage-1 4/4] COPY input2 input2 0.0s => exporting to image ...
场景3:修改input后清空缓存重新构建
操作:
$ echo 1 > input $ docker rmi hansbogert/test1 ; docker image prune -f; docker builder prune -af $ docker-compose build
结果:builder阶段的RUN sleep 10步骤未命中缓存,整个builder阶段全部重新执行,不符合预期,截断输出如下:
=> [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 254B 0.0s => [internal] load .dockerignore 0.1s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/debian:10 0.0s => importing cache manifest from hansbogert/test1:latest 1.3s => [builder 1/4] FROM docker.io/library/debian:10 0.1s => [internal] load build context 0.0s => => transferring context: 71B 0.0s => [builder 2/4] RUN sleep 10 10.5s => [builder 3/4] COPY input input 0.1s => [builder 4/4] RUN echo input > artifact 0.6s => CACHED [stage-1 2/4] COPY --from=builder artifact artifact 0.0s
核心问题
是否有方案可以让最终镜像之外的构建阶段也能正常复用缓存?
解决方案
问题原因
默认的BUILDKIT_INLINE_CACHE仅会将最终镜像的层缓存内嵌到推送的镜像中,多阶段构建的中间阶段(如本案例中的builder阶段)的层不会被包含在内,因此修改input文件后,builder阶段的所有前置步骤无法命中远端缓存,只能重新执行。
方案1:单独推送中间阶段镜像
新增对应builder阶段的构建配置,将中间阶段的镜像单独推送到镜像仓库,主服务的缓存源同时添加最终镜像和中间阶段镜像即可:
修改后的docker-compose配置:
version: '3.7' services: # builder阶段专属镜像服务 sdn-controller-builder: build: context: . target: builder # 指定仅构建到builder阶段 cache_from: - hansbogert/test1-builder:latest args: - BUILDKIT_INLINE_CACHE=1 image: hansbogert/test1-builder:latest sdn-controller: build: context: . cache_from: - hansbogert/test1:latest - hansbogert/test1-builder:latest # 新增中间阶段镜像作为缓存源 args: - BUILDKIT_INLINE_CACHE=1 image: hansbogert/test1:latest depends_on: - sdn-controller-builder
构建执行顺序:
export DOCKER_BUILDKIT=1 # 先构建推送中间阶段镜像 docker-compose build sdn-controller-builder docker-compose push sdn-controller-builder # 再构建推送最终镜像 docker-compose build sdn-controller docker-compose push sdn-controller
该方案无需额外依赖,适合阶段较少的多阶段构建场景。
方案2:使用Buildkit全阶段缓存导出
使用Buildkit原生支持的缓存导出模式,指定mode=max即可导出所有构建阶段的缓存,无需单独维护中间镜像,适合复杂多阶段构建场景:
直接使用docker buildx命令示例
docker buildx build \ --push \ -t hansbogert/test1:latest \ --cache-to=type=registry,ref=hansbogert/test1-cache:latest,mode=max \ --cache-from=type=registry,ref=hansbogert/test1-cache:latest \ .
docker-compose配置示例(需compose v2及以上版本支持)
services: sdn-controller: build: context: . cache_from: - type=registry,ref=hansbogert/test1-cache:latest cache_to: - type=registry,ref=hansbogert/test1-cache:latest,mode=max image: hansbogert/test1:latest
验证效果
修改input文件后重新构建,可观察到[builder 2/4] RUN sleep 10步骤标记为CACHED,仅COPY input之后的步骤重新执行,符合预期。
内容的提问来源于stack exchange,提问作者hbogert
相关产品推荐
相关产品推荐

