从 Jenkins 迁移到 GitHub Actions:决策框架与方案对比

从 Jenkins 迁移到 GitHub Actions:决策框架与方案对比

本文是《统一 CI/CD 流水线治理》系列第四篇,也是最后一篇。前三篇分别介绍了为什么要统一管理、Jenkins 如何实现、GitHub Actions 如何实现。本文的目的是帮助你做出有依据的决策:要不要迁移,以及如何迁移。本文来自一个覆盖 500+ 仓库、运行 2 年以上的生产实践——迁移决策不是纸上谈兵,而是真实发生过的权衡。


一、先说结论:Jenkins Shared Library 没有错

在讨论迁移之前,必须明确一件事:Jenkins Shared Library 是成熟的、经过验证的方案。大量组织基于它稳定运行多年,它解决了统一 CI/CD 治理的核心问题,有完整的生态支持。

迁移本身有真实的成本:

  • 重写成本:Jenkins Groovy 逻辑需要翻译成 GitHub Actions YAML + shell,不是简单的格式转换
  • 培训成本:团队需要重新学习 GitHub Actions 的概念和调试方式
  • 验证成本:需要并行运行两套流水线对比结果,确保没有行为差异
  • 历史数据:Jenkins 的 build 历史、artifact、部署记录无法迁移
  • 500+ 仓库的迁移规模:500 个仓库的迁移不是一次 sprint 能完成的,需要以月到年为单位规划

不要为了迁移而迁移。 本文的目的是给出有依据的决策框架,而不是推销迁移。


二、两种方案的本质差异(深度对比)

2.1 流水线结构:动态 vs 静态

这是两种方案最本质的技术差异。

Jenkins:运行时动态结构

1
2
3
4
5
6
7
8
9
10
11
12
13
// JenkinsStageGenerator:stage 结构在运行时决定
void run(Map config, String vaultToken) {
stage('Lint') { ... } // 总是运行

if (config.jobs.find { it.name == 'unit-test' }) {
stage('Unit Test') { ... } // 条件运行
}

// 业务仓库可以在 config.yaml 中声明任意数量的自定义 stage
config.jobs.findAll { it.name.startsWith('custom-') }.each { job ->
stage(job.name) { runCustomJob(job) } // 动态 stage
}
}

Jenkins UI 中,没有单元测试的仓库根本不显示 “Unit Test” stage。

GitHub Actions:编译时静态结构

1
2
3
4
5
# workflow YAML 在解析时就固定了 job 结构
jobs:
unit-test:
if: ${{ needs.config.outputs.has_unit_test == 'true' }} # 只能跳过,不能"不存在"
...

GitHub Actions 能跳过 job,但 job 始终出现在 workflow 定义中。你无法在 YAML 中声明”如果条件满足才有这个 job”。

500+ 仓库规模下的实际影响: 对 95% 的业务场景,”可以跳过”和”不存在”没有区别。但在 500+ 仓库规模下,如果有几十个仓库需要声明任意数量的自定义 stage(数据库迁移、E2E 测试、多阶段部署),Jenkins 方案天然支持,GitHub Actions 需要用 matrix 或其他 workaround,且 UI 体验差距明显。

实际数据: 在 500+ 仓库的平台中,大约 5-10% 的仓库使用了自定义 stage,这部分是迁移最复杂的场景——不是不能迁,而是迁移后的 YAML 体积会显著膨胀,且动态性降低。

2.2 凭证安全模型:静态 vs 动态

维度Jenkins AppRoleGitHub Actions JWT/OIDC
静态凭证SecretID 存在 Jenkins Credential Store无静态凭证
凭证范围所有 stage 共享同一 Vault token每个 sub-workflow 独立认证
Token 生命周期TTL 1小时(可配置)5分钟 batch token,不可续期
泄漏可查是(可从 Vault audit log 追踪)batch token 不出现在 accessors
Groovy 代码读取凭证理论上可以(sh 'printenv'不适用
分支隔离无(任何分支都能使用同一 SecretID)@refs/heads/main branch lock

500+ 仓库规模下的安全放大效应:

AppRole 的核心风险在于 SecretID 全局共享。当平台覆盖 500 个仓库时:

  • 任意一个仓库的 Groovy Pipeline 理论上都能访问共享 SecretID
  • SecretID 轮换需要协调所有 500 个仓库同时不在认证——在高频 CI 环境中,轮换窗口极难控制
  • 500 个仓库并发触发 AppRole 登录时,Vault 限流导致大量认证失败(见第三节)

GitHub Actions 的 job_workflow_ref + @refs/heads/main 后缀强制要求所有 workflow 修改经过代码审查后才能访问 Vault,这个安全边界是由 Vault 的 JWT bound_claims 在认证层强制执行的,workflow 代码本身无法绕过:

1
2
3
4
5
6
7
8
9
10
攻击者修改了 .github 仓库的 feature 分支中的 workflow 文件


job_workflow_ref = "OrgA/.github/.github/workflows/platform-ci-build.yml@refs/heads/attacker-branch"


Vault 验证:bound_claims 要求 @refs/heads/main


✗ 不匹配 → 403 → 无法获取任何 secrets

500 个仓库,无一存储任何凭证——这是 JWT/OIDC 方案在 500+ 规模下的核心安全价值。

2.3 基础设施负担

维度JenkinsGitHub Actions
主节点维护需要(JVM 调优、OOM 排查、插件管理)不需要(GitHub 托管)
执行节点维护Kubernetes 集群(Pod 调度、镜像更新)self-hosted runner(如需内网资源)
插件生态数百个插件,版本兼容性需要测试Actions Marketplace,版本独立
升级影响Jenkins 版本升级可能破坏 PipelineGitHub API 向后兼容,很少破坏性变更

500+ 仓库规模下的真实运维成本:

Jenkins 的典型运维事件(已在 500+ 仓库规模实际发生):

  • 每季度 1-2 次 Kubernetes Plugin 升级导致 Pod 调度失败,影响全量 CI(排查 2-4 小时,但全 500 仓库停摆)
  • 每年 1-2 次 Jenkins 主节点 OOM(高峰期 100-200 个并发 Pipeline,JVM 堆耗尽,排查 + 恢复 4-8 小时)
  • 每月 1-2 次 某个插件与新版 Jenkins 不兼容,需要降级(排查 1-2 小时)
  • Vault 限流:500+ 仓库同时 push 时(早高峰),AppRole 并发认证可能触发 Vault 限流,导致数百个 Pipeline 认证失败

GitHub Actions self-hosted runner 的典型运维事件:

  • 每季度 1 次 runner 软件更新(5 分钟脚本自动完成,对运行中 job 无影响)
  • 偶发 runner 网络超时(重跑 job 通常即可解决)
  • runner 容量:500+ 仓库需要规划充足的 runner 数量,防止高峰排队

2.4 开发者体验

这个维度的差异对工程师的日常工作影响最大,在 500+ 仓库规模下,支持成本直接受此影响

Jenkins:

  • CI 结果在 Jenkins UI 中,PR 页面只显示”succeeded/failed”
  • 查看失败详情需要跳转到 Jenkins(可能需要额外登录)
  • 日志在 Jenkins 的 Blue Ocean 或 Classic 界面,与代码审查流程割裂
  • 失败原因经常是”某个 stage 失败”,需要逐层展开才能看到具体错误

GitHub Actions:

  • CI 结果直接显示在 PR 的 Checks 标签页
  • 每个 step 的日志可以直接在 PR 页面展开查看
  • 与代码审查、comments、assignee 完全一体化
  • 失败的 step 有直接的”Re-run”按钮
  • Job Summary 页面可以展示结构化报告(配置解析结果、覆盖的模块列表等)

量化影响(500+ 仓库规模):

平台团队的调查显示,排查一次 CI 失败的平均时间:

  • Jenkins:8-12 分钟(包括切换平台、找到对应 build、定位 stage)
  • GitHub Actions:3-5 分钟(直接在 PR 页面展开)

在 500+ 仓库、每天数百次 CI 运行的规模下,如果每次 CI 失败平均少花 6 分钟排查,100 人工程团队每周节省约 50-100 人时。这个数字能直接支撑迁移投入的 ROI 计算。

2.5 配置合并能力

维度Jenkins Groovy MergerGitHub Actions shell + yq
类型安全是(Groovy 强类型)否(shell 字符串处理)
单元测试是(JUnit + Spock)较难(需要 bats 或 Python 测试)
复杂合并规则容易(Groovy 列表操作)需要手动处理 null、特殊字符、多行值
调试println、Groovy consoleecho + GITHUB_STEP_SUMMARY

shell 实现脆弱的场景:

1
2
3
# 当 pylint sourceSets 中包含路径含有空格时,tr '\n' ' ' 处理后的结果可能被 shell 分词
SOURCES=$(yq '.check.pylint.sourceSets[]' config.yaml | tr '\n' ' ')
# 如果某个路径是 "src/my module",分词后会变成两个参数

500+ 仓库的边界情况放大效应: 在 500 个仓库中,任何 config.yaml 格式的边界情况都一定会出现——某个团队用了带空格的路径、某个团队用了多行 YAML 字符串、某个团队的字段值包含了 shell 特殊字符。Groovy 的类型系统在 500 个仓库的输入多样性下提供了更好的保证,而 shell + yq 方案需要更充分的测试覆盖。

综合对比表

维度Jenkins Shared LibraryGitHub Actions Reusable Workflow
流水线结构动态(运行时决定)静态(解析时固定)
凭证安全AppRole(静态 SecretID,500 仓库共享)JWT/OIDC(无静态凭证)
基础设施负担高(主节点 + K8s 集群)低(仅 runner)
开发者体验独立平台,上下文切换与 GitHub PR 一体化
配置合并类型安全,可单元测试shell + yq,较脆弱
动态自定义 Stage原生支持不支持(只能跳过)
生态集成数百个成熟插件Marketplace 快速增长
500+ 规模 OOM 风险高(Jenkins 主节点 JVM)无(GitHub 托管计算)

三、迁移的真实驱动力

驱动力一:代码托管平台统一(运维简化)

维护两套系统(Jenkins + GitHub Enterprise)意味着:

  • 两套权限管理(新成员入职需要申请两个平台的访问权限)
  • 两套审计日志(安全审计需要关联两个系统的记录)
  • 两套事故响应(Jenkins 故障和 GitHub 故障是不同的响应流程)
  • 两套监控配置

在 500+ 仓库规模下,这个运维开销被进一步放大:两套系统的权限同步(离职人员需要从两个系统清除)、两套 CI 状态监控(某个仓库 CI 挂了,需要判断是 Jenkins 问题还是 GitHub 问题)。

如果代码已经全部迁移到 GitHub Enterprise,继续维护独立的 Jenkins 实例的边际价值在降低。

驱动力二:凭证安全合规要求

某些合规框架(SOC 2 Type II、ISO 27001)对静态凭证有明确要求:

  • 必须定期轮换(通常 90 天)
  • 必须有使用审计记录
  • 必须限制访问范围

500+ 仓库规模下的 AppRole 合规挑战:

AppRole 的 SecretID 满足这些要求需要额外的运维流程(定期轮换脚本、轮换审计)。在 500+ 仓库的高频 CI 环境中,90 天轮换意味着每次轮换都需要:

  1. 选择低峰窗口(避免影响太多正在运行的 Pipeline)
  2. 更新 Jenkins Credential Store
  3. 验证新 SecretID 对所有 Org 的 Vault role 都生效
  4. 监控接下来 24 小时是否有认证失败

JWT/OIDC 的回答是”不存在可泄漏的长期凭证”——这在合规审计场景下是更简洁的答案。对 500+ 仓库的平台,这个答案可以减少每季度约 1-2 天的合规运维工作。

驱动力三:Jenkins 基础设施到达生命周期

当出现以下信号时,迁移的 ROI 开始变得合理:

  • Jenkins 主节点 OOM 频率从每年 1 次增加到每月 1 次
  • Kubernetes Plugin 升级在每次 Jenkins 版本更新后都需要手动回滚
  • 负责维护 Jenkins 实例的工程师离职,知识传承困难
  • Jenkins LTS 版本接近 EOL(End of Life)

500+ 仓库规模的特殊信号:

  • 早高峰 Vault 限流成为常态(每天早晨都需要手动清理或等待)
  • Jenkins 主节点需要持续的 JVM 调优(堆大小已超过 16GB 仍然 OOM)
  • Pod 调度失败的排查成本超过了新功能开发的投入

这不是 Jenkins 的根本性问题,而是任何有状态基础设施在超规模使用时都会遭遇的生命周期问题。

驱动力四:开发者生产力

量化这个驱动力的方法:

  • 统计过去 3 个月,工程师在排查 CI 失败上花费的时间(通过 Jira ticket 或 Slack 记录)
  • 统计平台团队在维护 Jenkins 基础设施上花费的时间
  • 与迁移成本对比

500+ 仓库规模的具体估算:

1
2
3
4
5
6
7
8
假设:
500 个仓库,平均每天每仓库 5 次 CI 运行 = 2500 次/天
CI 失败率 10% = 250 次失败/天
Jenkins 排查时间:10 分钟/次
GitHub Actions 排查时间:4 分钟/次
节省时间:6 分钟/次 × 250 次/天 = 25 小时/天

年化节省(工作日 250 天) = 6250 小时/年

即便实际数字只有估算的 1/10,每年节省 625 小时也足以支持迁移投入。


四、不值得迁移的场景(重要)

场景一:动态 Stage 生成是核心需求,且覆盖大量仓库

如果你的平台允许业务团队在 config.yaml 中声明任意的自定义 stage,且这个能力被 超过 10% 的仓库 广泛使用,GitHub Actions 无法原生复制这个功能。

具体例子:某个业务仓库声明了 10 个数据库迁移 stage,每个对应一个目标环境,stage 名称和数量在运行时从配置文件生成。Jenkins 的 StageGenerator 一行代码搞定,GitHub Actions 需要复杂的 matrix 配置,且在 UI 展示上差距明显。

500+ 仓库下的评估方法:

1
2
3
4
5
6
7
8
9
# 检查有多少仓库使用了 custom- 开头的 stage(即动态 stage)
gh repo list OrgA --limit 1000 --json name \
| jq -r '.[].name' \
| while read repo; do
if gh api "repos/OrgA/${repo}/contents/.ci-config/config.yaml" --silent 2>/dev/null \
| jq -r '.content' | base64 -d | grep -q 'custom-'; then
echo "${repo}: 使用了自定义 stage"
fi
done | wc -l

如果输出超过 50 个仓库,动态 stage 迁移的工作量可能超过其他所有迁移工作之和。

场景二:流水线配置合并逻辑极其复杂

如果你的 ConfigMerger 已经积累了大量处理特殊场景的 Groovy 逻辑(递归合并、引用解析、条件覆盖),这些逻辑翻译成 shell + yq 不只是工作量大,还会降低可维护性和可测试性。

Groovy 的类型系统和 Jenkins 的测试工具链(JenkinsPipelineUnit)在这个场景下有不可替代的价值。在 500+ 仓库的输入多样性下,未经充分测试的 shell 配置解析可能引发大量偶发性问题。

场景三:深度依赖 Jenkins 特定插件

以下插件目前没有成熟的 GitHub Actions 替代:

  • Build Failure Analyzer:自动分析失败原因并分类
  • Performance Plugin:CI 性能趋势分析
  • Warnings Next Generation:多工具警告汇聚和趋势分析
  • Configuration as Code(JCasC):Jenkins 配置版本化管理

如果这些插件提供的功能是你的工作流核心,迁移会有明显的功能缺失。

场景四:Jenkins 运行稳定,团队熟悉,没有明显痛点

这是最重要的场景——如果现状运转良好,不要迁移

评估”是否有痛点”的具体问题(针对 500+ 仓库规模):

  1. 过去 6 个月,Jenkins 基础设施故障导致多少次全量 CI 中断(影响 500 个仓库)?
  2. 过去一年,Vault 限流导致多少次 Pipeline 批量失败?
  3. 工程师是否在抱怨 CI 排查困难、需要切换平台?
  4. 凭证轮换是否按时完成,还是一再推迟?
  5. Jenkins 主节点 JVM 堆是否持续满负荷运行?

如果这五个问题的答案都是”没有”或”很少”,迁移的 ROI 很可能是负的。

ROI 计算框架(500+ 仓库版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
迁移成本(一次性):
- 平台工程师重写流水线代码:8-12 人周
- 500 个仓库迁移:
* 无自定义 stage 仓库(约 90%):每仓库 30 分钟 = 225 人时
* 有自定义 stage 仓库(约 10%):每仓库 4 小时 = 200 人时
* 合计:约 425 人时(约 53 人天)
- 并行验证周期:4-8 周(双轨运行成本)
- 培训(500 仓库的工程师):1小时/人 × N 人

持续收益(年化):
- 减少 Jenkins 维护事件:6-10 次/年 × 4 小时/次 = 24-40 小时/年
- 减少 CI 故障排查时间:见第三节驱动力四估算
- 减少 Vault 限流影响:季度轮换 × 2 天/次 = 8 天/年
- 消除 Jenkins 主节点 OOM 风险

迁移 ROI = 持续收益 / 迁移成本(以年计)

五、如果决定迁移,如何降低风险(500+ 仓库策略)

策略一:分波次迁移,而非大爆炸

在 500+ 仓库规模下,”一次性迁移所有仓库”不是一个可执行的方案。分波次策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
第 0 阶段(1-2 个月):搭建 GitHub Actions 平台框架
- 实现 platform-ci-core.yml 及所有 sub-workflow
- 确保与 .ci-config/config.yaml 接口完全兼容
- 针对 5-10 个内部测试仓库验证

第 1 波(2-3 个月):新仓库 + 低风险存量仓库(约 100 个)
- 所有新创建仓库默认 GitHub Actions
- 迁移活跃度低、业务影响小的存量仓库
- 目标:积累 6+ 个月的运行数据,发现并修复边界情况

第 2 波(3-6 个月):中等重要性仓库(约 250 个)
- 无自定义 stage 的标准仓库
- 并行运行 2 周后切断 Jenkins

第 3 波(6-12 个月):核心仓库 + 复杂仓库(约 150 个)
- 有自定义 stage 的仓库(需要单独评估)
- 高可见性的核心业务仓库(需要最充分的验证)
- 部分仓库可能永久保留 Jenkins(见共存方案)

策略二:并行运行期验证

在正式切换前,让 Jenkins 和 GitHub Actions 同时运行,对比每次 build 的结果:

1
2
3
4
5
6
7
8
# 业务仓库的 ci.yml 临时配置
jobs:
# 新流水线(GitHub Actions)
platform-gha:
uses: OrgA/.github/.github/workflows/platform-ci-core.yml@main

# 旧流水线仍在 Jenkins 运行(通过 Jenkins GitHub Plugin 触发)
# 两者结果在 PR 的 Checks 中并排显示

只有当 GitHub Actions 结果稳定等于 Jenkins 结果至少 2 周、10 次以上 CI 运行后,才切断 Jenkins。

500+ 仓库的注意事项: 不要在整个 500 个仓库上同时启动并行运行——双倍的 CI 负载会对 runner 容量和 Vault 造成额外压力。按波次启动并行运行,每波完成后再切断 Jenkins 连接,释放资源。

策略三:保留 .ci-config/config.yaml 接口不变(关键)

这是降低业务团队迁移成本的最重要决策。如果 .ci-config/config.yaml 的结构保持兼容,业务团队的迁移工作只是:

  1. 创建一个新的 .github/workflows/ci.yml(15 行)
  2. 删除旧的 Jenkinsfile
  3. .ci-config/config.yaml 完全不动

业务团队的感知: CI 结果显示在不同的地方了,流水线快了,Jenkinsfile 不见了。其余一切不变。

在 500 个仓库中,这种无感迁移意味着每个仓库的迁移不需要专门的协调会议——平台团队准备好后,由业务团队在自己的节奏内完成两步操作。

策略四:通过 Org Variable 控制切换

利用 GitHub Enterprise 的 Org Variable 机制,实现不修改业务仓库代码的路由控制:

1
2
3
4
# 不同 Org 代表不同迁移阶段
# OrgA-Dev:新仓库优先接入 GitHub Actions(Dev 环境)
# OrgA-Stg:存量仓库逐步迁移(Staging 环境)
# OrgA:最后切换核心 prod 仓库(生产环境)

Org Variable(VAULT_ENVVAULT_URL 等)在不同 Org 中配置不同值,业务仓库的 ci.yml 代码完全相同,平台行为根据 Org 上下文自动路由。这是 500+ 仓库分阶段迁移的基础设施保证——你可以在不同 Org 处于不同迁移阶段时,平台仍然统一维护一套代码。

策略五:批量检查迁移进度

在 500 个仓库的迁移过程中,需要工具化监控迁移状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash
# 检查各仓库的迁移状态:Jenkins only / GitHub Actions only / Both(并行)/ Neither
ORG="OrgA"
echo "=== 迁移状态报告 ==="

gh repo list "${ORG}" --limit 1000 --json name \
| jq -r '.[].name' \
| while read repo; do
HAS_JENKINSFILE=$(gh api "repos/${ORG}/${repo}/contents/Jenkinsfile" --silent 2>/dev/null && echo "yes" || echo "no")
HAS_GHA=$(gh api "repos/${ORG}/${repo}/contents/.github/workflows/ci.yml" --silent 2>/dev/null && echo "yes" || echo "no")
HAS_CONFIG=$(gh api "repos/${ORG}/${repo}/contents/.ci-config/config.yaml" --silent 2>/dev/null && echo "yes" || echo "no")

if [ "${HAS_JENKINSFILE}" = "yes" ] && [ "${HAS_GHA}" = "no" ]; then
echo "[Jenkins only] ${repo}"
elif [ "${HAS_JENKINSFILE}" = "no" ] && [ "${HAS_GHA}" = "yes" ]; then
echo "[GHA only] ${repo}"
elif [ "${HAS_JENKINSFILE}" = "yes" ] && [ "${HAS_GHA}" = "yes" ]; then
echo "[Parallel run] ${repo}"
elif [ "${HAS_CONFIG}" = "yes" ]; then
echo "[No CI!] ${repo}"
fi
done | sort | tee migration_status.txt

echo ""
echo "统计:"
grep -c "\[Jenkins only\]" migration_status.txt | xargs echo " Jenkins only:"
grep -c "\[GHA only\]" migration_status.txt | xargs echo " GitHub Actions only:"
grep -c "\[Parallel run\]" migration_status.txt | xargs echo " 并行运行中:"
grep -c "\[No CI!\]" migration_status.txt | xargs echo " 无 CI(需要接入):"

这个脚本在 500 个仓库的迁移过程中,是平台团队每周例会的标准输入。

策略六:保留回滚方案(30 天窗口)

在切换后的 30 天内,保留 Jenkins Job 但暂停触发:

1
2
// 旧 Jenkinsfile 重命名为 .jenkinsfile.bak 保留在仓库中
// 如果 GitHub Actions 出现严重问题,可以临时恢复文件名并在 Jenkins 手动触发

500+ 仓库的回滚策略: 不要在整个 500 个仓库上同时删除 Jenkins 配置。按批次删除,每批次等待 30 天观察期。一旦出现平台级问题(比如 runner 容量不足导致排队严重),可以将该批次已迁移的仓库先回滚,同时扩容 runner,再重新迁移。


六、决策树(500+ 仓库版)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Q1: 你的仓库数量是否超过 50 个?

├─ 否 → 统一管理的收益可能低于成本,参考小规模建议

└─ 是 → Q2: Jenkins 是否运行稳定(过去一年全量 CI 中断 ≤ 2 次)?

├─ 是 → Q3: 是否有明确的凭证安全合规要求(SOC 2 / ISO 27001)?
│ │
│ ├─ 否 → Q4: 工程师排查 CI 失败是否需要频繁切换平台,
│ │ 且团队规模 > 50 人?
│ │ │
│ │ ├─ 否 → 继续使用 Jenkins,每年评估一次
│ │ └─ 是 → 计算年化节省时间(见第三节驱动力四)
│ │ 若 > 500 小时/年,评估迁移 ROI
│ │
│ └─ 是 → Q5: 超过 10% 的仓库依赖动态 Stage 生成?
│ │
│ ├─ 是 → 维持 Jenkins,加固 AppRole 轮换流程
│ │ (动态 stage 迁移成本过高)
│ └─ 否 → 推荐迁移,优先从 Dev/Stg 环境 Org 开始

└─ 否(Jenkins 不稳定)→ Q6: 不稳定原因是否是规模性问题?
│ (OOM、Vault 限流、插件兼容)

├─ 否(随机故障)→ 先解决根本原因,再评估迁移

└─ 是(规模性问题)→ Q7: 超过 10% 的仓库依赖动态 Stage?

├─ 是 → 迁移部分仓库(无自定义 stage),
│ 保留复杂仓库在 Jenkins
└─ 否 → 强烈推荐迁移
Jenkins 维护成本已超过迁移成本

七、两种方案能否共存?(500+ 仓库的现实选择)

可以,且在 500+ 仓库规模下,这往往是比”完全迁移”更现实的长期策略。

永久共存的合理场景

  • 动态 Stage 仓库永留 Jenkins:约 5-10% 的仓库因为大量自定义 stage,迁移成本远超收益,接受永久双系统
  • 新项目全走 GitHub Actions,存量仓库按节奏迁移:不设硬性截止日期,随仓库的正常维护周期自然迁移
  • 不同 CI 复杂度分流:Jenkins 处理复杂的多环境部署流水线,GitHub Actions 处理标准构建 + 测试

.ci-config/config.yaml 的战略价值

这个接口文件是两套系统可以共存的关键:

1
2
3
4
5
6
7
8
9
# .ci-config/config.yaml 的结构对两套系统都有效
containers:
- name: project-runtime
image: platform-registry.example.com/python:3.12
jobs:
- name: lint
steps:
- pyLint:
sourceSets: [src/]
  • Jenkins 的 ConfigMerger.groovy 读取它
  • GitHub Actions 的 config job 的 shell 脚本读取它

业务团队不需要知道底层是哪套系统——这是接口设计的价值所在。当平台团队决定在某个仓库上切换底层 CI 引擎时,业务团队唯一的感知是”CI 结果显示的位置变了”。

长期策略建议(更新的规模阈值)

规模建议策略
< 20 个仓库维持现状,不引入新复杂度
20-100 个仓库新仓库 GitHub Actions,存量仓库按需迁移
100-500 个仓库制定正式波次迁移计划,保留混合架构过渡期(1-2 年)
> 500 个仓库混合架构可能是永久状态;以接口统一(.ci-config/config.yaml)代替技术统一

在 500+ 仓库规模下,追求 100% 迁移不是目标——目标是让业务团队有统一的 CI 接入体验,同时让平台团队的维护工作收敛。即使 10% 的仓库永远留在 Jenkins,只要接口统一,这 10% 的维护成本是可接受的。

可观测性:双系统监控

在混合架构运行期间,平台团队需要统一的 CI 健康视图,跨越两套系统:

1
2
3
4
5
需要回答的问题:
- 今天 Jenkins CI 失败了多少次?GitHub Actions 失败了多少次?
- 两套系统的平均耗时对比?
- 哪些仓库的 Jenkins CI 超过 30 天没有运行(可以加速迁移)?
- 当前双轨运行的仓库数量(并行验证进度)?

这需要 Jenkins 的 build 数据和 GitHub Actions 的 workflow 数据汇聚到同一个 dashboard(如 Grafana),以避免平台团队在两个系统之间手动关联信息。


结语

本系列四篇文章从”为什么”出发,经过 Jenkins 和 GitHub Actions 的技术实现,到今天的迁移决策框架,试图回答一个完整的问题:如何在 500+ 仓库的工程组织中治理 CI/CD 流水线,以及在不同阶段应该选择什么工具。

核心结论:

  1. 统一管理的价值是真实的——分离关注点让平台团队和业务团队各自专注,这个价值在 500+ 仓库时乘以 500 倍的规模效应
  2. Jenkins Shared Library 是成熟可靠的方案,不是”旧技术”;在 500+ 规模下它的挑战是运维复杂度,而非功能缺失
  3. GitHub Actions 的 JWT/OIDC + Reusable Workflow 在凭证安全和开发者体验上有实质性优势,且在 500+ 规模下这些优势被进一步放大
  4. 迁移的决策应该基于 ROI,而不是技术时髦性——500 个仓库的迁移是一个以年为单位的工程项目
  5. .ci-config/config.yaml 这样的接口设计让平台迁移对业务团队透明——这是 500+ 仓库规模下迁移可行性的关键设计决策
  6. 混合架构可能是 500+ 仓库规模下的长期现实——以接口统一代替技术统一,是更务实的目标

无论你今天的选择是什么,最重要的原则是:让业务团队只需要声明意图,不需要理解实现。这个原则在 Jenkins 和 GitHub Actions 中都适用,在 5 个仓库和 500 个仓库时都成立。