账单逐月上涨,构建队列越排越长,但每个改动看起来都很合理。本文从成本结构出发,讲清托管 Runner 与自建 Runner 的真实账怎么算,给出杠杆最大的四个优化动作,以及安全与可观测性的三条底线。
CI/CD 的问题很少出在”跑不起来”,而是出在跑得太贵、太慢,且没人说得清贵在哪。账单逐月上涨,构建队列越排越长,但每个改动看起来都很合理。
本文从成本结构出发,讲清托管 Runner 与自建 Runner 的真实账怎么算,以及几个杠杆最大的优化动作。
一、先把成本结构拆开
讨论”哪家便宜”之前,得先知道钱花在哪。CI 的成本基本由三块构成:

1. 计算时间:最直观的一块
托管 Runner 按构建分钟数计费,且不同规格的机器系数不同(高核大内存机器通常是标准机的数倍系数)。这意味着把一个任务从 4 核挪到 16 核,价格不是线性增长,很容易在无意中把账单推高。
2. 并发数:最容易被忽略的隐性成本
很多人只盯着单价,忽略了并发额度。团队规模上来之后,真正卡住交付的不是单任务价格,而是队列等待——开发者提交后等 20 分钟才开始跑,这个成本远高于账单上多出的那点钱。
3. 存储与流量:缓存、制品、镜像
缓存、构建产物、容器镜像的存储与跨区流量,在规模上来之后会变成一笔可观的固定支出。尤其容器镜像仓库,如果没有清理策略,几个月就能堆到需要专门治理。
二、托管 Runner 还是自建?
这个问题没有普适答案,但有清晰的判断框架。
托管 Runner 划算的场景
- 团队规模小、构建量波动大:按需付费天然适配波峰波谷,不用为闲置付费;
- 没有专职运维:机器维护、镜像更新、安全补丁都不用管;
- 构建环境标准化:依赖官方提供的运行环境,不需要特殊的系统级依赖。
自建 Runner 划算的场景
- 构建量大且稳定:当月度分钟数超过某个阈值后,自建的边际成本优势会迅速显现;
- 需要特殊硬件或内网资源:要连内网数据库做集成测试、需要 GPU、需要特定的操作系统;
- 构建极重:大型 C++ / 移动端项目单次构建几十分钟,托管方案的性价比会明显下降;
- 合规要求:代码与制品不能离开自有基础设施。
一个务实的判断方法
不要凭感觉决策。先导出过去三个月的构建分钟数、并发峰值、存储用量,按自建方案的机器成本 + 运维人力折算成月度总成本,再和托管账单对比。多数团队算完会发现:临界点在”每月几千到上万构建分钟”这个量级,低于它托管更省心,高于它自建更划算。
还有一条常见的最优路径是混合模式:常规任务跑托管,重任务(移动端打包、全量编译)跑自建。既控制运维面,又掐掉账单上最贵的那部分。
三、杠杆最大的四个优化动作
按投入产出比排序,前两项几乎总是值得做。
1. 缓存:收益最大,且改动小
CI 里最典型的浪费是每次都重新下载全部依赖。正确配置缓存后,很多项目的构建时间能砍掉一大半。
# 思路示例:以依赖清单的哈希作为缓存键
- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: deps-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
deps-${{ runner.os }}-
关键点有两个:缓存键要绑定依赖清单的哈希(依赖变了缓存自动失效),以及配置 restore-keys 做前缀回退(依赖小改时仍能命中大部分缓存)。
2. 路径过滤:Monorepo 的救命稻草
Monorepo 里”改了前端却触发后端全量测试”是巨大的浪费。用路径过滤把变更范围和任务集关联起来:
# 只有 packages/web 下有变更时才跑前端任务
- uses: dorny/paths-filter@v3
id: changes
with:
filters: |
web:
- 'packages/web/**'
- if: steps.changes.outputs.web == 'true'
run: npm run test:web
在大仓库上,这一项的收益经常超过所有其他优化的总和。
3. 失败优先与快速反馈
把快且高失败率的任务放在最前面(lint、类型检查、单元测试),慢任务(端到端测试、打包)放后面。这样大部分错误能在几分钟内反馈,而不是让人等半小时才发现少了个分号。
# 取消同一分支上已被新提交取代的旧构建
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
4. 矩阵构建的维度控制
多版本 × 多平台的矩阵很容易膨胀。实用的做法是主流程只跑代表性组合(比如最新稳定版 + 最低支持版),完整矩阵放到定时构建里跑,而不是每次提交都跑全。
四、安全:三条必须守住的线
CI 是供应链攻击的高价值目标,这三条不能省:
- 第三方 Action 固定到具体 commit SHA,而不是 tag。tag 可以被移动,SHA 不能;
- 密钥通过平台的加密机制注入,绝不写进配置文件或代码。同时按环境拆分权限,生产环境的凭证不要对所有分支可见;
- 限制 fork PR 的Runner权限,避免外部提交触发的构建拿到写权限或生产密钥。
五、可观测性:先量再优化
优化的前提是知道时间花在哪。最低要求是让流水线自己产出每个步骤的耗时统计,并跟踪几个趋势指标:P50/P95 构建时长、月度构建分钟数、缓存命中率、队列等待时长。
其中缓存命中率最值得盯——它一旦下滑,通常意味着依赖清单在无意识地频繁变动,或者有任务的缓存键写错了。
六、小结
CI/CD 的成本问题,本质上很少是”选错了平台”,而多是没有缓存、没有路径过滤、没有并发控制这三件事没做。把这三件做对,账单和构建时长通常都能有数量级的改善。
至于托管还是自建,拿三个月的历史数据算一遍再决定——这个计算本身花不了半小时,但能避免长期在错误的方向上省钱。
#CI/CD#工程效能#成本优化#DevOps

