别再用 git pull 部署 Laravel 了

全栈小李 高级 2天前 305 浏览 11 点赞 约 2 分钟

在公司维护 CelebrateMe 这个项目的时候,我以前习惯用那种最原始的 git pull 脚本或者直接 FTP 上传。这种方式在小流量时没感觉,但只要遇到用户正好在 composer install 运行的时候访问,网站就会直接报 500 或者各种文件缺失错误。为了彻底解决这种由于代码更新导致的短暂宕机问题,我把部署流程全换成了 PHP 写的 Deployer。

虽然 Deployer 实现“零停机”的逻辑很硬核,但实际落地到生产环境时,我还是被两个坑折腾得够呛,尤其是 Git 缓存和 Horizon 队列的问题。

它是怎么实现零停机的?

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 仓库。它不是每次都重新 Clone,而是基于这个缓存去增量更新。

结果没跑几次,部署就开始莫名其妙报错。报错信息一会儿说有未提交的变更,一会儿说 Git 索引损坏,导致 deploy:update_code 这一步直接卡死。查了半天才发现,只要有一次部署因为网络或进程中断了,那个 .dep/repo 缓存目录就彻底脏了,后续怎么修都没用。

对于我们这种规模的项目,多花那几秒钟重新 Clone 根本不是问题,稳定性才是第一位的。我直接在 deploy.php 配置文件里加了一行,强制它每次都走全新的 Clone 策略,直接绕过了这个缓存坑:

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

加上这一行后,部署流程变得极其稳健,再也没见过那种因为 Git 索引损坏导致的部署失败。

自动化工作流建议

我现在是把 Deployer 挂载在 GitHub Actions 里的。每当我把代码推送到 mainbeta 分支,CI/CD 就会自动触发:先在 GitHub 环境里把前端资产 build 好,然后通过 SSH 密钥远程触发服务器上的 Deployer 任务。这种从代码提交到生产环境全自动化的实操,确实比以前手动敲命令要靠谱得多。

工作流laravelphpGitHub ActionsDeployer

全部回复 (4)

阿福在路上 高级 2天前
我也踩过这坑,更新时刚好有人下单,那一瞬间直接宕机,换了软链接方案后稳多了。
0 回复
调参侠小美 初级 2天前
软链接确实稳,不过你当时有没有配置好数据库迁移的逻辑?要是迁移一半断了也挺麻烦。
0 回复
咖啡续命折腾党 中级 2天前
确实,每次更新都怕报错。不过Deployer配置起来麻烦吗?有没有现成的模板?
0 回复
T
Tom 中级 2天前
配合 symbolic link 切换版本才稳,我之前就是因为没做软链接,更新时文件没同步完导致报错。
0 回复

发表回复

支持 Markdown 格式