GitLab CI/CD vs 手动操作:效率对比实测
为什么我开始做这个对比
上个月我们团队接了一个中等规模的项目,三个开发人员,每次发版流程是这样的:本地跑测试、手动打包、SSH 到服务器、备份旧版本、上传新包、重启服务、手动验证。整个过程我一个人操作大概要 25 分钟,而且有一次凌晨发版,我因为困了忘了备份,回滚花了两个小时。
第二天我下定决心:要么把流程自动化,要么我早晚要出事。但我也很好奇——引入 GitLab CI/CD 到底能省多少时间?网上都说“效率提升巨大”,但很少有人给出真实的数字。所以我做了一个月的对比实测,把每次部署的时间都记了下来。
环境说明
- 项目:Node.js 后端 + Vue 前端,单体应用
- 代码托管:自建 GitLab 16.x(公司内网服务器,4 核 8G)
- 部署目标:2 台阿里云 ECS
- 测试:Jest 单元测试 + 一套 API 冒烟测试
第一步:记录手动操作的基线
我先花了一周时间,把手动部署的每个步骤计时:
| 步骤 | 耗时 |
|---|---|
| 本地跑完整测试 | 6 分钟 |
| 手动构建 Docker 镜像并推送 | 4 分钟 |
| SSH 到两台服务器,拉镜像、重启 | 5 分钟 |
| 手动冒烟验证 | 3 分钟 |
| 写发版记录、通知群 | 2 分钟 |
| 合计(含切换上下文的隐性时间) | 22–28 分钟 |
更重要的是隐性成本:发版期间我不能干别的,而且发版经常被打断——测试跑到一半有人找我,回来忘了跑到哪一步了。
第二步:搭建最简流水线
我没有一上来就搞复杂的多阶段流水线,先写了一个能跑通的最小版本。项目根目录建 .gitlab-ci.yml:
stages:
- test
- build
- deploy
test:
stage: test
image: node:20
script:
- npm ci
- npm run test:ci
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
build:
stage: build
image: docker:24
services:
- docker:24-dind
script:
- docker login -u $REGISTRY_USER -p $REGISTRY_PASS $REGISTRY_URL
- docker build -t $REGISTRY_URL/myapp:$CI_COMMIT_SHORT_SHA .
- docker push $REGISTRY_URL/myapp:$CI_COMMIT_SHORT_SHA
only:
- main
deploy:
stage: deploy
image: alpine:3.19
before_script:
- apk add openssh-client
- eval $(ssh-agent -s)
- echo "$DEPLOY_SSH_KEY" | tr -d '\r' | ssh-add -
script:
- ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$SERVER_1
"cd /opt/myapp && docker compose pull && docker compose up -d"
- ssh -o StrictHostKeyChecking=no $DEPLOY_USER@$SERVER_2
"cd /opt/myapp && docker compose pull && docker compose up -d"
environment:
name: production
DEPLOY_SSH_KEY、REGISTRY_PASS 这些敏感信息都在 GitLab 项目的 Settings → CI/CD → Variables 里配置,类型选 Masked,日志里就不会明文显示。
我踩的三个坑
坑一:Runner 不够用。 第一次 push 之后流水线卡在 pending 状态半小时。查了半天才发现自建 GitLab 没有注册共享 Runner。我自己在一台 4 核的机器上用 Docker 方式注册了一个:
docker run --rm -it -v gitlab-runner-config:/etc/gitlab-runner \
gitlab/gitlab-runner register
注册时 executor 选 docker,默认镜像填 node:20。
坑二:node_modules 缓存反而更慢。 我最开始加了 cache 配置以为会加速,结果发现缓存的上传下载每次要 90 秒,比直接 npm ci 还慢。后来改成了用 npm ci 配合本地 .npm 缓存目录,才真正提速。缓存不是加了就快的,要实测。
坑三:Docker-in-Docker 权限问题。 build 阶段一直报 Cannot connect to the Docker daemon。原因是我忘了在 variables 里配 DOCKER_TLS_CERTDIR: "",或者说服务里的 dind 没起来。加上:
variables:
DOCKER_TLS_CERTDIR: ""
问题解决。这个问题网上的答案五花八门,最简单的办法就是看 Runner 的日志而不是流水线日志。
第三步:实测数据对比
配置稳定后,我记录了接下来三周的每次部署:
| 指标 | 手动操作 | CI/CD |
|---|---|---|
| 单次部署耗时(人参与时间) | 22–28 分钟 | 2–3 分钟(其中人工时间约 30 秒:merge + 点重试) |
| 机器执行时间 | — | 8–11 分钟 |
| 月部署次数 | 约 8 次 | 40+ 次 |
| 失败后回滚时间 | 30–60 分钟 | 约 2 分钟(重新部署上一个镜像 tag) |
| 凌晨发版需求 | 有 | 无(合并即部署) |
几个值得说的发现:
- 流水线的机器执行时间其实比手动打包还慢(要拉镜像、起容器),但这不重要——因为人不用盯着。实际节省的是“人的时间”,从每次 25 分钟降到 30 秒。
- 部署频率上去了,单次风险反而变小了。 以前一次发版塞五六个功能,现在每次合并一两个小改动,出问题好定位。
- 测试左移的效果超出预期。 以前偶尔有人本地不跑测试就发版,现在流水线里测试不过就部署不了,这个纪律是自动执行的。
进阶优化(第二个月做的)
流水线稳定后我又加了两样东西:
手动确认的部署门禁:生产环境部署前需要点一下:
deploy:
stage: deploy
when: manual
allow_failure: false
失败通知:加了一个 after_script 调用企业微信 webhook,失败时群里直接报警,不用自己去 GitLab 里翻。
诚实的局限性说明
- 前期投入是真的。 我搭第一版流水线花了大约一个整天,加上踩坑调试接近两天。如果你一个月只发一两次版,ROI 是负的。发版频率越高,这个投入越值。
- Runner 维护是持续成本。 我那个共享 Runner 有一次磁盘满了导致所有构建失败,排障花了半小时。共享 Runner 是新的单点。
- CI 日志偶尔会莫名超时,重跑就好了,但原因一直没查清楚,怀疑是内网 GitLab 服务器的并发问题。
- 小团队不需要一开始就上多环境、多阶段流水线。 我见过有人第一天就配了 review apps、canary 部署,两周后没人维护,退回了手动。先跑通 test → build → deploy 三步,够用很久。
我的建议
如果你现在的手动部署超过 10 分钟,或者一周发版超过 2 次,值得花一两天把 GitLab CI/CD 搭起来。从最小可用的三阶段流水线开始,用真实数据对比一周,你会自己得出结论的——我的结论是:那 25 分钟,我再也回不去了。