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. 让其他环节服从约束(非瓶颈环节不要超产)
  4. 提升约束(投资扩容)
  5. 约束被打破后回到第 1 步,不要让惯性成为新约束

2.3 戴明:质量不能靠检验

戴明质量管理 14 条原则的第 3 条:停止依赖检验来达成质量。检验只能证明缺陷存在,不能提升质量,应当把质量内建于流程。

一个常被引用的例子:美国汽车厂在装配线末端安排专人用橡胶锤敲车门检查安装质量,车门质量依然很差;日本工厂没有这个岗位,他们的回答是"设计车门时就保证它不会出问题"。对应到软件就是质量内建(见 5.5 节)。戴明另一项影响深远的贡献是 PDCA 循环(见第九章)。

2.4 康威定律

设计系统的组织,其产生的设计等同于组织的沟通结构。

它的推论对 DevOps 很关键:只要存在独立的测试部门,交付流程中就一定有独立的测试阶段,也就一定有交接和等待。想改变交付流程,往往要先改变组织结构。"逆康威操作"(Inverse Conway Maneuver)就是先按期望的架构调整团队,再让架构跟着演化。

2.5 持续交付的八条原则

出自《持续交付》:

  1. 为软件发布创建一个可重复且可靠的过程
  2. 将几乎所有事情自动化
  3. 把所有东西都纳入版本控制
  4. 越痛苦的事越要频繁地做
  5. 内建质量
  6. "完成"意味着"已发布"
  7. 交付过程是每个成员的责任
  8. 持续改进

三、从哪里开始:转型路径

3.1 工具先行还是文化先行

两个极端都会失败:

  • 工具决定论:"所有团队都用上 Jenkins 了",但照样按瀑布方式工作;敏捷管理工具被当成任务派发系统用;自研平台只是把线下审批搬到了线上
  • 空谈文化:文化不可量化,脱离实践就是"无根之水",组织迟迟看不到收益就会失去耐心

可行的路径是:先改变行为,再通过行为改变文化。改变行为靠机制,即人们愿意做、而且做了有好处的约定。几个机制设计的例子:

机制 改变的行为
Google SRE:服务先由开发自运维,达到质量标准后才交给 SRE;稳定性不达标会被"打回"开发 开发主动关注可运维性,形成责任共担
错误预算:预算内允许失败、不追责;预算耗尽冻结发布 发布速度与稳定性由数据仲裁,而不是由部门博弈决定
CI 红灯 10 分钟内未修复则自动回滚 提交者对集成结果负责
每个迭代固定预留一定比例的工作量给技术改进 改进不再被业务需求无限挤占

从 人、流程、平台 的两两组合看:

            人 ───── 流程               人 + 流程   = 文化(用流程约束和引导行为)
             \       /                  流程 + 平台 = 工具(平台承载标准化流程)
              \     /                   平台 + 人   = 赋能(平台让所有人"同样操作得到同样结果")
               平台

3.2 成熟度模型怎么用

成熟度模型(如中国信通院的 DevOps 能力成熟度模型,覆盖敏捷开发管理、持续交付、技术运营三大部分)是一张"能力地图",不是考试。用法分四步:

  1. 识别差距:对照模型盘点现状,建立能力基线
  2. 锚定目标:只挑出与当前业务瓶颈相关的差距。卖 CRM 软件的公司,容器化未必是眼下的瓶颈。目标要可量化,例如"环境准备时长缩短 50%"
  3. 关注能力而非数字:亚马逊一天部署上万次,不代表每家公司都需要这个频率。由目标推出所需能力,再导入与能力匹配的实践
  4. 持续改进:模型本身也在迭代

一个典型案例:某企业用成熟度模型盘点出 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 转型的通用路径

两种轨迹:

轨迹 优点 风险 要点
自底向上 局部容易见效,资源调动简单 局限在小团队,难以扩展 "羽化原则":先打通本团队与强依赖的上下游,逐步模糊边界;设法让管理层看到成效
自顶向下 资源和权威有保障 目标一压下来,团队总能找到某个口径"证明达标" 需要客观、统一的度量口径。反例:自称前置时间"一周",实际只统计了开发到测试完成,从提需求到上线要两个月

无论哪条轨迹,管理层的认可与持续投入都是必要条件。通用路径分四步:

  1. 选试点:贴近核心业务(关注度高、资源有保障);需求多、变更频繁(容易体现效果);团队有改进意愿(自认为已经很完美的团队不适合)
  2. 找痛点:在试点团队做一次价值流分析,按约束理论找到最短的那块木板
  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,一次性压更多需求 ←── 紧急插单增多 ←──────┘

加人不是解药。《人月神话》早已指出,人数增加后沟通成本迅速上升,新人培训短期内还会拖慢交付。

精益看板五步法:

  1. 可视化流程:如实呈现现有流程,初期不做优化,对组织冲击小
    • 列:按价值流阶段划分,每列再分"进行中"和"已完成"(后者是给下游拉取的缓冲区)
    • 泳道:给需求划清界限,可以设紧急通道、技术改进泳道;前后端任务放在同一泳道,方便暴露依赖
  2. 显式化规则:卡片颜色(需求、缺陷、改进各用一色)、谁在什么时候移动卡片、阻塞如何标记、什么情况必须线下沟通
  3. 限制 WIP:从现状出发渐进收紧,每人并行不超过 2~3 件。看板只能暴露问题,不能解决问题:把 WIP 压到 1,往往会立刻暴露环境不就绪、发布窗口太稀疏之类的固有问题
  4. 管理流动:
    • 站会只看阻塞、紧急和长期停滞的卡片(按停留时长排序看 Top N),不是轮流汇报"昨天做了什么"
    • 队列填充会:业务方和技术方按优先级把需求补入"就绪"列
    • 发布规划会:部署与发布分离后,做到"按节奏部署、按需发布"
  5. 反馈与持续改进:可以参考看板成熟度模型(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):

  1. 每次提交都会触发一次完整的流水线吗?
  2. 每次流水线都会运行自动化测试吗?
  3. 流水线失败后,能在 10 分钟内修复吗?

三个问题对应 CI 建设的三个阶段:

阶段 关键词 前置条件与要点
1 快速集成 有一条以集成为目的的分支;按分支类型定义不同的验证深度;构建资源池标准化,任务在任何节点跑都一样;反馈要快,超过 10~15 分钟研发就会失去耐心,CI 时长要纳入监控并设超时
2 质量内建 每次流水线都跑自动化测试,没有测试的 CI 是"瘸腿 CI";提交级跑快速检查和冒烟测试,集成级再加接口和 UI 测试;按本次变更的范围选择测试(精准测试)
3 文化建立 CI 红灯时停止提交新代码,优先修复;超时未修复就自动回滚;下班前确认 CI 是绿的

一个声明式 Jenkinsfile 示例(未实测):

pipeline {
  agent { label 'k8s-maven' }                  // 动态 Pod 节点,环境由镜像定义
  options { timeout(time: 15, unit: 'MINUTES') } // 超过 15 分钟直接判失败
  stages {
    stage('Checkout') { steps { checkout scm } }
    stage('Build')    { steps { sh 'mvn -B -DskipTests package' } }
    stage('Verify') {
      parallel {                                 // 单测与静态扫描并行
        stage('Unit Test') {
          steps { sh 'mvn -B test' }
          post  { always { junit 'target/surefire-reports/*.xml' } }
        }
        stage('Static Scan') {
          steps { withSonarQubeEnv('sonar') { sh 'mvn -B sonar:sonar' } }
        }
      }
    }
    stage('Quality Gate') {                      // SonarQube 门禁不过则终止
      steps { timeout(time: 5, unit: 'MINUTES') { waitForQualityGate abortPipeline: true } }
    }
  }
}

同样的流程用 GitHub Actions 表达(未实测):

name: ci
on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read            # 默认最小权限,按需放开

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with: { go-version: '1.23' }
      - 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%。误报一多,研发就不再相信测试结果,最后又回到手工确认。治理方法:

  1. 对失败原因分类:环境、网络、数据、脚本缺陷、真实缺陷
  2. 按异常特征自动归类
  3. 对已知的偶发问题(网络抖动)加有限次重试,并隔离不稳定用例(quarantine)
  4. 长期跟踪误报率趋势

UI 自动化的可维护性:用 Page Object 模式按页面封装操作;控件的定位方式放进配置文件,用例中用逻辑名称引用。控件变了只改配置,不改用例。

5.5 内建质量

两条原则:问题发现得越早,修复成本越低;质量是每个人的责任,而不是测试团队的责任。

两个"安灯"案例:

  • 丰田安灯绳:任何工人发现质量问题都可以拉绳停线,管理和质量人员立即到场。停线本身不是目的,目的是问题在源头当场解决
  • 亚马逊客服安灯(2012 年引入):一线客服发现商品有质量或安全风险,可以不经审批直接把商品设为不可购买

质量门禁是软件中的安灯。落地分五步:

  1. 选检查项:不要一次全上,否则研发没法干活。缺陷和安全漏洞优先于代码风格
  2. 定指标并达成共识:
    • 静态阈值用于零容忍项,例如高危漏洞数 = 0
    • 动态阈值用于存量项目,看增量和趋势,例如"问题总数不得高于基线"、"新增代码覆盖率 ≥ 80%"
  3. 自动执行:门禁尽量靠近问题产生的地方,IDE 插件好过提交检查,提交检查好过发布前检查
  4. 定义处理方式:结果分为失败、告警、人工确认三级,渐进式收紧;规则和处理方式要向全员宣导
  5. 持续调整:初期留出弹性,目标是质量提升,而不是"通过门禁"

门禁规则示例:

规则 比较 阈值 结果
单元测试通过率 = 100% 失败
新增代码行覆盖率 ≥ 80% 失败
阻塞/严重级代码问题 = 0 失败
高危依赖漏洞(CVSS ≥ 7) = 0 失败
代码重复率 ≤ 5% 告警

真正考验内建质量的,不是门禁建了多少,而是有多少发布走了"特殊审批"和"紧急通道"。

5.6 技术债务

技术债务是为了短期目标选择权宜之计所欠下的代价,而且会"生利息":拖得越久,修改成本越高。症状包括上千行的函数、到处引用的全局变量、一堆名字相近的脚本、改一处崩一片。过时的技术栈也是债务,例如 Python 2 已于 2020 年 1 月 1 日停止官方支持。

量化:SonarQube 用 SQALE 方法,把每条违规折算成修复所需的时间,再计算:

技术债务比例 = 修复全部已知问题的时间 / 从头重写全部代码的估算时间

据此给出 A~E 的评级。用比例而不是绝对值,是为了让 10 万行和 1 千行的项目可以相互比较。修复时间超过重写时间,说明代码已经不可维护。

治理四步:

  1. 共识:对危害、目标、规则集达成一致。规则集频繁变化会让指标失去可比性
  2. 可见:把扫描集成进流水线
  3. 止损:对核心模块设基线,漏洞和严重级问题总量不再增长
  4. 偿还:迭代中预留容量,或在业务低谷期集中处理

优先偿还高频修改的模块:利息在这里累积得最快。可以从版本控制历史统计文件的修改频率,再和复杂度叠加分析,找出热点。新项目从第一天起执行规则,存量项目先控制增量。

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 定义了四条原则:

  1. 声明式:系统的期望状态以声明方式表达
  2. 版本化且不可变:期望状态存储在版本化、不可变、保留完整历史的地方
  3. 自动拉取:代理从源头自动拉取期望状态
  4. 持续调谐:代理持续观测实际状态,并向期望状态收敛
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: { app: web }
  template:
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: registry.example.com/web:3f2a1c
          readinessProbe:  # 没有就绪探针,滚动更新会把流量打到还没准备好的实例上
            httpGet: { path: /healthz/ready, port: 8080 }
            periodSeconds: 5

用 Argo Rollouts 做基于指标自动判定的金丝雀(渐进式交付,未实测):

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: web
spec:
  replicas: 10
  selector:
    matchLabels: { app: web }
  template:            # 与 Deployment 的 Pod 模板相同,略
    metadata:
      labels: { app: web }
    spec:
      containers:
        - name: web
          image: registry.example.com/web:3f2a1c
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 10m }
        - analysis:                      # 成功率不达标则自动中止并回滚
            templates:
              - templateName: success-rate
        - setWeight: 50
        - pause: { duration: 10m }
---
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 异常) 按预估流量模型加压 注入真实世界的故障,常常多个同时发生
环境 多在预发或隔离环境 生产,隔离真实流量 最终走向生产

五条原则:

  1. 围绕稳定状态建立假设:用一组指标描述"系统正常"。业务指标比技术指标更重要,例如下单成功率、支付成功率,而不仅是 CPU 和 QPS
  2. 模拟真实世界的事件:实例宕机、网络延迟与分区、依赖超时、时钟漂移、磁盘满
  3. 在生产环境实验:流量形态和依赖关系只有生产环境是真实的
  4. 持续自动化运行:把实验纳入流水线或定期执行
  5. 最小化爆炸半径:从极小范围开始(例如 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 好指标与度量原则

好指标的四个特征:

  1. 明确受众:给非技术管理者看单测覆盖率没有意义
  2. 直指问题:看到数据就知道该改进什么,例如构建失败率高说明提交前的验证不足
  3. 量化趋势:可以客观采集、能看出趋势。手工填报的"项目达成率"不是好指标
  4. 充满张力:向上能归到业务结果,向下能拆到具体环节。反例:只统计需求个数,把一个需求拆成两个就"翻倍"了

五条原则:全局指标优于局部指标;综合指标优于单一指标;结果指标优于过程指标;团队指标优于个人指标;灵活指标优于固化指标。

反面例子:"准时提测率":按时 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%),让数据自己说明好坏。

把指标关联到组织可以分四步:

  1. 找抓手:需求是唯一贯穿研发全流程的对象
  2. 对大数:先让团队认可数据是准的
  3. 找差距:把大阶段拆细,例如测试周期拆成功能测试、产品走查、埋点测试,定位具体慢在哪一步
  4. 分级别:组织级、团队级、项目级分别看不同的指标。不是所有指标都适合下钻到个人

度量是双刃剑:把度量结果和个人绩效直接绑定,度量就会变成数字游戏。大公司反复推倒重建度量体系,往往就是因为旧体系被"摸透"了。度量的目的是激发团队的改进意愿,而不是用来排名。


九、持续改进:PDCA 与复盘

衡量一个团队是否"做到了 DevOps",要看它有没有持续改进的能力,而不是引入了多少工具。

  • 从 0 到 1:对照最佳实践补齐短板,相对容易
  • 从 1 到 N:团队要能根据业务发展自己识别下一个改进目标

PDCA 循环:Plan(识别问题、分析根因、制定计划)→ Do(实施)→ Check(检查结果是否符合预期)→ Act(好的做法标准化,没解决的问题进入下一轮)。持续改进应当是一系列小而高频的改进,而不是一次大而全的变革,后者影响面广,容易半途而废。

四个落地实践:

  1. 正向复盘:不定级定责,找出流程漏洞,再用机制补上。一个例子:收集常见的编译错误建成案例库,构建失败时自动匹配并推送解决建议
  2. 预留固定改进时间:在待办列表中设"持续改进"类任务,每个迭代固定分配容量;也可以办 Hackathon
  3. 共享业务指标:让团队看到自己交付的功能在线上表现如何;DevOps 指标对内公开,形成适度的横向压力
  4. 激励创造:把团队贡献、工具创新写进绩效目标;推动内部开源,避免重复造轮子

谷歌、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 的职责。这个角色通常有三项工作:

  1. 工具平台开发:打通全流程工具链,而不是遇到一个痛点就做一个孤立的工具
  2. 流程实践落地:同样的工具在不同的流程下效果完全不同。想在不改流程的前提下落地 DevOps 是不现实的
  3. 技术预研与试点:评估新技术是否适合本公司,而不是盲目追随业界

能力模型:

硬实力 要求
编码 脚本语言(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》 Google 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)