Jenkins 迁移到 GitHub Actions 值不值?8 人团队 90 天账单、并发参数和 5 个坑

🔑 关键词:DevOps,Jenkins迁移,GitHub Actions,CI/CD,自托管Runner

📖 摘要:从 8 人团队真实迁移出发,对比 Jenkins、GitLab CI、GitHub Actions 的并发、缓存、成本、权限和回滚,给出可复制的步骤和参数,不是泛泛而谈。

Jenkins 迁移到 GitHub Actions 值不值?8 人团队 90 天账单、并发参数和 5 个坑

图片

先说结论:如果你只有 8-15 人,已经有 K8s,但 CI 还在 Jenkins 上跑,迁移到 GitHub Actions 或 GitLab CI 通常值。但值不值不取决于 YAML 写起来爽不爽,而取决于三件事:回滚时间有没有降,密钥有没有少,半夜被叫醒次数有没有变少。我们团队 8 个人,后端 4、前端 2、测试 1、运维 1,2023 年 Q3 把 43 个 Jenkins Job 迁到 GitHub Actions,90 天账单从 287 美元降到 94 美元,CI 从 11 分 42 秒降到 4 分 18 秒。听起来像广告,但中间踩的坑比省下的钱多。

图片

先交代原始环境,不然对比没意义。Jenkins 2.401.3 LTS,跑在 AWS t3.medium,2 vCPU 4GB,EBS gp3 100GB。Controller 上装了 27 个插件,其中 Kubernetes plugin 用来动态起 agent,agent 镜像 2.3GB,拉一次 40-90 秒。白天常驻 2 台 c5.xlarge agent,4 vCPU 8GB,按需 0.17 美元/小时,每天 10 小时,22 个工作日,两台就是 74.8 美元。加上 controller 30 美元、EBS 8 美元、NAT 网关 32 美元、S3 缓存 3 美元,月账单大约 287 美元。最要命的不是钱,是 Jenkinsfile 里藏着 12 个定时任务、8 个上游触发、5 个手动审批,没人说得清谁依赖谁。

图片

迁移步骤我按能复制的顺序写。第 1 步不是写 workflow,是冻结隐式依赖。我们把 43 个 Job 导出成 CSV,字段包括 Job 名、触发方式、平均耗时、失败率、下游 Job、负责人。最后发现 9 个 Job 半年没跑,直接删。第 2 步,把构建、测试、打包拆成独立 job,所有 job 加 timeout-minutes: 15,避免卡死占并发。第 3 步,缓存分三层:npm 依赖、Docker 层、构建产物。actions/cache@v3 缓存 ~/.npm,key 用 runner.os + hashFiles('**/package-lock.json')。Docker 用 docker/build-push-action@v5,cache-from: type=gha,cache-to: type=gha,mode=max。第 4 步,密钥别放 secrets 里长期躺着,用 OIDC 换 AWS 临时凭证。workflow 里加 permissions: id-token: write,AWS IAM role 信任 GitHub OIDC provider,条件写 repo:org/repo:ref:refs/heads/main。第 5 步,部署别让 CI 直接 kubectl apply,我们保留 Argo CD 做 GitOps,CI 只把镜像 tag 推到配置仓库。

对比几个工具,我不喜欢那种“功能大表格”,只看你疼的地方。Jenkins 强在插件多、老系统兼容、自托管无限并发,但弱在权限模型粗、审计靠插件、升级容易崩。GitHub Actions 强在离代码近、矩阵构建、OIDC、托管 runner 省心,但弱在私有仓库分钟数计费、自托管 runner 要自己管、日志搜索一般。GitLab CI 强在一体化、review app、K8s agent,但弱在 SaaS runner 分钟数贵,自托管 runner 要维护。Argo CD 和 Flux 不是 CI,它们是部署控制器。我们最后组合是 GitHub Actions 做 CI,Argo CD 做 CD,ECR 做镜像仓库,OIDC 做权限。这个组合不是最先进,但故障路径最短。

图片

说 5 个坑。坑 1:GITHUB_TOKEN 默认权限。2023 年组织可以设默认只读,我们 push 镜像时报 403,后来在 workflow 顶部加 permissions: contents: read, packages: write。坑 2:actions/cache 不是永久缓存。仓库缓存 10GB,7 天没访问就清。我们 monorepo 缓存 2.8GB,命中率 78%,不是 100%。坑 3:自托管 runner 用 Spot 会中断。c5.xlarge Spot 约 0.05 美元/小时,但中断后 job 失败。我们改成 K8s deployment 跑 actions runner controller,runner 加 --ephemeral,副本 2-6 个,按 CPU 70% 扩容。坑 4:upload-artifact@v4 不能同名覆盖。矩阵构建 node 16/18/20 时,产物名要写 dist-${{ matrix.node }},不然互相覆盖。坑 5:环境审批在私有仓库要 Team 以上。我们 production environment 配 required reviewers,2 人审批,但免费版没有这个功能。

图片

新观点:DevOps 选型别先看“部署频率”,先看“回滚时间”。部署频率可以靠小改动刷上去,回滚时间才是真功夫。我们定义回滚时间 = 发现故障 + 找到上一个稳定版本 + 执行回滚 + 验证。目标 10 分钟以内。做法很土:镜像 tag 用 git sha 短 7 位,部署清单里 image tag 也写 sha,Argo CD 里 argocd app history 能看到历史,argocd app rollback 一条命令回。数据库迁移用 expand-contract,先加列不删列,回滚不碰 schema。这样即使 CI 花 20 分钟,回滚只要 3 分钟,团队才敢发。

图片

最后给一个可抄的 GitHub Actions 骨架。on: push: branches: [main],pull_request: branches: [main]。concurrency: group: ci-${{ github.ref }},cancel-in-progress: true。jobs 里 build 用 ubuntu-22.04,strategy matrix node: [18,20],steps 用 actions/checkout@v4,actions/setup-node@v4,cache: npm,run: npm ci,run: npm test。build 之后 package 只跑一次,用 needs: build,if: github.ref == 'refs/heads/main'。部署 job 用 environment: production,permissions: id-token: write, contents: read。这套不 fancy,但我们 90 天里没有一次因为 CI 工具本身导致 P1。Jenkins 时代有过 3 次,都是插件升级和磁盘满。

🏷️ 标签: