如何将Composer集成到Deployer部署脚本中实现零停机更新?

17次阅读

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

如何将Composer集成到Deployer部署脚本中实现零停机更新?

为什么不能直接在 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/.envstorage/ 的原子切换

Composer 安装本身不造成停机,但后续配置和存储目录若处理不当,会导致新旧版本读写冲突。例如 laravelstorage/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 并确保用户一致。

text=ZqhQzanResources