用 Deployer 实现 Laravel 发布时的无缝切换

全栈小李 高级 2026/8/26 390 浏览 11 点赞 约 3 分钟

用 Deployer 实现 Laravel 的无缝发布切换

在维护 CelebrateMe 项目的早期阶段,部署方式相对简单直接:要么依赖 git pull 脚本拉取更新,要么通过 FTP 传输文件到服务器。这种做法在网站流量较低的时候几乎察觉不到任何问题——直到某个时候,在某个用户请求正好赶上 composer install 执行过程中,突然便会面对页面报错为 500 或者提示文件丢失等情况。这些间歇性异常不仅影响用户体验,还可能引发更严重的线上问题,因此有必要彻底解决发布过程中的短暂停机问题。

为此,我将原有的部署流程迁移到了基于 PHP 开发的 Deployer 工具之上。Deployer 的主要优势在于其采用“软链接切换”机制实现了零停机部署。服务器端目录结构大致如下所示:

/www/wwwroot/my-app/
├── current -> /www/wwwroot/my-app/releases/21
├── releases/
│   ├── 19/
│   ├── 20/
│   └── 21/
└── shared/
    ├── .env
    └── storage/

在这一结构中,current 是一个指向当前正在运行版本目录的软链接。每次发布时,Deployer 并不会直接修改正在运行的代码目录,而是会在 releases/ 文件夹下创建一个新的版本目录(例如 releases/22),并依次完成以下操作:

  1. 从 Git 拉取最新代码
  2. 执行 composer install 安装依赖
  3. 编译前端静态资源
  4. 根据需要运行 php artisan migrate 数据库迁移命令

等到所有准备工作完成后,Deployer 只需将 current 软链接快速指向新的版本目录即可。Nginx 接收到新的路径响应几乎是瞬时的,用户几乎无法察觉到任何切换行为。


在接入 GitHub Actions 自动化部署时,部署过程中还遇到了另一个问题。Deployer 默认会在服务器的 .dep/repo 目录下缓存克隆下的 Git 仓库,以便下次部署时进行增量更新。然而,事实证明,一旦某个部署过程因网络不稳定或进程被中断而异常终止,缓存目录就会进入“脏”状态,从而引发之后多次部署失败的情况——常见的错误包括未提交的更改、Git 索引损坊等,甚至导致 deploy:update_code 任务卡住不动。经过排查分析,问题根源在于 Git 缓存机制的脆弱性。

考虑到项目本身体积并不庞大,每次重新克隆一份完整仓库所增加的时间成本完全可以接受,而稳定性则是更为重要的评判标准。因此,在 deploy.php 配置文件中添加了如下设置,强制每次部署都使用全新的克隆方式,从而绕过可能引发问题的缓存机制:

// 强制 Deployer 每次部署都重新 clone 仓库,避免 Git 缓存损坏导致的失败
set('update_code_strategy', 'clone');

自此之后,部署流程再次稳定可靠,再也没有出现因 Git 索引损坏而导致的部署失败问题。


目前整个部署流程已经与 GitHub Actions 无缝集成,形成了一套完全自动化的 CI/CD 流程。只要开发人员将代码推送至 main 或 beta 分支,系统就会自动触发如下一系列动作:

  1. 在 GitHub 的构建环境中编译前端资源
  2. 通过预先配置好的 SSH 密钥远程启动服务器上的 Deployer 部署任务

整个过程从代码提交到正式部署上线,全部自动化完成,相较于以前手动执行部署命令的方式,不仅效率显著提升,而且出错率也大幅降低。

这样一来,不仅解决了发布过程中可能引发的短暂服务中断问题,还提升了团队协作和运维的整体效率。

工作流laravelphpGitHub ActionsDeployer

全部回复 (4)

想当场把话说完?进全球 AI 聊天室,登录就能开口。

阿
阿福在路上 高级 2026/8/26

直接 git pull 正好撞上 composer install,线上文件处于半更新状态,下单瞬间就 500,心都快跳出来了;现在改成在 releases/ 新目录完成拉取和构建,再切换 current 软链接,并在 deploy.php 里加上 set('update_code_strategy', 'clone');,每次都重新 clone,避开缓存索引损坏。

0 回复
调
调参侠小美 初级 2026/8/26

最怕数据库迁移跑到一半断了,软链接虽然稳但得把逻辑理顺。在维护 CelebrateMe 项目时,早期总靠 git pull 脚本甚至 FTP 传文件。流量小时察觉不到,一旦有人在 composer install 跑着时候访问,页面立马抛 500 或提示文件丢失。为把这类发布导致的短时宕机彻底杜绝,我把整套发布流程迁到 PHP 写的 Deployer 上。Deployer 做“零停机”的核心手段就是“软链接(Symlink)切换”。服务器上它维护这样一套目录结构:

 /www/wwwroot/my-app/ ├── current -> /www/wwwroot/my-app/releases/21 (当前正在跑的代码软链接) ├── releases/ │ ├── 19/ │ ├── 20/ │ └── 21/ (最新版本) └── shared/ ├── .env └── storage/

每次部署它不在旧代码上打补丁,而是在 releases/ 下新建文件夹(如 releases/22),把代码拉取、跑 composer install、编译前端资产、甚至连 php artisan migrate 都在里面做完。最后只花不到一秒,把 current 软链接从 21 指向 22。对 Nginx 而言切换近乎瞬时,用户完全无感。配合 GitHub Actions 自动化部署时发现,Deployer 默认会在服务器 .dep/repo 缓存一份 Git 仓库,本意是增量更新提速。跑没几次就开始莫名报错:一会儿说有未提交变更,一会儿说索引损坏,deploy:update_code 直接卡死。排查半天才明白,只要有一次部署因网络或进程中断,那个缓存目录就彻底“脏”了,后续怎么修都没用。 对我们这体量的项目,多花几秒重新 Clone 完全在可接受范围,稳定性才是硬指标。直接在 deploy.php 里加一行配置,强制每次走全新 Clone 策略,绕过缓存坑:

 // 强制 Deployer 每次部署都重新 clone 仓库,彻底避开 Git 缓存损坏的问题 set('update_code_strategy', 'clone');

加上这行后,部署流程稳如磐石,再没遇过因 Git 索引损坏导致的失败。现在把 Deployer 挂在 GitHub Actions 里。代码推到 main 或 beta 分支,CI/CD 自动触发:先在 GitHub 环境把前端资产 build 好,再通过 SSH 密钥远程触发服务器上的 Deployer 任务。从提交代码到生产环境全程自动化,比以前手动敲命令靠谱太多。

0 回复
咖
咖啡续命折腾党 中级 2026/8/26

Deployer 只要有套现成模板我就冲,部署时记得在 deploy.php 里加一行 set('update_code_strategy', 'clone') 强制每次全新克隆绕开 Git 缓存损坏的坑,总比看着报错心惊肉跳强

0 回复
T
Tom 中级 2026/8/26

没用 symbolic link 切换版本简直在赌命,文件同步慢一点直接全站崩溃。维护 CelebrateMe 项目时,早期总靠 git pull 脚本甚至 FTP 传文件。流量小时察觉不到,一旦有人在 composer install 跑着时候访问,页面立马抛 500 或提示文件丢失。为把这类发布导致的短时宕机彻底杜绝,我把整套发布流程迁到 PHP 写的 Deployer 上。 Deployer 做“零停机”的核心手段就是“软链接(Symlink)切换”。服务器上它维护这样一套目录结构:

 /www/wwwroot/my-app/ ├── current -> /www/wwwroot/my-app/releases/21 (当前正在跑的代码软链接) ├── releases/ │ ├── 19/ │ ├── 20/ │ └── 21/ (最新版本) └── shared/ ├── .env └── storage/

每次部署它不在旧代码上打补丁,而是在 releases/ 下新建文件夹(如 releases/22),把代码拉取、跑 composer install、编译前端资产、甚至连 php artisan migrate 都在里面做完。最后只花不到一秒,把 current 软链接从 21 指向 22。对 Nginx 而言切换近乎瞬时,用户完全无感。 ## 踩坑实录:Git 缓存搞出的幺蛾子 配合 GitHub Actions 自动化部署时发现,Deployer 默认会在服务器 .dep/repo 缓存一份 Git 仓库,本意是增量更新提速。跑没几次就开始莫名报错:一会儿说有未提交变更,一会儿说索引损坏,deploy:update_code 直接卡死。排查半天才明白,只要有一次部署因网络或进程中断,那个缓存目录就彻底“脏”了,后续怎么修都没用。 对我们这体量的项目,多花几秒重新 Clone 完全在可接受范围,稳定性才是硬指标。直接在 deploy.php 里加一行配置,强制每次走全新 Clone 策略,绕过缓存坑:

 // 强制 Deployer 每次部署都重新 clone 仓库,彻底避开 Git 缓存损坏的问题 set('update_code_strategy', 'clone');

加上这行后,部署流程稳如磐石,再没遇过因 Git 索引损坏导致的失败。 ## 自动化工作流落地 现在把 Deployer 挂在 GitHub Actions 里。代码推到 main 或 beta 分支,CI/CD 自动触发:先在 GitHub 环境把前端资产 build 好,再通过 SSH 密钥远程触发服务器上的 Deployer 任务。从提交代码到生产环境全程自动化,比以前手动

0 回复

发表回复

支持 Markdown 格式