容器中composer写磁盘卡顿的根源是未将composer_cache_dir、sys_get_temp_dir()和vendor三路径用tmpfs挂载到内存,需分别挂载并设合理size,且避免与volume路径冲突。

为什么容器里 Composer 还在写磁盘?
不是 Composer 本身慢,而是它默认把包缓存、解压临时文件、甚至 vendor 写入都落在磁盘上。哪怕你用了国内镜像源,composer install 卡在 Extracting archive 或反复报 failed to open stream: Permission denied,大概率是 PHP 的临时目录(sys_get_temp_dir())或 Composer 缓存目录没挂到内存里——它还在往容器只读层或低速 volume 里硬写。
tmpfs 挂载点必须覆盖这三处路径
只挂 /tmp 不够,Composer 实际会用到三个独立路径,缺一不可:
-
COMPOSER_CACHE_DIR:包缓存(archived/、repo/),默认~/.composer/cache -
sys_get_temp_dir():解压 ZIP 时的临时工作目录,PHP 层控制,Composer 不提供配置项 -
vendor/目录(仅限运行时动态安装场景):若必须容器启动后composer install,该目录需可写
正确做法是用 --tmpfs 同时挂载:
docker run --tmpfs /tmp:rw,size=100m \ --tmpfs /root/.composer:rw,size=50m \ --tmpfs /app/vendor:rw,size=200m \ -e COMPOSER_CACHE_DIR=/root/.composer/cache \ -e TMPDIR=/tmp \ your-composer-image
注意:TMPDIR 环境变量必须显式设置,否则 PHP 仍会 fallback 到默认只读路径;/root/.composer 要整目录挂,因为 cache 子目录权限常被 Composer 自动修改。
大小设错比不挂更危险
tmpfs 不设 size= 会默认占主机一半内存,线上极易触发 OOM;但设太大也无用,Composer 解压单个包极少超 50MB,关键看并发数和包数量:
-
/tmp:设size=100m足够应付多数 ZIP 解压(含多层嵌套) -
/root/.composer:设size=50m—— 实测 100 个包缓存约占用 30–45MB -
/app/vendor:设size=200m—— vendor 目录本身不常驻 tmpfs,但安装过程峰值内存占用高 - 所有 tmpfs 总和建议 ≤ 宿主机可用内存的 15%,避免挤压应用 RSS
Docker Compose 中 tmpfs 和 volume 冲突的静默陷阱
最常被忽略的坑:在 volumes 里声明了 - ./vendor:/app/vendor,又在 tmpfs 里写了 - /app/vendor:size=200m。结果是 volume 数据完全不可见,且 Docker 不报错——tmpfs 会直接覆盖整个挂载点。
正确分离方式:
- 持久数据(如上传文件、数据库)→ 用
volumes - 临时数据(缓存、解压、vendor 生成)→ 用
tmpfs,路径不能与 volume 重叠 - 如果非要复用 vendor 目录,删掉 volume 声明,改用构建阶段预装 + 多阶段 COPY
检查是否生效:进容器执行 mount | grep tmpfs,确认每条路径的 size= 值和挂载选项匹配;再跑 php -r "echo sys_get_temp_dir();" 和 composer config -g cache-dir,输出必须是你挂载的路径。











