必须用多阶段构建分离构建与运行阶段,否则vendor/会混入dev依赖、测试文件、.git和未优化autoloader,导致镜像多出50–200mb;单阶段构建因残留非运行文件、未加--no-dev/--optimize-autoloader及选用胖基础镜像,易膨胀至400mb+。

必须用 FROM ... AS builder 分离构建与运行阶段,否则 vendor/ 里必然混入 --dev 依赖、测试文件、.git 目录和未优化的 autoloader,镜像多出 50–200MB 是常态。
为什么单阶段 composer install 会让镜像膨胀到 400MB+?
根本问题不是 Composer 本身大,而是它在运行时环境里“干了不该干的事”:
- 在同一个镜像里既装
phpunit又跑服务,vendor/里残留tests/、docs/、bin/脚本等非运行必需内容 - 没加
--no-dev和--optimize-autoloader,autoloader 文件体积翻倍,且加载慢 - 基础镜像选了
php:8.3-apache这类带完整 Web 服务器的胖镜像,而非php:8.3-cli-alpine -
COPY . /app后再composer install,把项目根目录下所有东西(包括.git、docker-compose.yml)全拖进镜像层
多阶段构建中 composer install 的正确执行顺序
缓存是否生效,取决于 COPY 和 RUN 的顺序是否严格对齐。错一步,整个 vendor 层就失效重装:
- 先
COPY composer.json composer.lock ./—— 必须单独这一步,不带其他文件 - 紧跟着
RUN composer install --no-dev --optimize-autoloader --classmap-authoritative—— 中间不能插COPY . .或RUN chmod - 再
COPY . .复制源码(不含composer.*),避免覆盖已缓存的 vendor 层 - 如果用了 BuildKit,加上
RUN --mount=type=cache,id=composer-cache,dest=/root/.composer/cache composer install ...,缓存自动跨构建复用
最终镜像里还藏着哪些“隐形体积杀手”?
即使用了多阶段,COPY --from=builder 写错路径或漏清理,照样白忙:
-
COPY --from=builder /app .是最常见错误 —— 它会把/app/vendor、/app/tests、/app/.git全复制进来 - 正确写法是分拆:
COPY --from=builder /app/vendor /var/www/html/vendor+COPY . /var/www/html - builder 阶段安装完后,建议加清理指令:
RUN find vendor/ -name '.git' -prune -exec rm -rf {} + 2>/dev/null || true - 检查 final 镜像是否含
/root/.composer、/tmp/composer*、vendor/bin/*—— 这些都不该存在
真正难的不是写对 Dockerfile,而是让每一层缓存都稳定命中、让每一条 COPY --from=builder 都只取所需。一旦路径写宽、顺序写乱、清理写漏,体积反弹比单阶段还难排查。











