GitHub Actions vs 手动操作:一次真实的效率对比实测
缘起:一次深夜的部署事故
去年11月的一个周五晚上,我正准备关电脑回家,突然接到同事电话:线上环境的一个接口报500。排查下来发现,是我下午手动部署时,忘了先跑数据库迁移脚本——本地测试时我跑过了,但部署到生产环境时,这一步全靠人脑记忆。
那次事故让我痛定思痛:我们团队每次发布的流程是这样的——
- 本地跑测试(约3分钟)
- 手动构建 Docker 镜像并推送到镜像仓库(约4分钟)
- SSH 到服务器,拉取镜像、重启服务(约2分钟)
- 手动验证接口是否正常(约2分钟)
整套流程约11分钟,看似不长,但它完全依赖“人记得每一步”。我们团队5个人,每个人对流程的理解都有细微差异,出问题只是时间问题。
于是我决定把整个流程迁到 GitHub Actions,并且做一次严格的效率对比,用数据说话,而不是凭感觉。
实测前的准备
我把对比分成三个维度:
- 单次操作耗时
- 每周累计耗时(我们每周大约发布8次,加上若干次 CI 触发)
- 出错率(统计过去两个月的发布事故)
先说手动操作的基线数据。我用 time 命令配合手动计时,记录了5次完整的发布流程:
第1次:11分42秒
第2次:10分55秒
第3次:13分08秒(测试环境一次失败,重跑)
第4次:11分20秒
第5次:12分15秒(SSH 连接超时重试了一次)
平均约 11分52秒。而且注意,这还不算“人切换上下文”的隐性成本——部署期间我基本干不了别的事。
第一步:写 workflow 文件
我们的项目是 Node.js + Docker,workflow 文件放在 .github/workflows/deploy.yml:
name: Deploy
on:
push:
tags:
- 'v*'
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test
- name: Build and push image
run: |
docker build -t registry.example.com/myapp:${{ github.ref_name }} .
docker login registry.example.com \
-u ${{ secrets.REGISTRY_USER }} -p ${{ secrets.REGISTRY_PASSWORD }}
docker push registry.example.com/myapp:${{ github.ref_name }}
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: deploy
key: ${{ secrets.SSH_KEY }}
script: |
cd /opt/myapp
docker compose pull
docker compose up -d
./scripts/migrate.sh
我踩的两个坑
坑一:Secrets 没配对,部署卡在登录环节。 第一次跑直接失败,日志显示 docker login 返回 unauthorized。排查了半天发现是我把 registry 密码存成了 REGISTRY_PASS,workflow 里写的是 REGISTRY_PASSWORD。GitHub Actions 不会告诉你“这个 secret 不存在”,它只会传空字符串进去。建议:配完 secrets 后先写一个临时的 step 打印 secrets 变量是否为空(注意不要打印值本身),确认名称无误。
坑二:迁移脚本权限问题。 ./scripts/migrate.sh 在服务器上没有执行权限,workflow 报 permission denied。我本地一直用的是 bash scripts/migrate.sh,所以从没暴露过这个问题。chmod +x 解决。
实测结果对比
配置修好之后,我连续记录了5次自动化的发布耗时(从打 tag 到部署完成):
第1次:6分31秒
第2次:5分58秒
第3次:6分12秒
第4次:6分45秒(服务器拉镜像稍慢)
第5次:6分05秒
平均约 6分14秒。
表面上看只快了5分钟左右,但真正的差异在别处:
1. 我的耗时是0。 那11分钟里,我过去是全程盯着的;现在打一个 tag(git tag v1.4.2 && git push --tags),人就可以去干别的了。按每周8次发布算,每周省下约 95分钟的专注时间。
2. 流程不可跳过。 这是最重要的。手动时代,“忘了跑迁移”这种事随时可能发生;现在迁移脚本是 workflow 里的一步,想跳过都难。
3. 回滚更快。 手动回滚要 SSH 上去翻历史镜像;现在直接把旧 tag 重新部署即可,一分钟内完成。
4. 出错率。 迁移后两个月,发布相关事故为0。之前两个月是3次(都是流程步骤遗漏)。
一个意外收获:缓存的重要性
第一版 workflow 跑一次要9分钟,比手动还慢。原因是每次都重新 npm ci。加上 cache: 'npm'(配合 setup-node)之后,依赖安装从约90秒降到15秒左右。如果你也要做迁移,先加缓存再对比,否则结论会失真。
实用建议
- 别一次全自动化。 先只自动化测试和构建,部署保留手动触发(用
workflow_dispatch),跑稳定两周再放开自动部署。 - 给关键 step 加通知。 我们在部署完成后加了一个飞书 webhook 通知,失败时立刻能收到。
- Secrets 管理要有规范。 统一命名前缀(如
PROD_、STAGING_),避免我犯的那种拼写错误。 - 保留一个手动逃生通道。 GitHub 偶尔有故障,SSH 直连的技能不能丢。
诚实的局限性
GitHub Actions 不是银弹:
- 私有仓库有免费额度限制,我们每月大约用掉2000分钟,超出部分要付费。如果团队发布频率更高,成本需要评估。
- 调试体验不如本地。 Workflow 报错时只能看日志反复改、反复跑,一个 YAML 缩进错误可能折腾你20分钟。可以用
act工具在本地跑 workflow 缓解,但兼容性不完美。 - 它不解决“流程设计错误”。 我们那次迁移事故的根因是流程本身有缺陷,Actions 只是让缺陷无法被人为绕过。如果你的流程本身是错的,自动化只会更快地做错事。
总结:这次实测让我把每周约95分钟的机械劳动换成了写一次 workflow 的两小时投入,两周回本。更重要的是,“发布”从一件需要勇气的事变成了一个 tag 命令。如果你团队还在手动部署,我强烈建议从最小的一步开始试。