You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多阶段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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 07:45:05