根本原因是copy . .出现在composer install之前破坏了docker层缓存链;必须最早单独copy composer.json和composer.lock,再run install,最后copy . .并忽略/vendor,否则任何代码变更都会导致vendor层失效。

为什么 vendor 目录总在重复构建?
根本不是 Composer 本身慢,而是 Docker 层缓存被破坏。只要 COPY . . 出现在 composer install 前,哪怕只改了一个空格,整个 vendor/ 层就失效——因为 Docker 认为上层输入变了,后续所有 RUN 指令都必须重跑。
常见错误现象包括:
-
Step 5/10 : RUN composer install每次都重新下载包、解压、生成 autoload - CI 构建耗时从 30 秒跳到 3 分钟以上
- 报错如
Package foo has a PHP requirement incompatible with your PHP version,其实是缓存错乱导致用了旧的 lock 文件或旧的 PHP 环境
如何让 vendor 层真正复用?
关键不是加参数,而是严格控制 COPY 顺序和内容稳定性。Docker 只认 composer.json 和 composer.lock 这两个文件的哈希值作为缓存锚点。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 必须最早单独
COPY composer.json composer.lock ./,不能混在其他文件里(比如和.env或README.md一起 COPY) - 这两个文件必须已提交到 Git,不能是本地生成、未
git add、或被.dockerignore过滤掉 -
RUN composer install --no-dev --no-scripts --optimize-autoloader --no-interaction --prefer-dist所有参数都要服务于确定性:禁用钩子、跳过开发依赖、不交互、优先用 dist 包 -
COPY . .放在最后,并确保.dockerignore包含/vendor,防止它覆盖刚装好的目录
OverlayFS 下 vendor 层膨胀的真实来源
很多人以为 vendor/ 占空间是因为包多,其实更关键的是 OverlayFS 的 copy-up 行为。当容器运行时修改 vendor/autoload.php 或写入 vendor/composer/installed.json,OverlayFS 会把整个文件从只读层复制到 upperdir —— 即使只改了 1 字节,也复制几 MB 的文件。
- PHP 的 opcache 预编译文件(
.opcache)如果写入容器内,也会触发 copy-up - Composer 插件(如
hirak/prestissimo)若启用,会在~/.composer下写缓存,同样落入 upperdir - 解决办法:用
--mount=type=cache,target=/root/.composer把 Composer 用户级缓存移到 tmpfs;设COMPOSER_CACHE_DIR=/dev/null彻底禁用非必要缓存
镜像基础层对 vendor 查找性能的影响
每次 require 一个类,PHP 要通过 autoloader 在 vendor/ 中查找路径;而这个 vendor/ 实际位于 OverlayFS 的 merged 视图下。路径查找要逐层遍历 lowerdir(镜像层)+ upperdir(容器层),层数越多,inode 查找越慢。
- 用
docker image inspect <image> --format='{{json .RootFS.Layers}}' | jq length</image>查看基础镜像层数,超过 5 层建议换php:8.3-cli-slim或alpine版本 - 避免在基础镜像中预装扩展(如
php-mysql),除非真用得上;每个扩展都增加一层、更多文件、更深目录树 - 如果项目用的是多阶段构建,builder 阶段可以宽松,但 final 阶段务必用最小运行时镜像,且不要
COPY --from=builder /app/vendor /app/vendor—— 这会让 vendor 落入 upperdir,失去只读共享优势
真正难处理的不是 vendor 多大,而是它是否“活”在 OverlayFS 的可写层里。一旦 vendor 被 copy-up 或覆盖写入,它就从共享资产变成每个容器独占副本,磁盘和 inode 开销都会指数级上升。










