3

git push 之后发生了什么

·5 分钟

博客搭好之后,还得把它弄到服务器上去。

手动部署的时候

一开始的部署方式很原始:SSH 到服务器,git pull 拉最新代码,docker build 构建镜像,docker-compose up 启动容器。每次改完代码想看线上效果,就走一遍这个流程。

不复杂,但繁琐。每次都是同样的几条命令,同样的等待。改个错别字也要 SSH 上去操作一遍。

听说 CI/CD 特别香

CI/CD 在社区里传得神乎其神——推一下代码就自动构建、自动部署、自动上线。出于体验和学习的目的,我决定试试。

选了 GitHub Actions,因为代码本来就托管在 GitHub 上,不用额外搭服务。企业级项目通常用 Jenkins 或 GitLab CI,但一个人的博客项目确实没那个必要。

搞好之后的流程变成了:本地 git push 到 master,GitHub 检测到推送,自动触发工作流。工作流做四件事:

  1. 拉代码
  2. 构建 Docker 镜像
  3. 把镜像压缩传到服务器
  4. 在服务器上重启容器

中间的 Docker 构建分两个阶段——第一阶段在 Node.js 环境里跑 npm run build,把所有 MDX 文件编译成静态 HTML,同时生成 Pagefind 搜索索引;第二阶段把这些静态文件塞进一个 Nginx 镜像。最终的镜像里没有 Node.js、没有源代码、没有 node_modules——只有静态文件和 Nginx,干干净净。

确实方便。从此改完东西 git push 就行,不用 SSH 上去手动操作了。

GitHub 的 runner 有点烦

不过 GitHub 自带的 runner 惹了个小麻烦:每次跑工作流分配的机器 IP 都不一样,我的服务器在阿里云上,安全组检测到陌生 IP 的 SSH 连接就告警。每次 push 就收一封告警邮件,很烦。

解决办法是 self-hosted runner——不用 GitHub 分配的机器,在自己的机器上跑。我把 runner 装在了本地的 kirara 上,IP 固定,阿里云认识它,不再告警了。

本地 runner 还有个附带好处:构建缓存是持久的。GitHub 的 runner 每次都是全新环境,npm install 要重新下载所有依赖;本地 runner 有上次的缓存,依赖没变就直接跳过,能省不少时间。

部署脚本里还踩过一个小坑:docker image prune 清理旧镜像的时候,如果容器刚启动还没稳定,有概率把正在用的镜像也清掉。加了一个 sleep 5 等容器跑稳,再用 --filter "dangling=true" 只清理没有标签的悬空镜像,问题就没再出过了。

没有想象中那么快

CI/CD 解决了"每次 SSH 上去手动操作"的问题,但速度上有落差。

一次完整的构建加部署,从 git push 到线上更新,要好几分钟。时间主要花在两个地方:npm run build 要编译所有页面生成静态文件,镜像传输要走 docker save 压缩再 SSH 传过去。runner 的 CPU 感觉也没跑满,在等 IO。

对比本地开发,改一行代码 dev server 热重载,不到十秒就能看到效果。中间差了一个数量级。

不过话说回来,agent 直接在服务器上跑脚本其实更快。CI/CD 的价值不在于快,在于"我不用记部署步骤了"——push 就完事。对于一个人维护的项目,这个程度的自动化够用了。

💬

留言区待配置

部署 Twikoo 后端后,设置环境变量 NEXT_PUBLIC_TWIKOO_ENV_ID