Composer本身不支持零停机更新,必须在CI构建阶段执行composer install --no-dev --optimize-autoloader生成不可变artifact,再通过原子软链切换、FPM重载及opcache清理实现蓝绿部署。

Composer 本身不提供应用级热切换能力,所谓“用 Composer 实现蓝绿部署”是常见误解。真正起作用的是部署层的路径隔离 + 原子链接 + FPM 重载,Composer 只负责在构建阶段生成确定性依赖快照。
为什么不能在运行中执行 composer install 切换版本
现场运行 composer install 会直接写入 vendor/ 目录,导致正在处理请求的 PHP-FPM worker 进程加载失败或类冲突:
-
Class not found或require(): failed to open stream—— 文件被删/覆盖瞬间,worker 仍在读旧路径 - opcache 缓存了旧字节码,即使文件更新也不会自动刷新(除非
opcache.validate_timestamps=1且间隔足够短,但仍有时间窗口) -
vendor/是目录树,无法像单个文件那样用mv原子替换,不存在“热切换”语义 - 若依赖含 C 扩展(如
ext-redis),跨 PHP 版本复用vendor/可能触发Module 'redis' already loaded或undefined symbol
composer install 必须放在 CI 构建阶段,且参数不能省
生产环境禁止出现 composer install 命令。它只应出现在 CI 流水线中,且必须带明确参数:
-
--no-dev:避免把phpunit、phpstan等开发依赖打进生产包,增大攻击面并引发 autoload 冲突 -
--prefer-dist:优先下载压缩包而非git clone,减少网络不确定性 -
--optimize-autoloader:生成vendor/composer/autoload_classmap.php,QPS 提升约 8–12%(实测 Laravel 9 + PHP 8.2) - 构建前确保
composer.lock已提交,且含platform锁定(如"php": "8.2.12"),防止因本地 PHP 版本差异导致依赖解析不同
软链接切换后,必须清理 opcache 并 reload FPM
仅改 current 软链不够。PHP 进程仍缓存旧代码和 autoload 映射:
立即学习“PHP免费学习笔记(深入)”;
- 执行
sudo systemctl reload php*-fpm强制 worker 重启(推荐),或调用opcache_reset()CLI 脚本 - 不要依赖
opcache.revalidate_freq=0生产启用 —— 每次请求都校验时间戳会显著拖慢响应 - 如果用了
--optimize-autoloader,上线后需确认vendor/composer/autoload_classmap.php被新路径下的 FPM worker 正确加载;旧进程若未退出,仍可能按老映射找类 - 检查
opcache.validate_root是否为On,否则软链切换后 opcache 可能仍指向原物理路径
Docker 场景下,vendor 必须打进镜像,不能挂载宿主机
使用 docker-compose 做蓝绿时,常见错误是把 vendor/ 挂载成 volume:
- 挂载宿主机
vendor/会让两个服务容器共享同一份依赖,失去环境隔离意义 - 镜像构建阶段就要跑
composer install --no-dev --optimize-autoloader,把vendor/固化进镜像层 - 蓝绿切换实际是
docker-compose up -d green启动新服务,再通过反向代理(如 Nginx)切upstream,不是改软链 - 务必在
Dockerfile中设WORKDIR /var/www/app,并在构建时COPY . .后立即执行composer install,避免分层缓存污染
最难的部分从来不是怎么写 composer.json,而是让每个 PHP-FPM worker 在切换瞬间加载正确的 vendor/ 和 opcache 条目——这要求构建、部署、运行时三者严格对齐,缺一不可。



















