多阶段Docker构建禁用缓存但复用前置阶段产物的优化方案咨询
方案合理性评估
方案1:按依赖顺序构建复用本地缓存
- 合理性:完全可行。你的Dockerfile阶段依赖链为
api_platform_composer→api_platform_php→api_platform_nginx,api_platform_ssh为独立阶段。优先构建依赖最深的nginx目标并开启--no-cache、--pull,后续构建php、ssh时关闭--no-cache,确实可以复用本地已构建的前置阶段产物,避免重复编译PHP扩展等耗时操作。 - 局限性:仅适用于本地有构建缓存的场景,CI环境无本地缓存时无效;如果仅需构建
php不需要nginx,会产生多余的构建步骤。
方案2:引入随机参数精准控制缓存失效
- 合理性:比方案1更灵活,是当前多目标构建场景下的推荐基础思路。仅在需要刷新缓存的步骤(如
apk upgrade、依赖拉取)前引入TIMESTAMP参数,不会影响PHP扩展编译等固定步骤的缓存复用,同时保留了--pull拉取最新基础镜像的能力。 - 注意事项:
ARG的作用域为当前FROM阶段之后,每个新的FROM块需要重新声明ARG TIMESTAMP才会生效。
更优实现方案
结合两种方案的优势,同时引入BuildKit特性进一步提升构建效率,改造如下:
第一步:改造Dockerfile
精准控制缓存失效节点,同时添加缓存挂载减少依赖重复下载:
# 全局声明通用参数 ARG TIMESTAMP ARG BASE_COMPOSER_IMAGE ARG BASE_PHP_IMAGE ARG BASE_OPENRESTY_IMAGE ARG BASE_SSH_IMAGE FROM ${BASE_COMPOSER_IMAGE} as api_platform_composer FROM ${BASE_PHP_IMAGE} AS api_platform_php # 引入全局时间戳参数 ARG TIMESTAMP # 挂载apk缓存目录,避免重复下载安装包 RUN --mount=type=cache,target=/var/cache/apk \ # 时间戳变化时该步骤缓存自动失效 echo "${TIMESTAMP}" > /dev/null && \ apk upgrade --no-cache # 固定依赖安装、扩展编译步骤无需加时间戳,自动复用缓存 RUN --mount=type=cache,target=/var/cache/apk \ apk add --no-cache {some packages} RUN set -eux; \ apk add --no-cache --virtual .build-deps $PHPIZE_DEPS {some packages}; \ docker-php-ext-configure {some packages}; \ docker-php-ext-install -j$(nproc) {some packages}; \ pecl install {some packages}; \ pecl clear-cache; \ docker-php-ext-enable {some packages}; \ runDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local/lib/php/extensions \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )"; \ apk add --no-cache --virtual .phpexts-rundeps $runDeps; \ apk del .build-deps COPY --from=api_platform_composer /usr/bin/composer /usr/bin/composer # 如果需要每次拉取最新Composer依赖,也可以加时间戳触发缓存失效 ARG TIMESTAMP RUN echo "${TIMESTAMP}" > /dev/null && \ composer install --no-dev --optimize-autoloader FROM ${BASE_OPENRESTY_IMAGE} AS api_platform_nginx ARG TIMESTAMP RUN --mount=type=cache,target=/var/cache/apk \ echo "${TIMESTAMP}" > /dev/null && \ apk upgrade --no-cache RUN echo -e "env UPSTREAM;\n$(cat /usr/local/openresty/nginx/conf/nginx.conf)" > /usr/local/openresty/nginx/conf/nginx.conf COPY src/docker/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf WORKDIR /srv/api/public COPY --from=api_platform_php /srv/api/public ./ FROM ${BASE_SSH_IMAGE} as api_platform_ssh ARG TIMESTAMP # 若需要更新ssh镜像依赖则加上述相同逻辑,无需则省略
第二步:优化构建脚本
移除全局--no-cache参数,统一传入时间戳,提取公共参数减少冗余:
# 生成单次构建统一使用的时间戳 export BUILD_TIMESTAMP=$(date +%s) # 提取公共构建参数,避免重复编写 COMMON_BUILD_ARGS=( --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg TIMESTAMP=${BUILD_TIMESTAMP} --build-arg BASE_COMPOSER_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/composer:latest --build-arg BASE_PHP_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/php:7.4-fpm-alpine --build-arg BASE_OPENRESTY_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/openresty/openresty:1.17.8.2-alpine --build-arg BASE_SSH_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/alpine:3 --build-arg BASE_MONGODB_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/mongo:4.4 --build-arg BASE_VARNISH_IMAGE=gitlab.com/good-technologies/dependency_proxy/containers/varnish:6.4 --progress=plain --pull --secret id=php-secrets,src=.secrets/php/.secrets . ) # 按需构建对应镜像,无需调整顺序自动复用缓存 docker build --target api_platform_php \ --cache-from registry.digitalocean.com/good-technologies/crm:latest-php,registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-php \ --tag registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-php \ "${COMMON_BUILD_ARGS[@]}" docker build --target api_platform_nginx \ --cache-from registry.digitalocean.com/good-technologies/crm:latest-nginx,registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-php,registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-nginx \ --tag registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-nginx \ "${COMMON_BUILD_ARGS[@]}" docker build --target api_platform_ssh \ --cache-from registry.digitalocean.com/good-technologies/crm:latest-ssh,registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-ssh \ --tag registry.digitalocean.com/good-technologies/crm:1.0.0-beta2-ssh \ "${COMMON_BUILD_ARGS[@]}"
额外优化点
- CI环境下可以提前拉取对应
cache-from的镜像到本地,BuildKit会自动识别复用远程缓存,无需依赖本地历史构建产物 - 不需要刷新依赖时可以固定
BUILD_TIMESTAMP的值,所有步骤都会完全复用缓存,构建速度最快
内容的提问来源于stack exchange,提问作者TobyG
相关产品推荐
相关产品推荐

