Docker构建中如何利用缓存层跳过重复CMake源码编译流程
Docker构建源码编译缓存优化方案
完全可以利用Docker原生分层缓存机制保留编译结果,只要调整Dockerfile编写逻辑符合缓存命中规则,后续构建时未改动的依赖编译步骤会直接跳过,不需要额外第三方工具。
Docker缓存核心规则
- Docker构建时逐行执行指令,每执行完一条就生成对应缓存层
- 只要指令内容、前置依赖、关联文件的校验和与上一次构建完全一致,就会直接复用该层缓存,跳过执行
- 一旦某条指令缓存失效,其后续所有指令的缓存都会同步失效
当前写法的问题
现有写法把单个依赖的下载、解压、配置、编译拆成了多条独立RUN指令,虽然不直接影响缓存命中,但会产生大量冗余镜像层;最容易踩的坑是如果把频繁变动的业务代码复制等步骤放在这些依赖编译步骤之前,每次改业务代码都会导致所有依赖的缓存全部失效,每次都要全量重编。
优化方案
- 把所有不常变动的系统依赖安装、第三方库编译步骤放在Dockerfile最靠前的位置,频繁改动的业务代码复制、应用构建步骤放在文件末尾
- 单个依赖的下载、解压、编译、安装、临时文件清理操作合并为一条RUN指令,减少冗余层数
- 编译时加
-j$(nproc)参数调用多核编译,缩短首次编译耗时 - 只要不修改对应依赖的版本号、编译参数,对应编译层就会永久命中缓存,不会重复执行
优化后的核心代码段参考:
# 先安装编译所需的系统依赖,不修改依赖列表就会命中缓存 RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ cmake \ wget \ ca-certificates \ libtiff-dev \ libcurl4-openssl-dev \ && rm -rf /var/lib/apt/lists/* # 编译安装SQLite,版本、编译参数不变则全程跳过 RUN wget -O sqlite-autoconf-3380200.tar.gz https://sqlite.org/2022/sqlite-autoconf-3380200.tar.gz \ && tar -xvzf sqlite-autoconf-3380200.tar.gz \ && cd sqlite-autoconf-3380200 \ && ./configure \ && make -j$(nproc) \ && make install \ && cd /tmp \ && rm -rf sqlite-autoconf-3380200* # 编译安装PROJ,版本、编译参数不变则全程跳过 RUN wget -O proj-9.0.0.tar.gz https://download.osgeo.org/proj/proj-9.0.0.tar.gz \ && tar -xvzf proj-9.0.0.tar.gz \ && cd proj-9.0.0 \ && mkdir build \ && cd build \ && cmake .. \ && cmake --build . -j$(nproc) \ && cmake --build . --target install \ && projsync --system-directory --list-files \ && cd /tmp \ && rm -rf proj-9.0.0* # 后续再放业务代码复制、应用构建步骤,这部分变动不会影响前面的依赖缓存 # COPY ./your-app-src /app # RUN 执行你的应用构建/启动配置
注意事项
- 正常执行
docker build .就会自动复用缓存,不要随意加--no-cache参数,否则会强制清空所有缓存全量构建 - 不要在第三方依赖编译步骤之前执行
COPY . /xxx这类复制整个项目目录的指令,本地项目任何文件改动都会导致后续所有缓存失效 - 如果只修改了某一个依赖的版本,只有该依赖对应的编译步骤缓存会失效,其他未改动的依赖依然会命中缓存,不会重复编译
内容的提问来源于stack exchange,提问作者Michal Dolnik
相关产品推荐
相关产品推荐

