根本原因在于docker层依赖链被破坏:只要copy . .出现在composer install之前,哪怕仅修改readme.md,docker就会丢弃后续所有层缓存(含vendor/);必须最早单独copy composer.json和composer.lock ./,再run install,最后copy . .,且锁文件须已提交至git。

为什么 vendor 层总不命中缓存?
根本不是 Composer 自身的问题,而是 Docker 构建层依赖链被破坏。只要 COPY . . 出现在 composer install 前面,哪怕只改了一个 README.md,Docker 就会丢弃所有后续层缓存——包括 vendor/ 目录。
常见错误现象:
-
docker build每次都重新下载包、解压、安装,耗时 2–5 分钟 -
composer install日志里反复出现Loading composer repositories和Downloading... - 构建日志中该层显示
Cache miss,而非Using cache
真正关键的判断点只有两个:
-
composer.json和composer.lock是否已提交到 Git(未提交 = 每次解析版本,缓存必然失效) - 它们是否被
COPY到镜像中且是最早、最独立的一次复制(中间不能插RUN chmod、COPY .env.example等任何其他指令)
如何让 composer install 层稳定复用?
必须确保这一步只依赖两个文件,且不被其他变更干扰。否则缓存逻辑就失效了。
- 先执行:
COPY composer.json composer.lock ./(注意路径结尾是./,不是/app/,避免因路径差异触发重建) - 紧跟着执行:
RUN composer install --no-dev --optimize-autoloader --no-scripts --no-progress --prefer-dist - 之后再
COPY . .,把源码复制进来 - 不要在
composer install前做chmod、chown或mkdir -p—— 这些操作如果参数不稳定(比如用变量),也会污染缓存
参数选择有明确影响:
-
--no-dev:跳过require-dev,避免本地开发环境差异(如phpunit版本浮动)导致缓存跨机器失效 -
--no-scripts:禁用post-install-cmd等钩子,防止脚本执行引入不确定性(比如生成随机 token、调用外部 API) -
--prefer-dist:优先用压缩包而非 Git clone,下载快、解压稳,比--prefer-source更利于缓存复用
多阶段构建中 vendor 层怎么精准复制?
builder 阶段装好依赖后,final 阶段不能 COPY --from=builder /app 整个目录——那会把 .git、node_modules、临时 zip 文件全带进来。
- builder 阶段用完整镜像(如
php:8.2-cli-bullseye),装扩展、设缓存、跑composer install - final 阶段用精简镜像(如
php:8.2-slim),只COPY --from=builder /app/vendor /app/vendor - 再
COPY . .复制应用代码(前提是.dockerignore里已排除vendor/,否则本地vendor/会覆盖镜像里的) - final 阶段不要运行
composer install,也不挂载/root/.composer—— 它不该存在
容易踩的坑:
- builder 阶段写入了
/tmp/composer*或/root/.composer/cache—— 这些路径不会被COPY --from复制,但可能污染 builder 缓存层,导致下次构建行为异常 - final 阶段
COPY . .包含了composer.json和composer.lock,然后又在该阶段运行composer install—— 这等于放弃所有分层缓存优势
BuildKit cache mount 有用吗?
它能加速包下载,但解决不了 vendor/ 层复用这个核心问题。而且在 CI 环境下容易出错,收益有限。
- 启用方式:
RUN --mount=type=cache,id=composer-cache,dest=/root/.composer/cache composer install - 实际效果取决于 CI 节点是否共享
id=composer-cache存储;并发 job 可能冲突,报错如Package foo has a PHP requirement incompatible with your PHP version - 更可控的替代方案:CI 中挂载宿主机的
~/.composer/cache到构建容器,用--build-arg COMPOSER_CACHE_DIR=/host/cache+ENV设置 - 本地开发几乎不用开 —— 分层
COPY+ 稳定lock文件已足够
真正容易被忽略的是:--ignore-platform-reqs 这个参数。它会让 composer install 绕过 PHP 版本和扩展检查,看似安装成功,但镜像部署后可能直接报 Class not found。缓存再稳也没用。











