不能直接在 deploy:prepare 里运行 composer install,因为该阶段代码尚未进入独立版本目录,执行会污染 shared/vendor,导致多版本依赖冲突、缓存不一致及无法回滚;正确做法是在 deploy:symlink 前、新 release 目录中独立执行 composer install,并排除 vendor/ 同步、合理配置 shared_files 和 shared_dirs 以保障零停机。

为什么不能直接在 deploy:prepare 里运行 composer install
因为 Deployer 默认的部署流程是先拉取代码到新版本目录,再软链接切换,而 composer install 需要写入 vendor/ 和生成 autoload.php,如果在 deploy:prepare 阶段执行,会污染共享目录(比如 shared/vendor),导致多版本共用同一份依赖,引发类加载冲突或缓存不一致。更严重的是,若 Composer 安装失败,部署流程已推进到一半,无法回滚干净。
正确做法:在新版本目录内独立执行 composer install
Deployer 的 deploy:publish 前有一个天然时机 —— deploy:symlink 之前,此时新版本目录(如 releases/20241105123456)已存在且完整,但尚未上线。应在此阶段进入该目录执行安装:
- 使用
within()显式切换工作路径,避免误操作共享目录 - 加
--no-dev --optimize-autoloader --no-interaction,跳过开发依赖、生成高效 autoload map、防止交互阻塞 - 指定
--no-scripts可选,避免触发post-install-cmd中可能存在的非幂等操作(如清缓存、生成静态文件)
before('deploy:symlink', 'deploy:composer:install');
task('deploy:composer:install', function () {
$releasePath = get('release_path');
within($releasePath, function () {
run('composer install --no-dev --optimize-autoloader --no-interaction');
});
});
如何确保 vendor/ 不被重复上传或同步
Deployer 默认会 rsync 整个 Git 工作区,但 vendor/ 体积大、易变、不应进 Git。必须排除它,否则每次部署都传几百 MB,拖慢速度还可能覆盖刚装好的依赖:
- 在
deploy.php中设置set('rsync_exclude', ['vendor/', 'node_modules/', '.git/']); - 确认
.gitignore已包含vendor/,否则deploy:update_code可能因未跟踪文件报错 - 不要把
vendor/放进shared_dirs—— 共享 vendor 是反模式,不同 release 可能需要不同依赖版本
零停机关键:shared/.env 和 storage/ 的原子切换
Composer 安装本身不造成停机,但后续配置和存储目录若处理不当,会导致新旧版本读写冲突。例如 Laravel 的 storage/logs/ 若未共享,新 release 会新建日志目录,旧进程还在往老目录写;又如 .env 若硬编码在 release 目录,每次部署都要手动改,极易出错。
- 将
.env放入shared_files,Deployer 会自动软链接到每个 release - 将
storage/、bootstrap/cache/加入shared_dirs,确保所有版本共用同一份运行时数据 - 注意:Laravel 的
bootstrap/cache/config.php等缓存文件需在composer install后、symlink前重新生成,否则新版本可能读到旧缓存
真正容易被忽略的是:某些框架(如 Symfony)的 var/cache/ 目录权限或用户归属变化后,新 release 的 cache warmup 可能失败,需在 deploy:composer:install 后追加 cache:warmup 并确保用户一致。










