运行时执行composer install在云原生环境必然失败,因readOnlyRootFilesystem导致file_put_contents报错;正确做法是在CI构建阶段执行composer install并固化vendor到镜像,运行时只读挂载。

运行时执行 composer install 会直接失败
云原生环境(尤其是 Kubernetes)常启用 readOnlyRootFilesystem: true,此时容器根文件系统被挂载为只读。若在启动脚本中误写 composer install,会立刻报错:file_put_contents(/app/composer.lock): failed to open stream: Read-only file system。这不是权限配置问题,而是设计误用——composer install 必须发生在构建阶段,而非运行时。
vendor 目录必须在构建镜像时完成写入
正确路径是:CI 流程中拉取代码 → 执行 composer install --no-dev --optimize-autoloader → 将完整 vendor/ 打包进镜像 → 运行时只读挂载该镜像。关键点:
-
composer.lock必须提交到 Git,否则不同环境依赖不一致 - 不要在 Dockerfile 中用
RUN composer install之后再ADD . .,这会导致 vendor 被覆盖 - 若需动态加载插件(如自定义 autoloader),应通过
composer dump-autoload --classmap-authoritative提前固化,避免运行时写入vendor/composer/autoload_classmap.php
私有仓库认证不能写进 auth.json 提交
当项目依赖私有 GitLab 或 GitHub Package Registry 时,auth.json 若明文存于仓库,会泄露凭据。而只读文件系统又不允许运行时生成它。解决方案只有两个:
- CI 构建时通过
--auth参数注入(如composer install --auth gitlab-token=xxx),凭据不落地 - 或在构建阶段用 CI secret 挂载临时
auth.json到/root/.composer/auth.json,安装完立即清理,不打包进镜像
切勿尝试在容器启动时用 echo '{...}' > /root/.composer/auth.json —— 只读文件系统下该命令必然失败。
扩展或二进制依赖的权限陷阱
有些 Composer 包(如 h4cc/wkhtmltopdf-amd64)会在 vendor/bin/ 放可执行文件。即使镜像构建完成,若宿主机 SELinux 启用或容器以非 root 用户运行,仍可能提示 Permission denied。这时要确认:
- Dockerfile 中是否显式设置了
RUN chmod +x vendor/bin/wkhtmltopdf - 是否用了
USER 1001但未将该 UID 加入文件所属组(如chown -R 1001:1001 vendor/) - 某些 OCI 镜像扫描工具会标记
vendor/bin/下的二进制为高风险,需在 CI 中提前声明白名单
最稳妥的做法,是在构建阶段就验证可执行性:RUN vendor/bin/wkhtmltopdf --version || exit 1,早发现问题,别留到运行时才暴露。


















