DevOps:从理念、工程实践到平台建设
DevOps:从理念、工程实践到平台建设
DevOps 要解决什么问题;三步工作法、精益和戴明这几条理论根基;转型该从哪里入手;配置管理、分支、CI、测试、 环境、部署、混沌工程这些工程实践各自解决什么;SRE、可观测性和供应链安全怎么接进来;怎么度量; 企业平台怎么分阶段建设;组织和文化层面的配套。最后给出工具选型和面试高频问题。
主线参考石雪峰《DevOps 实战笔记》(极客时间,2019–2020),并补充 DORA、SRE、GitOps、平台工程、 DevSecOps 等后来成为主流的内容。文中的 YAML、Groovy 配置示例未实测,用于说明结构。
目录
| # | 章节 |
|---|---|
| 一 | DevOps 要解决什么问题 |
| 二 | 理论根基:三步工作法、精益与戴明 |
| 三 | 从哪里开始:转型路径 |
| 四 | 需求侧:业务敏捷与精益看板 |
| 五 | 工程实践:从提交到上线 |
| 六 | SRE 与可观测性 |
| 七 | DevSecOps 与软件供应链安全 |
| 八 | 度量:DORA 指标与度量体系 |
| 九 | 持续改进:PDCA 与复盘 |
| 十 | 平台建设:从工具到平台工程 |
| 十一 | 组织与文化 |
| 十二 | 工具选型速查 |
| 十三 | 面试高频问题 |
| 十四 | 参考资料 |
一、DevOps 要解决什么问题
1.1 软件交付的三个阶段
| 阶段 | 核心理念 | 解决了什么 | 留下的问题 |
|---|---|---|---|
| 瀑布 | 按需求→设计→开发→测试→运维分阶段推进,每阶段有交付物和验收标准 | 大规模软件的工程化管理 | 项目开头就要定死范围,而那时对用户和市场了解最少;变更代价巨大 |
| 敏捷 | 大目标拆成小目标,短迭代、持续验证 | 需求不确定性;测试从末端环节变成贯穿开发 | 只让开发和测试协同,运维一句"没到发布窗口"就能把功能挡在上线门外 |
| DevOps | 打通开发到运维乃至业务的全链路 | 交付的"最后一公里",以及开发与运维的对立 | —— |
敏捷不会让人写代码更快。它快在持续迭代和验证砍掉了大量返工,这一点对 DevOps 同样成立。
1.2 根本矛盾:开发与运维的目标冲突
开发(Dev) 运维(Ops)
考核:交付了多少需求 考核:稳定性、可用性、安全
诉求:尽快上线、多变更 诉求:少变更(变更是故障的第一来源)
│ │
└────────────► 混乱之墙 ◄──────────────┘
(Wall of Confusion)
结果:开发"扔过墙"了事;运维抬高上线门槛、收紧发布窗口 → 交付越来越慢、每次上线越来越大、越大越容易出事
这个循环的出路不是"开发干掉运维",也不是"运维全部转开发",而是让两者共享同一个目标:以可持续的方式,快速、稳定地向用户交付价值。
1.3 时间线
| 年份 | 事件 |
|---|---|
| 2001 | 《敏捷宣言》发布 |
| 2008 | Andrew Shafer 与 Patrick Debois 在 Agile 大会讨论 "Agile Infrastructure" |
| 2009 | Flickr 的 John Allspaw、Paul Hammond 在 Velocity 大会演讲 "10+ Deploys per Day: Dev and Ops Cooperation at Flickr" |
| 2009 | Patrick Debois 在比利时根特举办第一届 DevOpsDays,"DevOps" 一词由此流行 |
| 2010 | Jez Humble、David Farley《持续交付》出版 |
| 2013 | 小说《凤凰项目》出版,提出"三步工作法" |
| 2014 | 第一份《DevOps 状态报告》(State of DevOps Report)发布,此后每年一份 |
| 2016 | 《DevOps 实践指南》(The DevOps Handbook)、Google《SRE》出版 |
| 2018 | 《Accelerate》出版;DORA 被 Google 收购 |
| 2019 | 《Team Topologies》出版;持续交付基金会(CDF)成立 |
| 2020 年代 | GitOps、平台工程(Platform Engineering)、软件供应链安全成为主流议题 |
1.4 DevOps 的定义
DevOps 的发起者们有意没有给出官方定义。一个可操作的描述是:
DevOps 通过平台(Platform)、流程(Process)和人(People)的整合,以 CALMS 为指引, 建设一个能快速交付价值、并具备持续改进能力的 IT 组织。
| CALMS | 含义 | 落地形态举例 |
|---|---|---|
| Culture 文化 | 协作、责任共担、无责复盘 | 开发参与值班;故障复盘不追责个人 |
| Automation 自动化 | 消除手工环节 | CI/CD 流水线、IaC、自动化测试 |
| Lean 精益 | 消除浪费、小批量、限制在制品 | 价值流分析、看板 WIP 限制 |
| Measurement 度量 | 用数据驱动改进 | DORA 指标、价值流指标 |
| Sharing 共享 | 知识、工具、责任共享 | 内部开源、共享的 Runbook |
范围也在不断扩大:业务加入叫 BizDevOps(线上数据回流到需求端,用来评估需求价值),安全加入叫 DevSecOps(把安全反馈注入每个环节,而不是上线前一次性审查)。
二、理论根基:三步工作法、精益与戴明
2.1 三步工作法(The Three Ways)
出自《凤凰项目》和《DevOps 实践指南》,是整个 DevOps 实践体系的骨架:
| 步 | 名称 | 目标 | 典型实践 |
|---|---|---|---|
| 第一步 | 流动(Flow) | 加速从开发到运维的左→右流动 | 工作可视化、限制 WIP、小批量、CI/CD、低风险发布 |
| 第二步 | 反馈(Feedback) | 建立右→左的快速反馈 | 自动化测试、监控告警、质量门禁、安灯绳 |
| 第三步 | 持续学习与实验 | 形成高信任、敢试错的文化 | 无责复盘、预留改进时间、混沌工程、内部分享 |
后文的工程实践基本都能归入这三步。
2.2 精益:价值流、浪费与利特尔法则
精益思想来自丰田生产系统,是 DevOps 最主要的理论来源:
- 价值流:从用户提出需求到用户拿到价值所经过的全部活动。只优化其中一段属于局部优化,整体未必变快
- 浪费:不增值的活动,例如等待、交接、返工、半成品(写完没上线的代码)、多任务切换
- 拉动:下游有能力时才从上游拉取工作,而不是上游把工作推给下游
利特尔法则(Little's Law)描述了在制品与前置时间的关系:
平均在制品数(WIP) = 平均吞吐率 × 平均前置时间
⇒ 平均前置时间 = WIP / 吞吐率
一个团队每周能完成 10 个需求,手上同时开着 30 个,每个需求的平均前置时间就是 3 周。吞吐率不变时,把 WIP 降到 15,前置时间就降到 1.5 周。不加人、不加班,只减少并行,交付就会变快,这就是看板要限制 WIP 的原因。
约束理论(TOC,高德拉特《目标》)指出,系统产出由瓶颈决定。它的改进五步法是持续改进的基础:
- 识别约束
- 充分利用约束(别让瓶颈闲着)
- 让其他环节服从约束(非瓶颈环节不要超产)
- 提升约束(投资扩容)
- 约束被打破后回到第 1 步,不要让惯性成为新约束
2.3 戴明:质量不能靠检验
戴明质量管理 14 条原则的第 3 条:停止依赖检验来达成质量。检验只能证明缺陷存在,不能提升质量,应当把质量内建于流程。
一个常被引用的例子:美国汽车厂在装配线末端安排专人用橡胶锤敲车门检查安装质量,车门质量依然很差;日本工厂没有这个岗位,他们的回答是"设计车门时就保证它不会出问题"。对应到软件就是质量内建(见 5.5 节)。戴明另一项影响深远的贡献是 PDCA 循环(见第九章)。
2.4 康威定律
设计系统的组织,其产生的设计等同于组织的沟通结构。
它的推论对 DevOps 很关键:只要存在独立的测试部门,交付流程中就一定有独立的测试阶段,也就一定有交接和等待。想改变交付流程,往往要先改变组织结构。"逆康威操作"(Inverse Conway Maneuver)就是先按期望的架构调整团队,再让架构跟着演化。
2.5 持续交付的八条原则
出自《持续交付》:
- 为软件发布创建一个可重复且可靠的过程
- 将几乎所有事情自动化
- 把所有东西都纳入版本控制
- 越痛苦的事越要频繁地做
- 内建质量
- "完成"意味着"已发布"
- 交付过程是每个成员的责任
- 持续改进
三、从哪里开始:转型路径
3.1 工具先行还是文化先行
两个极端都会失败:
- 工具决定论:"所有团队都用上 Jenkins 了",但照样按瀑布方式工作;敏捷管理工具被当成任务派发系统用;自研平台只是把线下审批搬到了线上
- 空谈文化:文化不可量化,脱离实践就是"无根之水",组织迟迟看不到收益就会失去耐心
可行的路径是:先改变行为,再通过行为改变文化。改变行为靠机制,即人们愿意做、而且做了有好处的约定。几个机制设计的例子:
| 机制 | 改变的行为 |
|---|---|
| Google SRE:服务先由开发自运维,达到质量标准后才交给 SRE;稳定性不达标会被"打回"开发 | 开发主动关注可运维性,形成责任共担 |
| 错误预算:预算内允许失败、不追责;预算耗尽冻结发布 | 发布速度与稳定性由数据仲裁,而不是由部门博弈决定 |
| CI 红灯 10 分钟内未修复则自动回滚 | 提交者对集成结果负责 |
| 每个迭代固定预留一定比例的工作量给技术改进 | 改进不再被业务需求无限挤占 |
从 人、流程、平台 的两两组合看:
人 ───── 流程 人 + 流程 = 文化(用流程约束和引导行为)
\ / 流程 + 平台 = 工具(平台承载标准化流程)
\ / 平台 + 人 = 赋能(平台让所有人"同样操作得到同样结果")
平台
3.2 成熟度模型怎么用
成熟度模型(如中国信通院的 DevOps 能力成熟度模型,覆盖敏捷开发管理、持续交付、技术运营三大部分)是一张"能力地图",不是考试。用法分四步:
- 识别差距:对照模型盘点现状,建立能力基线
- 锚定目标:只挑出与当前业务瓶颈相关的差距。卖 CRM 软件的公司,容器化未必是眼下的瓶颈。目标要可量化,例如"环境准备时长缩短 50%"
- 关注能力而非数字:亚马逊一天部署上万次,不代表每家公司都需要这个频率。由目标推出所需能力,再导入与能力匹配的实践
- 持续改进:模型本身也在迭代
一个典型案例:某企业用成熟度模型盘点出 100 多个问题点、40 多个差距项,逐项沟通后收敛到 30 个改进事项。其中"环境初始化要 2 周"的根因有两个:一是环境依赖写在一份 40 多页、写完就没人维护的文档里;二是审批链冗长。对策是引入基础设施即代码(环境描述进版本控制,初始化降到分钟级),并按环境分级审批(单次审批当天完成)。环境准备最终从 2 周缩短到 2 天。
3.3 价值流分析:找到真正的瓶颈
价值流图(VSM,Value Stream Mapping)是转型的第一步,用来回答"时间都花在哪了"。它有三个关键要素:
| 要素 | 定义 | 实操建议 |
|---|---|---|
| 前置时间 LT | 从需求提出到交付用户的总时长 | 分两个口径:需求前置时间(从需求创建算起,用户感知的周期)和开发前置时间(从进入开发算起,衡量工程能力) |
| 增值时间 VAT / 不增值时间 NVAT | 真正在加工的时间 vs 等待、交接、返工 | 初期不必细分,先统计等待时长,例如从"就绪"到"开始开发"的间隔 |
| 完成度与准确度 %C/A | 下游不需要打回就能直接使用的比例 | 可以先用"提测一次通过率"之类的质量门禁指标近似 |
由此可以算出流动效率 = VAT / LT。多数团队的流动效率远低于直觉:大部分时间花在排队上,而不是在干活。
flowchart LR
A["需求提出"] -->|"等待 5d"| B["需求评审<br/>加工 1d"]
B -->|"等待 8d"| C["开发<br/>加工 4d"]
C -->|"等待 3d"| D["测试<br/>加工 2d · %C/A 70%"]
D -->|"等待 6d"| E["发布<br/>加工 0.5d"]
E --> F["用户可用"]
上图中 LT ≈ 29.5 天,VAT = 7.5 天,流动效率约 25%。最大的浪费在"评审后等待开发"和"等待发布窗口",而不在开发本身。
数据陷阱:研发上线后才一次性把卡片从"待开发"拖到"已完成",前置时间就只剩几秒;把所有需求一次性拖进"开发中",等待时间就测不出来。解决办法是让状态由系统事件自动流转,例如提测时关联需求自动变为"待测试",合并时自动变为"已完成"。
VSM 最好以工作坊形式开展,让交付链上所有环节的资深成员坐在一起。它的价值不只是找到瓶颈:很多上下游团队是"网友关系",同楼办公却只靠 IM 交流,第一次看到对方的痛点会产生同理心,这对协作的改善往往比数据本身还大。
3.4 转型的通用路径
两种轨迹:
| 轨迹 | 优点 | 风险 | 要点 |
|---|---|---|---|
| 自底向上 | 局部容易见效,资源调动简单 | 局限在小团队,难以扩展 | "羽化原则":先打通本团队与强依赖的上下游,逐步模糊边界;设法让管理层看到成效 |
| 自顶向下 | 资源和权威有保障 | 目标一压下来,团队总能找到某个口径"证明达标" | 需要客观、统一的度量口径。反例:自称前置时间"一周",实际只统计了开发到测试完成,从提需求到上线要两个月 |
无论哪条轨迹,管理层的认可与持续投入都是必要条件。通用路径分四步:
- 选试点:贴近核心业务(关注度高、资源有保障);需求多、变更频繁(容易体现效果);团队有改进意愿(自认为已经很完美的团队不适合)
- 找痛点:在试点团队做一次价值流分析,按约束理论找到最短的那块木板
- 快速建立初期成功:只选一个改进点、一个目标,几周内见到成果。铺得太开是最常见的陷阱
- 展示与扩展:及时向管理层汇报、在内部分享,逐步加大投入
J 型曲线(2018 年 DevOps 状态报告):引入自动化后效能快速提升;随后手工回归测试、架构耦合、技术债等深层问题浮现,效能进入平台期甚至下降;只有持续投入测试自动化、架构解耦等长期建设,才能进入第二次提升。很多转型就死在曲线的谷底,因为业务压力下改进被叫停了。
从中型团队入手(middle-out):微软的经验是先专注 40~100 人的中型团队。最大的核心团队流程最特殊、定制要求最多,而且转型不是他们的最高优先级,常常一拖再拖。中型团队需求明确、资源不够充裕,更愿意配合,做好之后的口碑会带动其他团队主动求助。
3.5 部署引力图:频率提升会牵动什么
把发布频率从"100 天 1 次"提到"1 天 100 次",以下各项都得跟着变:
| 维度 | 低频发布 | 高频发布 |
|---|---|---|
| 分支策略 | 长期特性分支、发布前大合并 | 主干开发、短命分支 |
| 测试 | 手工回归,一轮数天 | 分层自动化,分钟级反馈 |
| 架构 | 大单体,牵一发而动全身 | 解耦、可独立部署 |
| 发布策略 | 停机发布、发布窗口 | 灰度、蓝绿、特性开关 |
| 基础设施 | 手工申请、数周交付 | 自服务、IaC、分钟级 |
| 数据库 | 发布时一起停机变更 | 向后兼容的渐进式迁移 |
| 组织 | 职能型部门、层层审批 | 跨职能团队、自治发布 |
这张表也解释了为什么"只上一套 CI/CD 工具"无法带来质变。
四、需求侧:业务敏捷与精益看板
4.1 为什么需要业务敏捷
交付效率不直接等于业务价值。如果业务方抱着"宁可错杀一千,不可放过一个"的心态提需求,研发做得再快也只是更快地交付了无用功能。业务不敏捷,IT 再努力也没用。产品需求管理的三个核心思想是:开发更少的功能、聚焦用户价值、持续快速验证。
| 工具 | 解决的问题 | 要点 |
|---|---|---|
| 影响地图(Impact Mapping) | 功能和业务目标脱节 | Why(目标)→ Who(影响谁)→ How(怎么影响)→ What(交付什么) |
| 卡诺模型(Kano) | 需求优先级靠拍脑袋 | 需求分五类:必备型、期望型、兴奋型、无差别型、反向型。优先做必备型和期望型,识别并砍掉无差别型和反向型,用 MVP 低成本验证兴奋型 |
| 用户故事 | 需求共识方式低效 | "作为〈角色〉,我想要〈活动〉,以便〈价值〉";一个故事在一个迭代内可交付,粒度一般 3~5 天 |
| INVEST 原则 | 故事拆得不好 | Independent 独立、Negotiable 可协商、Valuable 有价值、Estimable 可估算、Small 小、Testable 可测试 |
| 需求价值度量 | 上线后没人知道效果如何 | 需求提出时就定义上线后的观测指标,每个业务需求配套一个埋点需求 |
最后一条容易被忽视:没有数据支撑时,上线评估会变成业务方自评,结果十有八九是"符合预期"。原则是既要听用户怎么说,也要看用户怎么做。
4.2 看板:没有 WIP 限制就只是可视化
看板源于丰田的拉动式生产。核心判别标准是:没有在制品限制的拉动系统只是"可视化板",不是看板系统。在 Jira 里建一个分列的面板,如果不设 WIP 上限,那只是可视化板。
WIP 过高会形成恶性循环:
WIP 高 → 前置时间长 → 多任务切换多、需求记忆衰减 → 质量下降、返工增多
↑ │
└── 业务不信任 IT,一次性压更多需求 ←── 紧急插单增多 ←──────┘
加人不是解药。《人月神话》早已指出,人数增加后沟通成本迅速上升,新人培训短期内还会拖慢交付。
精益看板五步法:
- 可视化流程:如实呈现现有流程,初期不做优化,对组织冲击小
- 列:按价值流阶段划分,每列再分"进行中"和"已完成"(后者是给下游拉取的缓冲区)
- 泳道:给需求划清界限,可以设紧急通道、技术改进泳道;前后端任务放在同一泳道,方便暴露依赖
- 显式化规则:卡片颜色(需求、缺陷、改进各用一色)、谁在什么时候移动卡片、阻塞如何标记、什么情况必须线下沟通
- 限制 WIP:从现状出发渐进收紧,每人并行不超过 2~3 件。看板只能暴露问题,不能解决问题:把 WIP 压到 1,往往会立刻暴露环境不就绪、发布窗口太稀疏之类的固有问题
- 管理流动:
- 站会只看阻塞、紧急和长期停滞的卡片(按停留时长排序看 Top N),不是轮流汇报"昨天做了什么"
- 队列填充会:业务方和技术方按优先级把需求补入"就绪"列
- 发布规划会:部署与发布分离后,做到"按节奏部署、按需发布"
- 反馈与持续改进:可以参考看板成熟度模型(Kanban Maturity Model,共 7 级)
限制 WIP 的根本目的,是用快速、可预期的交付在业务和 IT 之间建立信任。研发承诺最快交付最高优先级的需求,业务方看到一次次按期上线,才愿意不再一次性压一堆需求。
五、工程实践:从提交到上线
flowchart LR
subgraph DEV["开发"]
A["需求/任务"] --> B["特性分支<br/>提交"]
end
subgraph CI["持续集成 CI"]
B --> C["构建"] --> D["单测 + 静态扫描<br/>+ 安全扫描"] --> E{"质量门禁"}
end
E -->|通过| F["制品库<br/>镜像/包"]
subgraph CD["持续部署 CD"]
F --> G["测试环境"] --> H["预发环境"] --> I["生产:灰度 → 全量"]
end
I --> J["监控 / 告警 / 埋点"]
J -.反馈.-> A
E -.失败.-> B
CI 的产出是制品,CD 的输入是制品,两者的结合点是制品库。整条流水线的原则是:一次构建,多次部署(Build Once, Deploy Many)。同一个制品从测试环境逐级晋级到生产,环境差异只通过配置注入。部署前重新打包会让"测过的"和"上线的"不是同一个东西,这是常见的反模式。
5.1 配置管理:一切工程实践的地基
这里的配置管理是宏观概念:规范和控制整个交付过程中的变更,保证过程完整、一致、可追溯。Ansible 之类的环境配置工具和 CMDB 只是它的一部分。
| 原则 | 要求 | 反模式 |
|---|---|---|
| 版本变更标准化 | 每次变更记录谁、何时、改了什么、为什么、谁批准的;提交信息关联需求编号 | 所有提交都填同一个需求号,数据"合规"但无效 |
| 一切纳入版本控制 | 代码、配置、脚本、流水线定义、环境描述、数据库变更 | 工具升级后某参数默认值由关改为开,排查了好几天。如果有版本记录,对比差异几分钟就能定位 |
| 全流程可追溯 | 从需求能追到代码、版本、测试、上线记录和线上反馈 | 线上出问题查不到包含哪些变更 |
| 单一可信数据源 | 代码只有一个托管源;制品只有一个发布渠道;依赖只有一个受控来源;应用元数据全公司统一 | 本地打包直接上传服务器;同一个应用在两个部门叫两个名字 |
判断"要不要纳入版本控制"的准则:能由其他受控产物重新生成的,作为制品管理即可。软件包由代码、构建脚本、构建环境生成,所以前三者进版本控制,包进制品库。大文件不适合放进 Git,交给 Artifactory、Harbor 这类制品库。
提交信息示例(Conventional Commits 风格 + 需求关联):
fix(order): 修复优惠券叠加时的金额溢出
优惠券与满减同时生效时,折后金额可能为负,导致支付回调校验失败。
改为在计算阶段对最终金额做下限截断,并补充边界单测。
风险:影响所有叠加优惠订单的金额计算
Refs: PAY-2231
配置管理的推进路径通常是 标准化 → 自动化 → 数据化 → 服务化:没有标准化就没法自动化,没有自动化沉淀下来的数据就无法度量。
5.2 分支策略
分支策略决定了团队的协作方式和发布方式。
| 策略 | 做法 | 适用场景 | 主要风险 |
|---|---|---|---|
| 主干开发,分支发布 | 所有人提交主干;发布前拉出发布分支,只修 bug 不加功能 | 有固定版本节奏的客户端、智能硬件、App | 主干坏了会阻塞所有人;发布分支的修复忘记合回主干,下个版本问题复现 |
| 分支开发,主干发布(GitHub Flow) | 特性分支开发,经 PR/MR 合回主干,主干随时可发布 | Web 服务、开源项目 | 特性分支活得太久就会陷入合并地狱 |
| 主干开发,主干发布(TBD) | 所有人直接小步提交主干,未完成的功能用特性开关隐藏 | 工程能力强、测试自动化充分的团队 | 对每次提交的质量要求极高 |
| GitFlow | master/develop/feature/release/hotfix 五类分支 | 版本化交付的传统软件 | 分支多、合并成本高,与持续交付相悖,其作者也已不再推荐给持续交付的 Web 应用 |
推荐组合是主干开发 + 短命特性分支:
- 团队共享一条主干
- 特性分支存活最好不超过 3 天,最多不超过一周
- 每天至少向主干合并一次;分支存在超过 1 天,每天同步主干
- 特性分支和主干分别设质量门禁
- 谨慎使用特性开关,用完及时清理,否则开关本身会变成技术债
要不要保留发布分支取决于发布模式:App、硬件等按固定节奏发版的,拉发布分支;能独立部署的服务,直接从主干发布,再配合安全的发布策略。
发布分支的配套机制:
- 版本火车:功能"持票上车",赶不上就搭下一班,不为某个功能推迟发车
- Hotfix 双提交:修复必须同时进发布分支和主干,或由工具自动扫描两者的差异
Facebook 的演进可以作为参照:早期是主干开发、分支发布,每天固定发布 2 次;后来改为主干开发、主干发布,每次提交都触发构建、单测、扫描和自动化测试,合入后先发内部员工环境,再发 2% 的线上用户,自动检测反馈数据后再全量,最终做到按需发布。
5.3 持续集成
CI 常被误解为"编译打包"或"负责打包的那个人"。它的定义来自极限编程(Kent Beck,1990 年代后期),Martin Fowler 的描述是:团队成员频繁集成工作成果,通常每人每天至少集成一次;每次集成都由自动化构建(含测试)验证,以尽早发现集成错误。背后的理念是:越痛苦的事越要频繁地做。集成越少,每次集成的冲突就越大,大家就越怕集成。
CI 三问(来自 Martin Fowler 的 CI Certification):
- 每次提交都会触发一次完整的流水线吗?
- 每次流水线都会运行自动化测试吗?
- 流水线失败后,能在 10 分钟内修复吗?
三个问题对应 CI 建设的三个阶段:
| 阶段 | 关键词 | 前置条件与要点 |
|---|---|---|
| 1 | 快速集成 | 有一条以集成为目的的分支;按分支类型定义不同的验证深度;构建资源池标准化,任务在任何节点跑都一样;反馈要快,超过 10~15 分钟研发就会失去耐心,CI 时长要纳入监控并设超时 |
| 2 | 质量内建 | 每次流水线都跑自动化测试,没有测试的 CI 是"瘸腿 CI";提交级跑快速检查和冒烟测试,集成级再加接口和 UI 测试;按本次变更的范围选择测试(精准测试) |
| 3 | 文化建立 | CI 红灯时停止提交新代码,优先修复;超时未修复就自动回滚;下班前确认 CI 是绿的 |
一个声明式 Jenkinsfile 示例(未实测):
pipeline {
agent { label 'k8s-maven' } // 动态 Pod 节点,环境由镜像定义
options { } // 超过 15 分钟直接判失败
stages {
{ steps { checkout scm } }
{ steps { sh 'mvn -B -DskipTests package' } }
{
parallel { // 单测与静态扫描并行
{
steps { sh 'mvn -B test' }
post { always { junit 'target/surefire-reports/*.xml' } }
}
{
steps { { sh 'mvn -B sonar:sonar' } }
}
}
}
{ // SonarQube 门禁不过则终止
steps { { waitForQualityGate abortPipeline: true } }
}
}
}
同样的流程用 GitHub Actions 表达(未实测):
name: ci
on:
pull_request:
push:
branches:
permissions:
contents: read # 默认最小权限,按需放开
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
- run: go vet ./...
- run: go test -race -coverprofile=cover.out ./...
image:
needs: test
if: github.ref == 'refs/heads/main' # 只有主干产出可部署制品
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }} # 用提交 SHA 作为不可变标签
镜像标签用提交 SHA,而不是 latest。这样制品和代码版本一一对应,也才谈得上"一次构建,多次部署"。
5.4 自动化测试
自动化测试要解决的是测试成本问题:功能不断累积,回归范围越来越大,测试时间却在被压缩。它的目标是守住质量底线(防止已有功能回退),而不是发现更多缺陷。某金融公司单测覆盖率 80%、接口覆盖率 100%,但自动化测试发现的问题只占全部问题的 5% 左右,这完全正常。新缺陷主要靠探索性测试发现,自动化负责回归。
适合自动化的场景:大量重复执行的回归;接口规范明确且稳定;大批量兼容性测试(上百个机型);长时间运行的压力测试和稳定性测试。不适合的场景:只上线一次的活动页;UI 频繁变化的功能。
测试分层:
/\ UI / 端到端:最接近用户,但慢、脆、维护贵
/ \
/----\ 接口 / 集成:前后端分离后性价比最高
/ \
/--------\ 单元测试:最快、最便宜,覆盖核心逻辑
Mike Cohn 的测试金字塔主张底层多、顶层少。对于前后端分离的 Web 应用,也有人主张以接口测试为主体的"椭圆形"或"奖杯形"结构。不管哪种形状,原则都一样:把测试尽量放在能发现问题的最低层。
微软的测试分级改造(2014 年起)是一个有代表性的案例。改造前,每日自动化测试要跑 22 小时,全量测试要 2 天,只有 60% 的 P0 用例能执行成功,8 年里每日测试从未全部通过。改造后按依赖和执行位置分级:
| 级别 | 定义 | 执行位置 | 耗时 |
|---|---|---|---|
| L0 | 无外部依赖的单元测试 | PR 中 | 每个 < 60ms |
| L1 | 有外部依赖(如数据库)的单元测试 | PR 中 | 平均约 400ms,一般 < 2s |
| L2 | 面向接口的功能测试 | 流水线,测试或预发环境 | —— |
| L3 | 线上测试 | 生产环境,靠监控判断健康度 | —— |
改造分四步:先把 L0/L1 的框架、规则和平台做好,让研发只管写用例;逐条审视旧的每日用例,能删的删,能下沉到 L0/L1 的下沉;剩下的转成 L2 接口测试;最后建设 L3。经过 40 多个迭代、近 3 年时间,测试分布从"每日长跑用例为主"反转为"L0 为主",提交后约 10 分钟就能跑完数万个测试。
误报率是自动化测试可信度的核心指标:
误报率 = 非代码缺陷导致的失败用例数 / 失败用例总数
100 个用例失败 20 个,其中只有 5 个是真缺陷,误报率就是 15/20 = 75%。误报一多,研发就不再相信测试结果,最后又回到手工确认。治理方法:
- 对失败原因分类:环境、网络、数据、脚本缺陷、真实缺陷
- 按异常特征自动归类
- 对已知的偶发问题(网络抖动)加有限次重试,并隔离不稳定用例(quarantine)
- 长期跟踪误报率趋势
UI 自动化的可维护性:用 Page Object 模式按页面封装操作;控件的定位方式放进配置文件,用例中用逻辑名称引用。控件变了只改配置,不改用例。
5.5 内建质量
两条原则:问题发现得越早,修复成本越低;质量是每个人的责任,而不是测试团队的责任。
两个"安灯"案例:
- 丰田安灯绳:任何工人发现质量问题都可以拉绳停线,管理和质量人员立即到场。停线本身不是目的,目的是问题在源头当场解决
- 亚马逊客服安灯(2012 年引入):一线客服发现商品有质量或安全风险,可以不经审批直接把商品设为不可购买
质量门禁是软件中的安灯。落地分五步:
- 选检查项:不要一次全上,否则研发没法干活。缺陷和安全漏洞优先于代码风格
- 定指标并达成共识:
- 静态阈值用于零容忍项,例如高危漏洞数 = 0
- 动态阈值用于存量项目,看增量和趋势,例如"问题总数不得高于基线"、"新增代码覆盖率 ≥ 80%"
- 自动执行:门禁尽量靠近问题产生的地方,IDE 插件好过提交检查,提交检查好过发布前检查
- 定义处理方式:结果分为失败、告警、人工确认三级,渐进式收紧;规则和处理方式要向全员宣导
- 持续调整:初期留出弹性,目标是质量提升,而不是"通过门禁"
门禁规则示例:
| 规则 | 比较 | 阈值 | 结果 |
|---|---|---|---|
| 单元测试通过率 | = | 100% | 失败 |
| 新增代码行覆盖率 | ≥ | 80% | 失败 |
| 阻塞/严重级代码问题 | = | 0 | 失败 |
| 高危依赖漏洞(CVSS ≥ 7) | = | 0 | 失败 |
| 代码重复率 | ≤ | 5% | 告警 |
真正考验内建质量的,不是门禁建了多少,而是有多少发布走了"特殊审批"和"紧急通道"。
5.6 技术债务
技术债务是为了短期目标选择权宜之计所欠下的代价,而且会"生利息":拖得越久,修改成本越高。症状包括上千行的函数、到处引用的全局变量、一堆名字相近的脚本、改一处崩一片。过时的技术栈也是债务,例如 Python 2 已于 2020 年 1 月 1 日停止官方支持。
量化:SonarQube 用 SQALE 方法,把每条违规折算成修复所需的时间,再计算:
技术债务比例 = 修复全部已知问题的时间 / 从头重写全部代码的估算时间
据此给出 A~E 的评级。用比例而不是绝对值,是为了让 10 万行和 1 千行的项目可以相互比较。修复时间超过重写时间,说明代码已经不可维护。
治理四步:
- 共识:对危害、目标、规则集达成一致。规则集频繁变化会让指标失去可比性
- 可见:把扫描集成进流水线
- 止损:对核心模块设基线,漏洞和严重级问题总量不再增长
- 偿还:迭代中预留容量,或在业务低谷期集中处理
优先偿还高频修改的模块:利息在这里累积得最快。可以从版本控制历史统计文件的修改频率,再和复杂度叠加分析,找出热点。新项目从第一天起执行规则,存量项目先控制增量。
5.7 环境管理:基础设施即代码与 GitOps
环境是软件行业的"头号背锅侠"。它的五个难点:环境种类多(开发、测试、预发、灰度、生产);微服务化后一套完整环境的依赖极多;环境之间不一致("在我机器上是好的");交付慢(申请一套环境要数周、层层审批);变更不可追溯。
基础设施即代码(IaC)用描述性文本定义环境,并由工具自动达成期望状态:
- 每个环境一份配置,公共部分复用;改环境就是改文件,而不是登录机器
- 工具保证幂等:重复执行结果一致,已达成的步骤自动跳过
- 纳入版本控制后,变更入口收敛且可追溯
- 配置文件是"活文档",开发也能看懂和修改
| 工具 | 类型 | 模型 | 典型用途 |
|---|---|---|---|
| Terraform / OpenTofu | 声明式 | 调用云 API,状态文件记录现实 | 云资源编排(VPC、主机、数据库、K8s 集群) |
| Pulumi | 声明式,用通用编程语言写 | 同上 | 需要复杂逻辑的资源编排 |
| Ansible | 过程式为主 | 推模式,基于 SSH,无需 agent | 主机配置、应用部署 |
| Puppet / Chef / Salt | 声明式 | 拉模式为主,需要 agent | 大规模主机配置管理 |
| Helm / Kustomize | 声明式 | 生成 K8s 清单 | K8s 应用打包与多环境差异化 |
Terraform 于 2023 年 8 月改用 BSL 许可证,社区随后分叉出 OpenTofu,由 Linux 基金会托管。
GitOps 把 IaC 与版本控制推到极致:Git 是环境期望状态的唯一可信来源,所有变更通过 PR 完成,由集群内的控制器持续把实际状态收敛到 Git 中的期望状态。OpenGitOps 定义了四条原则:
- 声明式:系统的期望状态以声明方式表达
- 版本化且不可变:期望状态存储在版本化、不可变、保留完整历史的地方
- 自动拉取:代理从源头自动拉取期望状态
- 持续调谐:代理持续观测实际状态,并向期望状态收敛
sequenceDiagram
participant Dev as 开发
participant App as 应用仓库
participant CI as CI 流水线
participant Reg as 镜像仓库
participant Env as 环境配置仓库
participant Ctl as Argo CD / Flux
participant K8s as Kubernetes
Dev->>App: 提交 / 合并 PR
App->>CI: 触发构建与测试
CI->>Reg: 推送镜像 web:3f2a1c
CI->>Env: 自动发起 PR:镜像标签改为 3f2a1c
Note over Env: 测试环境自动合并<br/>生产环境需运维批准
Ctl->>Env: 定期拉取 / Webhook
Ctl->>K8s: 应用差异,持续调谐
K8s-->>Ctl: 实际状态(漂移会被自动纠正)
与传统"推模式"(CI 直接 kubectl apply)相比,拉模式有三个好处:CI 不需要持有生产集群的凭据;手工改集群造成的漂移会被自动纠正;回滚就是 git revert。
Argo CD Application 示例(未实测):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: web-staging
namespace: argocd
spec:
project: default
source:
repoURL: https://git.example.com/env/staging.git # 环境配置仓库,而不是应用代码仓库
targetRevision: main
path: apps/web
destination:
server: https://kubernetes.default.svc
namespace: web
syncPolicy:
automated:
prune: true # Git 中删除的资源,集群中也删除
selfHeal: true # 手工改动被自动还原
开发环境同样需要治理。常见做法:
- 把工具链打包成容器镜像(Dev Container),新人几分钟就能得到一致的环境
- 用 Skaffold、Tilt 等工具实现"本地保存 → 自动构建 → 部署到开发集群"的内循环
- 用 K8s 命名空间为每个 PR 拉起临时预览环境,合并后自动销毁
5.8 部署与发布
部署 ≠ 发布:
| 部署(Deploy) | 发布(Release) | |
|---|---|---|
| 性质 | 技术行为 | 业务行为 |
| 含义 | 新版本运行在生产环境 | 新功能对用户可见 |
| 决策者 | 研发、运维 | 产品、业务 |
| 手段 | 流水线、编排系统 | 特性开关、流量路由 |
电商零点上线的大促活动,如果部署和发布不分离,就要在零点前一秒完成所有服务器的变更。分离后,代码提前几天部署,零点只需打开开关。
质量观的转变:发布越来越频繁,终端形态越来越多,上线前穷尽测试是伪命题。DevOps 的质量思路是:在一定质量水平下加快发布节奏,用低风险的发布策略控制爆炸半径,用线上监控尽早发现问题,用最简单的手段快速恢复。
| 策略 | 做法 | 优点 | 代价 / 注意 |
|---|---|---|---|
| 重建(Recreate) | 停旧起新 | 最简单 | 有停机时间 |
| 滚动(Rolling) | 分批替换实例 | K8s 默认策略,无需额外资源 | 新旧版本并存,必须兼容;回滚也要逐批进行 |
| 蓝绿(Blue-Green) | 两套完全相同的环境,切换路由 | 切换和回滚都是秒级 | 资源翻倍;数据库通常共用,须向后兼容 |
| 金丝雀(Canary) | 先让小比例流量或用户使用新版本,观测指标后逐步放量 | 性价比最高,最普遍 | 需要按用户 ID 或 cookie 保证同一用户的体验一致,并有指标对比能力 |
| 影子 / 暗部署(Shadow / Dark Launch) | 复制真实流量到新版本,结果不返回用户 | 零用户影响的线上验证 | 写操作要隔离,避免重复扣款之类的副作用 |
| 特性开关(Feature Flag) | 代码已部署,按开关控制功能是否可见 | 部署与发布彻底分离,可按用户、地域灰度 | 开关要有生命周期管理,否则会变成技术债 |
| A/B 测试 | 不同用户看到不同方案,比较业务指标 | 用数据决策 | 属于实验手段,不是发布手段 |
Pete Hodgson 把特性开关分为四类:发布开关(短期,隐藏未完成功能)、实验开关(A/B)、运维开关(降级、熔断,可能长期存在)、权限开关(按用户开放功能)。只有第一类应当在功能全量后尽快删除。
微软的部署环:每次生产部署依次经过五个环,配置变更也一样,没有紧急通道。每一环的推进都需要人工确认,核心理念是控制爆炸半径:
flowchart LR
R0["环 0<br/>内部用户(金丝雀)"] --> R1["环 1<br/>小批量外部用户"] --> R2["环 2<br/>大批量外部用户"] --> R3["环 3<br/>国际用户"] --> R4["环 4<br/>所有用户"]
K8s 滚动更新的关键参数(未实测):
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 25% # 最多额外多出的 Pod 数
maxUnavailable: 0 # 更新期间不允许低于期望副本数
minReadySeconds: 10 # 就绪后稳定 10 秒才算可用
selector:
matchLabels:
template:
metadata:
labels:
spec:
containers:
- name: web
image: registry.example.com/web:3f2a1c
readinessProbe: # 没有就绪探针,滚动更新会把流量打到还没准备好的实例上
httpGet:
periodSeconds: 5
用 Argo Rollouts 做基于指标自动判定的金丝雀(渐进式交付,未实测):
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: web
spec:
replicas: 10
selector:
matchLabels:
template: # 与 Deployment 的 Pod 模板相同,略
metadata:
labels:
spec:
containers:
- name: web
image: registry.example.com/web:3f2a1c
strategy:
canary:
steps:
- setWeight: 10
- pause:
- analysis: # 成功率不达标则自动中止并回滚
templates:
- templateName: success-rate
- setWeight: 50
- pause:
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: success-rate
interval: 1m
count: 5
successCondition: result[0] >= 0.99
failureLimit: 1
provider:
prometheus:
address: http://prometheus.monitoring:9090
query: |
sum(rate(http_requests_total{app="web",code!~"5.."}[1m]))
/
sum(rate(http_requests_total{app="web"}[1m]))
数据库变更是蓝绿和滚动发布的难点,因为新旧版本的代码会同时访问同一个库。解决办法是扩展-收缩(Expand/Contract,又称并行变更)。以把列 name 改名为 full_name 为例:
| 步骤 | 数据库 | 应用 | 能否回滚到上个版本 |
|---|---|---|---|
| 1 扩展 | 新增 full_name 列(可空) |
不变 | 能 |
| 2 双写 | —— | 同时写 name 和 full_name,仍读 name |
能 |
| 3 回填 | 把历史数据从 name 复制到 full_name |
—— | 能 |
| 4 切读 | —— | 改为读 full_name,仍然双写 |
能 |
| 5 停写旧列 | —— | 只写 full_name |
能(回到第 4 步) |
| 6 收缩 | 删除 name 列 |
—— | 旧版本已全部下线后才执行 |
每一步都是一次独立的、向后兼容的发布。数据库变更脚本用 Flyway、Liquibase 等工具纳入版本控制,随流水线执行。
5.9 线上质量与快速恢复
监控是一种全量的测试。测试环境是动物园,生产环境是大自然:设备、用户行为、流量形态、依赖服务,都只有生产环境里才是真实的。线上验证的手段有:
- 灰度与众测,配合埋点采集用户行为和质量数据
- 用户反馈与舆情监控,按关键字发现负面反馈
- 流量回放:录制真实流量,在预发环境回放,可以过滤、放大(用于压测)、比对新旧版本的响应。工具如 GoReplay
恢复时间可以拆解:
故障发生 ──MTTD──► 发现 ──MTTI──► 定位 ──修复──► 恢复
检测时长 诊断时长
|◄─────────────────── MTTR(恢复时长)───────────────────►|
恢复有两个方向:向后回滚到上一个稳定版本,或向前修复发布一个新版本。两者都依赖一条足够快的自动化部署流水线。原则是先止血再根治:多数情况下回滚或关闭特性开关比现场修 bug 更快、更安全。
降级与兜底是故障自愈的第一步:
- 降级:高峰期用开关屏蔽非主路径功能(推荐、评论、非核心日志),保住核心链路
- 兜底:依赖不可用时返回缓存数据、默认值、兜底页面或骨架屏,而不是报错
- 熔断与限流:防止故障沿调用链扩散成雪崩
5.10 混沌工程
Chaos Engineering is the discipline of experimenting on a system in order to build confidence in the system's capability to withstand turbulent conditions in production. —— Principles of Chaos Engineering
微服务化之后,没有人能完整理清调用关系。如果可用性设计建立在"某个服务不会出问题"的假设上,这个服务迟早会出问题。
| 故障演练 | 全链路压测 | 混沌工程 | |
|---|---|---|---|
| 目标 | 验证已知故障的预案 | 验证容量 | 发现未知的脆弱点 |
| 场景 | 预先定义(断电、磁盘写满、DNS 异常) | 按预估流量模型加压 | 注入真实世界的故障,常常多个同时发生 |
| 环境 | 多在预发或隔离环境 | 生产,隔离真实流量 | 最终走向生产 |
五条原则:
- 围绕稳定状态建立假设:用一组指标描述"系统正常"。业务指标比技术指标更重要,例如下单成功率、支付成功率,而不仅是 CPU 和 QPS
- 模拟真实世界的事件:实例宕机、网络延迟与分区、依赖超时、时钟漂移、磁盘满
- 在生产环境实验:流量形态和依赖关系只有生产环境是真实的
- 持续自动化运行:把实验纳入流水线或定期执行
- 最小化爆炸半径:从极小范围开始(例如 0.5% 的流量或单个可用区),能随时终止
前置条件:系统已经具备基本的弹性(超时、重试、熔断、降级),有应急预案和监控。连基本的可恢复性都没有,做混沌实验只会制造事故。
Netflix 的 Chaos Monkey 随机终止生产实例,"猴子军团"后来扩展到模拟整个可用区乃至区域失效。所以 AWS 出现区域级故障时,Netflix 往往受影响很小。常用工具:Chaos Mesh、LitmusChaos(均为 CNCF 项目)、ChaosBlade、AWS Fault Injection Service、Gremlin(商业)。
六、SRE 与可观测性
6.1 DevOps 与 SRE 的关系
Google 的说法是 "class SRE implements interface DevOps":DevOps 给出理念和原则,SRE 是 Google 用软件工程方法实现这些原则的一种具体做法。
| DevOps 原则 | SRE 实践 |
|---|---|
| 消除组织孤岛 | 开发与 SRE 共享服务所有权 |
| 接受失败是常态 | 错误预算、无责复盘 |
| 小步变更 | 渐进式发布、金丝雀 |
| 工具与自动化 | 消除琐事(Toil),SRE 花在琐事上的时间不超过 50% |
| 度量一切 | SLI / SLO |
6.2 SLI、SLO、SLA 与错误预算
| 概念 | 定义 | 例子 |
|---|---|---|
| SLI 服务等级指标 | 衡量服务质量的量化指标 | 成功请求数 / 总请求数;P99 延迟 |
| SLO 服务等级目标 | SLI 的内部目标值 | 30 天窗口内可用性 ≥ 99.9% |
| SLA 服务等级协议 | 对外承诺,违约有赔偿 | 可用性低于 99.5% 按比例返还费用 |
| 错误预算 | 1 − SLO | 99.9% 的 SLO 意味着允许 0.1% 的失败 |
SLA 应当比 SLO 宽松,给内部留出反应空间。30 天窗口下的错误预算:
| SLO | 允许的不可用时间(30 天) |
|---|---|
| 99% | 7.2 小时 |
| 99.9% | 43.2 分钟 |
| 99.95% | 21.6 分钟 |
| 99.99% | 4.32 分钟 |
| 99.999% | 约 26 秒 |
错误预算把"发布速度 vs 稳定性"的部门之争变成了数据问题:预算充足时可以多发布、多做实验;预算耗尽时冻结功能发布,集中做稳定性改进。100% 从来不是正确的目标。用户的网络、手机本身的可用性都达不到 100%,追求它只会让发布无限放缓。
按燃烧率告警:直接对"错误率 > 0.1%"告警会非常嘈杂。Google SRE Workbook 推荐多窗口、多燃烧率告警。燃烧率 14.4 表示按当前速度,1 小时就会消耗 30 天预算的 2%(14.4 × 1h / 720h = 2%)。Prometheus 规则示例(未实测):
groups:
- name: slo-web
rules:
- alert: WebErrorBudgetFastBurn
# 1 小时长窗口确认趋势,5 分钟短窗口确保仍在发生(避免恢复后继续告警)
expr: |
(
sum(rate(http_requests_total{job="web",code=~"5.."}[1h]))
/ sum(rate(http_requests_total{job="web"}[1h]))
) > (14.4 * 0.001)
and
(
sum(rate(http_requests_total{job="web",code=~"5.."}[5m]))
/ sum(rate(http_requests_total{job="web"}[5m]))
) > (14.4 * 0.001)
labels:
severity: page
6.3 可观测性
监控回答"系统是否正常",可观测性回答"系统为什么不正常",包括事先没有预料到的问题。
| 信号 | 回答的问题 | 典型工具 |
|---|---|---|
| 指标(Metrics) | 发生了什么、趋势如何 | Prometheus、VictoriaMetrics |
| 日志(Logs) | 具体发生了什么事件 | Loki、Elasticsearch |
| 链路(Traces) | 请求在哪个服务、哪一步慢了或错了 | Jaeger、Tempo |
| 性能剖析(Profiles) | 哪段代码消耗资源 | Pyroscope、eBPF 工具 |
OpenTelemetry(CNCF,由 OpenTracing 与 OpenCensus 于 2019 年合并而来)统一了遥测数据的采集 API、SDK 和传输协议(OTLP),把"埋点"与"后端存储"解耦。
该看哪些指标:
| 方法 | 适用对象 | 指标 |
|---|---|---|
| 四个黄金信号(Google SRE) | 面向用户的服务 | 延迟、流量、错误、饱和度 |
| RED(Tom Wilkie) | 请求驱动的服务 | Rate 速率、Errors 错误、Duration 耗时 |
| USE(Brendan Gregg) | 资源(CPU、磁盘、网络) | Utilization 使用率、Saturation 饱和度、Errors 错误 |
告警只对影响用户的症状发出(页面打不开、下单失败),而不是对原因(CPU 高)发出。原因类指标用于排查,不用于半夜叫醒人。
6.4 值班与无责复盘
值班:谁构建谁运维(You Build It, You Run It,Werner Vogels 2006 年提出)。微软把值班工程师称为 DRI(Designated Responsible Individual),要求工作日 5 分钟、休息日 15 分钟内响应,并纳入考核。
无责复盘(Blameless Postmortem)的前提是:故障是系统问题,不是个人问题。追责只会让人隐瞒信息,而隐瞒信息会让同样的故障再次发生。一份复盘报告应包含:影响范围、时间线、根因(常用"5 个为什么")、做得好的地方、做得不好的地方、带负责人和截止时间的改进项。
GitLab 删库事件(2017-01-31)是公开复盘的标杆:
- 经过:主从同步因流量异常中断,工程师在修复时误把生产库当作备库清空,丢失约 300GB 数据
- 更糟的是:五种备份机制全部失效。定时备份因版本不匹配一直在静默失败,失败告警邮件也没有送达
- 应对:用公开的 Google 文档实时记录排查过程,在 YouTube 直播恢复,事后发布详细的复盘报告
- 启示:没有演练过恢复的备份等于没有备份
Etsy 每年把一件"三只袖子的毛衣"颁给当年引发最大故障的工程师,传递的信息是:错误暴露了系统的漏洞,本身也是一种贡献。2019 年 DevOps 状态报告把心理安全列为影响生产力的关键能力之一。
七、DevSecOps 与软件供应链安全
7.1 安全左移:把检查嵌入流水线
| 环节 | 检查 | 工具举例 |
|---|---|---|
| 编码 | IDE 安全插件、密钥检测 pre-commit 钩子 | gitleaks、SonarLint |
| 提交 / PR | SAST 静态应用安全测试 | Semgrep、CodeQL、SonarQube |
| 依赖 | SCA 软件成分分析(已知漏洞、许可证合规) | Dependabot、Renovate、OWASP Dependency-Check、Snyk |
| 构建 | 容器镜像扫描、生成 SBOM | Trivy、Grype、Syft |
| 部署 | IaC 配置扫描;准入策略(禁止特权容器、禁止未签名镜像) | Checkov、OPA Gatekeeper、Kyverno |
| 运行 | DAST 动态测试;运行时威胁检测 | OWASP ZAP;Falco |
安全门禁同样遵循 5.5 节的原则:高危漏洞零容忍,存量问题看趋势,别让门禁一上线就挡住所有发布。
7.2 供应链攻击
| 事件 | 时间 | 攻击面 |
|---|---|---|
| SolarWinds Orion | 2020 | 构建系统被入侵,恶意代码被植入官方签名的更新包 |
| Log4Shell(CVE-2021-44228) | 2021-12 | 广泛使用的依赖存在远程代码执行漏洞,企业普遍说不清自己哪里用了 log4j |
| xz utils 后门(CVE-2024-3094) | 2024-03 | 攻击者花两年时间取得开源项目维护者身份,把后门藏进发布的源码包 |
应对思路是让每个制品都能回答"它由什么组成、从哪里来、谁构建的、有没有被篡改":
- SBOM(软件物料清单,格式有 SPDX、CycloneDX):记录制品包含的所有组件和版本。下一个 Log4Shell 出现时,查 SBOM 就能在分钟级定位受影响的服务
- 制品签名:用 Sigstore/cosign 对镜像签名,集群准入时校验签名
- SLSA(Supply-chain Levels for Software Artifacts):按构建过程的可信程度分级。v1.0 的 Build 轨道分为 L0~L3,越往上要求构建平台越可信,并能生成难以伪造的来源证明(Provenance)
- 依赖固定:锁定依赖版本(lock 文件);CI 中的第三方 Action 固定到提交 SHA,而不是可变的标签
- 最小权限:CI 的令牌默认只读;优先使用 OIDC 短期凭据,而不是长期有效的云密钥
八、度量:DORA 指标与度量体系
8.1 DORA 指标
DORA(DevOps Research and Assessment,2018 年被 Google 收购)基于多年问卷调查,找到了预测软件交付效能的核心指标:
| 指标 | 定义 | 衡量 |
|---|---|---|
| 部署频率 | 向生产环境部署的频率 | 吞吐 |
| 变更前置时间 | 从代码提交到成功运行在生产环境的时长 | 吞吐 |
| 变更失败率 | 部署后导致故障、需要修复或回滚的比例 | 稳定性 |
| 故障恢复时间 | 从故障发生到恢复服务的时长(2023 年报告改为"失败部署恢复时间") | 稳定性 |
这组指标最重要的结论是:速度和稳定性不是此消彼长的关系。 高效能团队在四项指标上同时领先。2018 年报告中,精英团队相对低效能团队的部署频率高 46 倍,前置时间快 2555 倍,恢复时间快 2604 倍,变更失败率低 7 倍。"慢工出细活"的直觉在软件交付中不成立:小批量、高频率的变更风险更低,也更容易定位问题。
指标和分档阈值每年都有调整,分档是按当年样本聚类得出的:
| 年份 | 变化 |
|---|---|
| 2021 | 精英档:按需部署(每天多次),前置时间 < 1 小时,恢复时间 < 1 小时,变更失败率 0~15%;运维表现指标由 2018 年的"可用性"扩展为"可靠性",与四项交付指标并列 |
| 2023 | 精英档:前置时间 < 1 天,变更失败率约 5%,恢复时间 < 1 小时 |
| 2024 | 新增返工率(Rework Rate,计划外修复性部署的比例);指标分为吞吐与不稳定性两组。报告还发现,AI 采用率每提高 25%,交付吞吐估计下降 1.5%,交付稳定性下降 7.2% |
反模式:把 DORA 指标当作团队排名或个人 KPI。一旦与考核挂钩,大家就会把一次部署拆成十次,或者不把回滚记作失败。古德哈特定律同样适用于这里:当指标变成目标,它就不再是好指标。
8.2 好指标与度量原则
好指标的四个特征:
- 明确受众:给非技术管理者看单测覆盖率没有意义
- 直指问题:看到数据就知道该改进什么,例如构建失败率高说明提交前的验证不足
- 量化趋势:可以客观采集、能看出趋势。手工填报的"项目达成率"不是好指标
- 充满张力:向上能归到业务结果,向下能拆到具体环节。反例:只统计需求个数,把一个需求拆成两个就"翻倍"了
五条原则:全局指标优于局部指标;综合指标优于单一指标;结果指标优于过程指标;团队指标优于个人指标;灵活指标优于固化指标。
反面例子:"准时提测率":按时 100 分,延期 1 天 90 分,延期 2 天 70 分。延期 1 小时和延期 23 小时得分相同,而且这个指标只衡量了是否守约,没有衡量交付效率。
一个可以直接采用的体系:三组八项结果指标
| 组 | 指标 | 定义 |
|---|---|---|
| 交付效率 | 需求前置时间 | 从需求提出到上线 |
| 开发前置时间 | 从开始开发到上线 | |
| 交付能力 | 发布频率 | 单位时间内的发布次数 |
| 发布前置时间 | 从提交一行代码到上线 | |
| 交付吞吐量 | 单位时间交付的需求数 × 需求粒度 | |
| 交付质量 | 线上缺陷密度 | 平均每个需求产生的线上缺陷数 |
| 线上缺陷分布 | 严重和致命级缺陷的占比 | |
| 故障修复时长 | 从有效缺陷提出到修复上线 |
判断 CI/CD 能力最简单的方法只有一个问题:只改一行代码,从提交到上线要多久?
其他框架:
- SPACE(Nicole Forsgren 等,2021)从五个维度衡量开发者生产力:满意度与幸福感(Satisfaction)、绩效(Performance)、活动(Activity)、沟通协作(Communication)、效率与心流(Efficiency)。它强调不能只看活动量(提交数、代码行数)
- DevEx(2023)关注开发者体验的三个维度:反馈回路、认知负荷、心流状态
8.3 度量落地
事前:先对齐口径。"测试周期"在需求系统里可能是从"待测试"到"待发布"的 5 天,在测试系统里可能是测试任务的平均执行时长 1 天。建平台之前,至少要能手工算出这些指标并达成共识,否则平台建完也没人信。数据校准和指标对齐花的时间,往往比开发平台本身还多。
事中:采集与存储:
flowchart LR
J["Jira / 需求平台"] --> C1["采集器"]
G["GitLab"] --> C2["采集器"]
P["流水线"] --> C3["采集器"]
M["监控 / 工单"] --> C4["采集器"]
C1 & C2 & C3 & C4 --> K["消息队列<br/>Kafka"]
K --> R["原始数据<br/>HBase / ClickHouse"]
R --> A["清洗、聚合"] --> S["指标库<br/>MySQL"]
S --> V["可定制看板"]
每个数据源对应一个采集器插件,屏蔽 API、JDBC、消息订阅等取数方式的差异,统一上报格式。原始数据量大、结构不统一,用可横向扩展的存储;聚合后的指标供前端直接展示。开源方案有 Apache DevLake,早期还有 Hygieia 等。
事后:运营比建设更难。数据失真最常见的原因是操作不规范,比如上线后才一次性拖动卡片、提交代码不关联需求。对策是状态自动流转,并在指标旁标出参考值和在部门中的位置(前 10%、后 10%),让数据自己说明好坏。
把指标关联到组织可以分四步:
- 找抓手:需求是唯一贯穿研发全流程的对象
- 对大数:先让团队认可数据是准的
- 找差距:把大阶段拆细,例如测试周期拆成功能测试、产品走查、埋点测试,定位具体慢在哪一步
- 分级别:组织级、团队级、项目级分别看不同的指标。不是所有指标都适合下钻到个人
度量是双刃剑:把度量结果和个人绩效直接绑定,度量就会变成数字游戏。大公司反复推倒重建度量体系,往往就是因为旧体系被"摸透"了。度量的目的是激发团队的改进意愿,而不是用来排名。
九、持续改进:PDCA 与复盘
衡量一个团队是否"做到了 DevOps",要看它有没有持续改进的能力,而不是引入了多少工具。
- 从 0 到 1:对照最佳实践补齐短板,相对容易
- 从 1 到 N:团队要能根据业务发展自己识别下一个改进目标
PDCA 循环:Plan(识别问题、分析根因、制定计划)→ Do(实施)→ Check(检查结果是否符合预期)→ Act(好的做法标准化,没解决的问题进入下一轮)。持续改进应当是一系列小而高频的改进,而不是一次大而全的变革,后者影响面广,容易半途而废。
四个落地实践:
- 正向复盘:不定级定责,找出流程漏洞,再用机制补上。一个例子:收集常见的编译错误建成案例库,构建失败时自动匹配并推送解决建议
- 预留固定改进时间:在待办列表中设"持续改进"类任务,每个迭代固定分配容量;也可以办 Hackathon
- 共享业务指标:让团队看到自己交付的功能在线上表现如何;DevOps 指标对内公开,形成适度的横向压力
- 激励创造:把团队贡献、工具创新写进绩效目标;推动内部开源,避免重复造轮子
谷歌、Netflix 内部很少提 DevOps 这个词,相关实践早已是日常。Gerrit 就是谷歌工程师为弥补"缺少基于 Git 的带权限代码评审工具"而开发的。遇到钉子再造锤子,而不是拿着锤子到处找钉子。
十、平台建设:从工具到平台工程
10.1 三个阶段
| 阶段 | 特征 | 策略 | 注意 |
|---|---|---|---|
| 从无到有 | 大量手工和本地操作,没有专门的工具团队 | 直接引入主流开源或商业工具,快速补齐高频环节(需求、代码、CI) | 选主流工具:接口完善、插件多、踩过的坑多。选冷门工具的最终往往还是要迁移 |
| 从小到大 | 工具齐了,需求从"够用"变成"好用";差异化需求和规模化问题出现 | 基于开源二次开发或封装,定制商业工具 | 设计上预留扩展(例如一开始就支持多租户;参数配置化,而不是写死在页面上);尽早治理元数据(应用名、模块名等全公司统一,建 CMDB) |
| 从繁到简 | 平台太多、太复杂,价值说不清 | 整合成统一的解决方案:统一入口、简化操作、有效度量 | 找到交付主路径,用一个平台串联;区分"平台"和"工具",收敛同类工具 |
DevOps 状态报告的一个发现是:倾向于完全自建工具的企业,效能往往并不高。自建本身没错,错在阶段不对:在"从无到有"阶段自建,等于在别人的成熟方案之上重复造轮子。
采购商业工具时,要区分它是成本还是投资。自研仿制一个成熟的商业工具,重复造轮子的成本往往并不低。一个国内反例:某大型企业内部有 1700 多个工具平台,大量功能重复建设。
平台建设的核心理念可以概括为四化:
- 标准化:一切皆有规则,一切皆有标准
- 自动化:去掉一切不必要的手工环节
- 服务化:面向普通用户设计,而不是面向专家设计;用户不依赖外部帮助就能完成工作
- 数据化:采集、分析数据,用数据指导改进
10.2 平台产品设计的五个层次
DevOps 平台的产品经理多是技术出身,容易一上来就钻进实现细节。借用用户体验的五层模型:
| 层次 | 要回答的问题 | DevOps 平台的例子 |
|---|---|---|
| 战略层 | 解决什么问题? | 建立在不变的诉求上:效率、质量、成本、安全。反例:对标竞品全面模仿 |
| 能力层 | 做什么、不做什么? | 一个 4 人团队面对千人规模研发:不替换现有工具,只做链路打通,让已有平台以插件形式接入。避免与现有平台零和博弈 |
| 资源层 | 凭什么吸引用户? | 集中管理的构建资源(例如 iOS 构建机群),规模越大成本越低 |
| 角色层 | 用户在什么场景下用? | 分支名每天要手动输入几十次,加个历史记录和自动补全,差评就消失了 |
| 感知层 | 好不好用? | "内部产品不需要好看"是误区;参考成熟的设计规范(一致、反馈、效率、可控) |
不要让你的产品只有专家才会使用。Jenkins 社区的 "5 Click, 5 Minutes" 项目,目标是点 5 下、花 5 分钟就能建好一个 Jenkins,这就是面向普通用户的思路。
10.3 现代流水线的十大特征
| # | 特征 | 要点 |
|---|---|---|
| 1 | 平台而非能力中心 | 流水线只负责编排、调度、记录和可视化;测试、扫描、发布等专业能力由垂直平台提供,以插件形式接入。它是唯一贯穿端到端交付的平台 |
| 2 | 可编排、可视化 | 阶段串行,阶段内步骤可串可并;编排结果存为 JSON 等结构化格式,由引擎解释执行。可复用的最小单元称为原子(如"拉取代码"),要求通用、与业务无关 |
| 3 | 流水线即代码 | Jenkinsfile、.gitlab-ci.yml、GitHub Actions workflow;流水线的变更可追溯、可评审。它和原子一起是现代流水线的两大支柱 |
| 4 | 实例化 | 参数化执行,复用模板;每次运行保存配置快照,便于回溯和重跑;支持并发,每个实例有独立工作空间 |
| 5 | 有限支持 | 只满足约 90% 的常见场景。Jenkins 原生的 Xcode 构建步骤有 53 个参数,这是反面教材。长尾需求用"自定义脚本"类通用原子满足 |
| 6 | 流程可控 | 多条流水线分段覆盖(提交、集成、部署);支持定时、手动、事件(Webhook)触发;阶段之间可以设人工审批 |
| 7 | 动静分离 | 原子的参数用标准数据结构描述,前端根据描述动态渲染表单。新增参数不改前端代码 |
| 8 | 快速接入 | 定义标准的接入接口:任务调用、状态查询、数据上报。新平台几天内就能接入 |
| 9 | 内建质量门禁 | 门禁是一级能力:规则配置 → 数据收集 → 判定 → 报告,形成闭环。规则由 QA 统一配置 |
| 10 | 数据聚合 | 每次运行汇总各环节的结果;度量平台建成后,流水线成为它的数据源之一 |
flowchart TB
subgraph PL["流水线平台"]
UI["编排与可视化"] --> ENG["流程引擎<br/>调度 · 快照 · 并发"]
ENG --> GATE["质量门禁"]
ENG --> POOL["执行资源池<br/>K8s 动态 Pod"]
end
ENG <-->|"调用 / 查询状态 / 上报数据"| T["自动化测试平台"]
ENG <--> Q["代码质量平台"]
ENG <--> S["安全扫描平台"]
ENG <--> D["发布平台"]
ENG --> M["度量平台"]
10.4 平台团队怎么做产品
一个"三个月从零做出千人规模平台"的案例(6 人,分布在两地)可以提炼出几条通用经验:
- 启动:讲清楚为什么做、为什么是现在、为什么非做不可;用一个简陋但能跑的 demo 建立团队信心;尽早识别技术风险,指定专人攻关
- 技术选型:选团队熟悉、上手快的技术栈。内部平台用不着"高大上"的框架
- 研发效率:开发环境容器化,新人几分钟就能开始写代码;用自己开发的工具来开发这个工具(Use what you build to build what you use);研发自己上线、自己运维
- 协作规则:建任务要写清描述、验收标准、负责人和迭代;提交代码关联任务号;由提出人验收并关闭任务
- 运营:好平台也需要推广。找种子用户;每次发布都发 release notes;设 OnCall 轮值及时响应用户问题;在内部流量最大的入口和技术分享渠道宣传
10.5 平台工程与内部开发者平台
"You build it, you run it"把运维职责交给了开发团队,也把 K8s、Terraform、监控、安全扫描的复杂度一起压了过去。平台工程(Platform Engineering)的回应是:由平台团队把这些能力封装成自服务的内部开发者平台(IDP),降低业务团队的认知负荷。
- 黄金路径(Golden Path,Spotify 的说法;Netflix 称为 Paved Road):一条官方支持、开箱即用的最佳实践路径。例如一个"新建 Go 服务"的模板,自动生成代码仓库、CI 流水线、Dockerfile、K8s 清单、监控看板和告警规则。团队可以偏离黄金路径,但偏离了就要自己负责
- Backstage(Spotify 开源,CNCF 项目):开发者门户框架,提供软件目录(谁拥有哪个服务、依赖什么)、模板脚手架、技术文档聚合和插件体系
- 平台即产品:平台团队要像做外部产品一样对待内部用户,做用户调研、收集满意度,而不是强制推行
课程中的"从繁到简""服务化""有限支持原则",与平台工程的核心思想是一致的。
10.6 云原生时代的流水线
老一代 CI 系统在云原生环境中会遇到结构性问题。以 Jenkins 为例:
- Java 单体应用,状态存在本地文件系统,天生不支持高可用
- 流水线逻辑在 Master 上解释执行,任务越多 Master 压力越大;一个输出 500MB 日志的任务就可能拖垮整个服务
- 把 Jenkins 装进容器交给 K8s 管理,并不等于云原生化
云原生 CI/CD 的特征:
| 特征 | 做法 |
|---|---|
| 每一步都在容器中执行 | 环境由镜像定义,步骤之间互相隔离 |
| 执行层无状态 | 日志、制品、报告写入外部存储(对象存储、日志服务) |
| 以 K8s 资源描述流水线 | Tekton 用 Task、Pipeline 等 CRD 定义流水线,由 K8s 调度 |
| 配置自动生成 | 根据代码语言套用模板,生成 Dockerfile、流水线文件、Helm Chart |
| 每个 PR 一个预览环境 | 评审时可以直接在预览环境验收 |
| GitOps 做环境晋级 | 环境之间的晋级就是环境仓库上的一次 PR |
| ChatOps | 在 PR 评论里输入 /approve、/retest 触发操作(如 Prow) |
在 K8s 中构建镜像,不要把宿主机的 Docker socket 挂进构建容器,那相当于把宿主机 root 权限交了出去。可以改用不依赖守护进程的方案,如 BuildKit rootless 模式或 Buildah。
十一、组织与文化
11.1 团队拓扑
《Team Topologies》(Matthew Skelton、Manuel Pais,2019)给出了组织设计的通用语言,核心目标是控制每个团队的认知负荷:
| 团队类型 | 职责 |
|---|---|
| 流对齐团队(Stream-aligned) | 对齐一条业务价值流,端到端负责交付,是组织的主体 |
| 平台团队(Platform) | 提供自服务的内部平台,减轻流对齐团队的负担 |
| 赋能团队(Enabling) | 短期介入,帮助其他团队掌握新能力(如测试自动化、可观测性),然后撤出 |
| 复杂子系统团队(Complicated-subsystem) | 负责需要深度专业知识的部分(如音视频编解码、定价引擎) |
团队之间有三种交互模式:协作(短期紧密合作,共同探索)、X 即服务(通过清晰的接口提供和消费能力)、促进(一方帮助另一方成长)。
一个常见的反模式是成立一个叫"DevOps 团队"的新部门,夹在开发和运维之间。这只是在两堵墙之间又砌了一堵墙。
11.2 案例:微软的转型
2014 年之后,微软云业务的需求量爆发:2016 年的需求数量超过此前 4 年的总和,2017 年又翻了一倍。只做局部优化能提升 10%,要提升 200% 就必须做大的改变。
组织:
- 第一次变革:开发和测试合并为工程团队,不再有专职测试。但运维仍然独立,开发再快,运维也跟不上
- 第二次变革:组建跨职能的特性团队,每队 10~12 人,包含产品、开发、测试、运维,坐在一起,自己控制部署,组建后 12~18 个月保持稳定
- 员工可以自主选择加入哪个团队:不到 20% 的人换了岗位,但 100% 的人获得了选择权
计划:迭代 3 周;计划周期 = 3 个迭代;Season = 6 个月;Scenario = 18 个月的远景。管理层负责回答"去哪里",团队自己决定"怎么去"。每个迭代结束发送附演示录屏的状态邮件;需求旁边附上原始的用户反馈,避免开发只拿到"翻译过"的需求。
工程:
- 1ES(One Engineering System):约 200 人的组织级效能团队,先统一交付主路径上的三件事:工作项管理、版本控制、构建。原则是 "Use what we ship to ship what we use",内部使用的就是对外销售的 Azure DevOps
- 测试分级改造见 5.4 节;部署环见 5.8 节
- 不承认"半自动化部署"。检验标准:一个不熟悉系统的人能否完成部署
- 按账号维度统计可用性,避免个别用户的问题被整体平均值掩盖;发现某个账号频繁受影响时,主动联系用户
2019 年 3 月公开的数据:每天部署 8.2 万次,每月 44 万个 PR、460 万次构建。
11.3 文化的几个样本
| 案例 | 做法 | 体现的文化 |
|---|---|---|
| NUMMI 工厂 | 通用汽车最差的工厂,旷工率一度达 20%,后被关闭;丰田与通用合资重开,重新雇用原班员工,送到日本培训。半年后成为通用旗下效益最好的工厂 | 问题出在系统,不在人 |
| GitLab | 公开故障排查过程、直播恢复、公开复盘报告和运维手册 | 透明带来信任 |
| Etsy | 给引发最大故障的人颁发"三只袖子的毛衣" | 心理安全,从错误中学习 |
| Netflix | 取消不必要的流程和审批,"把员工当成年人";开源 Chaos Monkey、Hystrix、Eureka、Spinnaker | 自由与责任,开源共享 |
| Capital One | 从外包、商业采购转向开源优先,自建平台,跨角色交叉培养 | 从每天迭代一次到每天多次部署 |
判断一个组织的文化,看它出了事之后怎么做,而不是它墙上怎么写。
11.4 DevOps 工程师
"DevOps 应该是文化而不是岗位"的争论一直存在,但现实中工程效能、运维、配置管理、SRE 团队都在承担 DevOps 的职责。这个角色通常有三项工作:
- 工具平台开发:打通全流程工具链,而不是遇到一个痛点就做一个孤立的工具
- 流程实践落地:同样的工具在不同的流程下效果完全不同。想在不改流程的前提下落地 DevOps 是不现实的
- 技术预研与试点:评估新技术是否适合本公司,而不是盲目追随业界
能力模型:
| 硬实力 | 要求 |
|---|---|
| 编码 | 脚本语言(Shell、Python)熟练,至少一门主力语言能写生产级平台代码 |
| 自动化 | 深入理解 CI/CD;Git、GitLab、Jenkins、SonarQube、Ansible、Docker、Kubernetes 中至少精通几个环节 |
| IT 基础 | Linux、网络、存储;能看懂并修改环境配置 |
| 云与容器 | K8s 被称为"云时代的 Linux";云厂商的核心服务 |
| 业务与流程 | 能发现流程瓶颈,知道更好的流程是什么样 |
| 软实力 | 要求 |
|---|---|
| 沟通 | 向上争取支持,横向打破部门边界,向下讲清楚价值 |
| 同理心 | 交付链上的上下游就是你的用户 |
| 学习 | "Don't just do the same things better – find better things to do."(戴明) |
理想的画像是"梳子型"人才:在多个领域有深度,并能把它们连接起来。
十二、工具选型速查
12.1 按环节
| 环节 | 开源 / 自建 | 商业 / SaaS |
|---|---|---|
| 需求与协作 | Redmine、Taiga | Jira + Confluence、TAPD、Linear |
| 代码托管与评审 | GitLab CE、Gitea、Gerrit | GitHub、GitLab EE |
| CI/CD | Jenkins、GitLab CI、Tekton、Argo Workflows、Woodpecker | GitHub Actions、CircleCI、Harness |
| 构建 | Maven/Gradle、Bazel、BuildKit | Gradle Enterprise(Develocity) |
| 制品库 | Nexus、Harbor | Artifactory |
| 代码质量 | SonarQube CE | SonarQube 商业版、SonarCloud |
| 安全扫描 | Trivy、Semgrep、OWASP ZAP、gitleaks | Snyk、Checkmarx、Black Duck |
| IaC / 配置 | OpenTofu、Ansible、Pulumi、Crossplane | Terraform Cloud |
| GitOps / 发布 | Argo CD、Flux、Argo Rollouts、Flagger | Harness、Spinnaker 托管版 |
| 特性开关 | Unleash、Flagsmith、OpenFeature(标准) | LaunchDarkly |
| 配置中心 | Apollo、Nacos | —— |
| 监控与可观测性 | Prometheus、Grafana、Loki、Tempo、Jaeger、OpenTelemetry | Datadog、New Relic |
| 混沌工程 | Chaos Mesh、LitmusChaos、ChaosBlade | Gremlin、AWS FIS |
| 开发者门户 | Backstage | Port、Cortex |
| 研发度量 | Apache DevLake | LinearB、Jellyfish |
12.2 CI 工具对比
| 工具 | 定位 | 优势 | 劣势 | 适用 |
|---|---|---|---|---|
| Jenkins | 通用"百宝箱" | 插件最多,什么都能做,存量极大 | 架构老,Master 有状态,插件兼容性和安全问题多 | 流程复杂、异构环境多、已有大量存量的企业 |
| GitLab CI | 代码平台内置 | 与 MR 深度集成,自建无额外成本 | 复杂场景下扩展性一般 | 已自建 GitLab 的团队 |
| GitHub Actions | 代码平台内置 + 市场 | Action 生态丰富,托管 Runner 开箱即用 | 企业版之外不能私有化部署;有使用成本 | 开源项目、SaaS 化团队 |
| Tekton | K8s 原生流水线引擎 | 以 CRD 描述,每一步都是容器 | 偏底层,需要自己包装上层体验 | 自建云原生 CI/CD 平台的底座 |
| Argo Workflows | K8s 原生工作流引擎 | 通用 DAG,也常用于数据和 ML 任务 | 同上 | 已采用 Argo 生态的团队 |
| Drone / Woodpecker | 轻量容器化 CI | 简单,一两天就能上手 | 生态较小;Drone 于 2020 年被 Harness 收购,Woodpecker 是它的社区分叉 | 容器化交付的中小团队 |
Jenkins X 是另一个值得了解的产品。它与 Jenkins 只是名字相近:K8s 原生,底层基于 Tekton,强制使用 GitOps 做环境晋级,为每个 PR 自动创建预览环境。它的很多理念后来被广泛采纳,但产品本身的社区活跃度已明显下降,新项目选型前需要评估现状。
选型原则:没有最好的工具,只有最适合当前场景的工具;团队熟悉度是重要因素;工具之间的连通性比单个工具的功能更重要;也不要陷入"工具决定论"。
十三、面试高频问题
Q1:DevOps 是什么?和敏捷、SRE、CI/CD 是什么关系? DevOps 是一套理念和实践,目标是以可持续的方式快速、稳定地交付价值。它的核心是打破开发与运维之间的目标冲突,常用 CALMS 概括。敏捷解决的是需求到开发、测试的协作;DevOps 把协作延伸到运维乃至业务。CI/CD 是 DevOps 最核心的工程实践。SRE 是 Google 对 DevOps 原则的一种具体实现("class SRE implements interface DevOps")。
Q2:CI、持续交付、持续部署的区别? CI:频繁合并到主干,每次合并都自动构建和测试。持续交付:任何通过流水线的版本都随时可以发布到生产,是否发布由人决定。持续部署:通过流水线的版本自动发布到生产,没有人工卡点。
Q3:怎么衡量 DevOps 做得好不好? DORA 四项指标:部署频率、变更前置时间、变更失败率、故障恢复时间(2024 年又加入返工率)。前两项衡量吞吐,后两项衡量稳定性,高效能团队两类指标同时领先。另外两个要点:不要把指标变成个人 KPI;只改一行代码,从提交到上线要多久,是最直观的综合检验。
Q4:蓝绿、金丝雀、滚动发布怎么选? 滚动发布不需要额外资源,但新旧版本并存、回滚慢。蓝绿切换和回滚都是秒级,但资源翻倍,适合规模不大、对回滚速度要求高的系统。金丝雀用小比例流量验证后再放量,性价比最高,大规模系统普遍使用,前提是有指标对比能力。三者的共同前提是新旧版本兼容,尤其是数据库,要用扩展-收缩模式做变更。
Q5:部署和发布有什么区别?为什么要分离? 部署是技术行为,让新代码运行在生产环境;发布是业务行为,让功能对用户可见。分离之后可以随时部署、按需发布(特性开关),降低发布风险,也把"何时上线"的决策交还给业务。
Q6:为什么推荐主干开发?特性分支有什么问题? 长期特性分支会推迟集成,冲突在最后一刻集中爆发,本质上违背了 CI。主干开发强制小批量、频繁集成,未完成的功能用特性开关隐藏。折中方案是生命周期不超过 1~3 天的短命特性分支加 PR 评审。
Q7:自动化测试怎么建设?覆盖率越高越好吗? 分层建设:单元测试多、接口测试为主力、UI 测试少。自动化的目标是防止回归,不是发现更多缺陷。覆盖率只说明代码被执行过,不说明断言是否有效。更应该关注误报率和执行时长:误报一多,大家就不再相信测试结果。存量项目看增量覆盖率。
Q8:什么是 GitOps?和传统 CI/CD 推送部署有什么区别?
Git 是环境期望状态的唯一可信来源,集群内的控制器持续拉取并调谐。与 CI 直接 kubectl apply 的推模式相比:CI 不需要持有集群凭据;漂移会被自动纠正;每次变更都有 PR 记录;回滚就是 git revert。
Q9:SLO 和错误预算怎么用? SLO 是 SLI 的内部目标,错误预算 = 1 − SLO。预算充足时可以加快发布、多做实验;预算耗尽时冻结功能发布,优先做稳定性。它把"要速度还是要稳定"的争论变成了数据决策。告警按燃烧率配置,避免噪声。
Q10:线上出了故障怎么处理? 先止血,再根治:用回滚、关闭开关、切流、降级等最快的方式恢复服务;同步通报影响;恢复后做无责复盘,梳理时间线和根因,给出有负责人、有截止时间的改进项,并跟踪到关闭。改进项优先考虑"机制"(自动化检查、门禁、演练),而不是"下次注意"。
Q11:如何在一家传统企业推动 DevOps? 先获得管理层支持;选贴近核心业务、变更频繁、有改进意愿的试点团队;用价值流分析找瓶颈;只挑一个改进点快速拿到成果;及时展示,再逐步扩展。预期会经历 J 型曲线,每个迭代预留固定的改进容量。先改行为,再改文化,关键在设计机制。
Q12:技术债务怎么治理? 先对规则达成共识,然后把扫描接入流水线让债务可见,对核心模块设基线止损,最后按迭代预留容量偿还。优先处理高频修改的模块,因为利息在那里累积最快。新代码严格执行规则,存量代码控制增量。
Q13:什么是软件供应链安全?流水线里怎么做? 防止依赖、构建过程和制品被污染(SolarWinds、Log4Shell、xz 后门)。流水线里的做法:SCA 扫描依赖漏洞;生成 SBOM;对镜像签名,并在集群准入时校验;构建环境隔离、临时化;第三方 Action 固定到提交 SHA;CI 使用最小权限和短期凭据。可以参照 SLSA 分级逐步提升。
Q14:平台团队和"DevOps 团队"有什么不同?什么是平台工程? 单独成立一个"DevOps 团队"夹在开发和运维之间,只是多了一堵墙。平台团队把基础设施、CI/CD、可观测性、安全等能力做成自服务的产品,通过黄金路径降低业务团队的认知负荷。业务团队仍然端到端负责自己的服务。
Q15:Jenkins 还适合云原生场景吗? Jenkins 的 Master 有状态、难以高可用,流水线逻辑在 Master 上执行,不是为 K8s 设计的。它依然适合存量大、流程复杂、环境异构的企业,可以配合 K8s 动态节点使用。新建的云原生平台更常选 GitLab CI 或 GitHub Actions 做 CI,Argo CD 或 Flux 做 CD;需要自建平台底座时考虑 Tekton。
Q16:什么是混沌工程?和压测、故障演练有什么区别? 混沌工程通过在生产环境受控地注入故障,发现系统中未知的脆弱点。故障演练验证已知场景的预案,压测验证容量。混沌工程的前提是系统已具备基本的弹性和监控;实验要定义稳定状态指标,控制爆炸半径,并能随时终止。
十四、参考资料
课程
- 石雪峰,《DevOps 实战笔记》,极客时间,2019–2020。本文中的配置管理四原则、CI 三问、流水线十大特征、平台建设三阶段、产品设计五层次、度量三组八项指标,以及 NUMMI、GitLab、Etsy、微软、乐视、Facebook 等案例,主要整理自该课程
书籍
| 书名 | 作者 | 看点 |
|---|---|---|
| 《持续交付》 | Jez Humble、David Farley | 部署流水线、配置管理、八条原则 |
| 《持续交付 2.0》 | 乔梁 | 把精益创业与持续交付结合 |
| 《凤凰项目》 | Gene Kim 等 | 小说形式讲三步工作法 |
| 《DevOps 实践指南》 | Gene Kim、Jez Humble 等 | 三步工作法的系统展开与大量案例 |
| 《Accelerate》 | Nicole Forsgren、Jez Humble、Gene Kim | DORA 指标背后的研究方法 |
| 《SRE:Google 运维解密》《The Site Reliability Workbook》 | SLO、错误预算、燃烧率告警 | |
| 《Team Topologies》 | Matthew Skelton、Manuel Pais | 团队类型与交互模式 |
| 《目标》 | 高德拉特 | 约束理论 |
| 《精益产品开发》 | 何勉 | 精益看板在软件开发中的应用 |
报告与网站
- DORA《Accelerate State of DevOps Report》:dora.dev
- Martin Fowler 的文章:Continuous Integration、Feature Toggles(Pete Hodgson)、ParallelChange
- Principles of Chaos Engineering:principlesofchaos.org
- OpenGitOps:opengitops.dev
- SLSA:slsa.dev
- GitLab 2017-01-31 数据库事故复盘:about.gitlab.com/blog(Postmortem of database outage of January 31)
暂无评论,欢迎留下第一条评论。