<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>DevOps on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/devops/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Wed, 02 Sep 2026 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/devops/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>CI/CD 端到端落地实录：从 Git Push 到 K8s 发布的完整流水线</title>
      <link>https://ftmao.com/blog/posts/cicd-end-to-end-jenkins-k8s/</link>
      <pubDate>Wed, 02 Sep 2026 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/cicd-end-to-end-jenkins-k8s/</guid>
      <description>&lt;h1 id=&#34;cicd-端到端落地实录从-git-push-到-k8s-发布的完整流水线&#34;&gt;CI/CD 端到端落地实录：从 Git Push 到 K8s 发布的完整流水线&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：会写 Jenkinsfile、会用 kubectl，但还没在生产环境把「提交代码 → 自动发布 → 失败回滚」整条链路跑通的同学。
文中例子取自我主导的真实项目 videocloud（视频云转码服务，Spring Boot + Nacos + K8s），与站内《Kubernetes 学习总结与避坑指南》（kubernetes-authoritative-guide-notes.md）互为补充。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;CI/CD 的 Demo 半小时能搭出来，生产可用要踩半年坑。&lt;code&gt;kubectl apply&lt;/code&gt; 返回 0 不代表发布成功，Pod Ready 不代表配置生效。本文按真实落地顺序拆解五个阶段，每个阶段给出可抄的配置和踩过的坑。&lt;/p&gt;
&lt;h2 id=&#34;一全景一条流水线要干多少事&#34;&gt;一、全景：一条流水线要干多少事&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    Dev[git push / merge] --&amp;gt; WH[Webhook 触发 Jenkins]

    subgraph S1[&amp;#34;阶段一：拉取与编译&amp;#34;]
        A1[检出 commit] --&amp;gt; A2[恢复依赖缓存]
        A2 --&amp;gt; A3[单测 + 打包]
    end

    subgraph S2[&amp;#34;阶段二：推送产物&amp;#34;]
        B1[构建镜像] --&amp;gt; B2[推 ACR&amp;lt;br/&amp;gt;tag = git commit]
        B3[渲染部署模板&amp;lt;br/&amp;gt;快照留档]
    end

    subgraph S3[&amp;#34;阶段三：执行变更单&amp;#34;]
        C1[DB changelog&amp;lt;br/&amp;gt;Liquibase] --&amp;gt; C2[Nacos changelog&amp;lt;br/&amp;gt;OpenAPI 发布]
    end

    subgraph S4[&amp;#34;阶段四：部署与验证&amp;#34;]
        D1[更新 ConfigMap&amp;lt;br/&amp;gt;+ 滚动更新] --&amp;gt; D2[rollout status 阻塞]
        D2 --&amp;gt; D3[冒烟测试]
        D3 --&amp;gt;|成功| OK[发布完成]
        D3 --&amp;gt;|失败| RB[rollout undo&amp;lt;br/&amp;gt;自动回滚 + 告警]
    end

    WH --&amp;gt; S1 --&amp;gt; S2 --&amp;gt; S3 --&amp;gt; S4&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;阶段顺序有一条铁律：&lt;strong&gt;先备好「环境」，再动「代码」&lt;/strong&gt;——数据库先于应用升级（新代码依赖新表结构），配置先于 Pod 重启（新 Pod 不能拿旧配置）。顺序反了，就是半夜被叫醒的那种故障。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二阶段一jenkins-拉取与编译&#34;&gt;二、阶段一：Jenkins 拉取与编译&lt;/h2&gt;
&lt;h3 id=&#34;21-触发webhook不是轮询&#34;&gt;2.1 触发：Webhook，不是轮询&lt;/h3&gt;
&lt;p&gt;Git 平台配 Webhook，push/merge 时推给 Jenkins。不要用 SCM 轮询——几十个仓库每分钟扫一遍，Jenkins 自己先爆了。分支与环境对应：&lt;code&gt;feature/*&lt;/code&gt; 只编译+单测，&lt;code&gt;develop&lt;/code&gt; 自动部署到 dev，&lt;code&gt;master&lt;/code&gt;/tag 走完整流水线 + 人工审批发 prod。&lt;/p&gt;
&lt;h3 id=&#34;22-编译提速缓存--并行&#34;&gt;2.2 编译提速：缓存 + 并行&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;拉取与编译&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        checkout scm
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            mvn -q -T 1C test                      # 单测先跑，挂了直接终止
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            mvn -q -T 1C -DskipTests package       # -T 1C 按核数并行
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;依赖缓存是性价比最高的优化&lt;/strong&gt;：&lt;code&gt;~/.m2&lt;/code&gt; 挂共享 PVC，构建从 5 分钟压到 1 分钟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;单测只跑变更相关模块&lt;/strong&gt;（&lt;code&gt;mvn -pl -am&lt;/code&gt;），全量留给 nightly。门禁是拦问题的，不是表演严格的。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;23-jenkins-自身怎么部署才不拖后腿&#34;&gt;2.3 Jenkins 自身怎么部署才不拖后腿&lt;/h3&gt;
&lt;p&gt;流水线慢，一半原因在 Jenkins 自己的形态。我们的规范五条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Controller 只调度不构建&lt;/strong&gt;（executors 设为 0），否则构建打满 Controller，UI 卡死、webhook 丢失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;agent 用 Kubernetes 插件动态拉起&lt;/strong&gt;：按语言预定义 Pod 模板（maven/node/go），构建完即销毁，并发上限就是集群容量：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# agent Pod 模板要点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;nodeSelector&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;node-pool&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ci-build }     &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 调度到 CI 专用节点池&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;containers&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;maven&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;image&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;registry.../ci/maven:3.9-jdk17 &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 自维护构建镜像，工具一次装好&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;resources&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;requests&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;, memory&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;8Gi }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;volumeMounts&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - { &lt;span style=&#34;color:#f92672&#34;&gt;name: m2-cache, mountPath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/root/.m2 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;volumes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;m2-cache&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;persistentVolumeClaim&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;claimName&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;ci-m2-cache }&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ol start=&#34;3&#34;&gt;
&lt;li&gt;&lt;strong&gt;CI 独立节点池&lt;/strong&gt;：SSD 磁盘（编译是 IO 密集型）、taint 防业务混部、闲时自动缩容。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建镜像自己维护&lt;/strong&gt;：JDK、Maven 私服配置、docker/kubectl/cosign 全打进去，agent 起来就能干活。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;部署阶段并行化&lt;/strong&gt;：无依赖的服务用 &lt;code&gt;parallel&lt;/code&gt; 并发部署。我们 8 个服务的流水线从 25 分钟降到 9 分钟。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三阶段二推镜像推配置&#34;&gt;三、阶段二：推镜像、推配置&lt;/h2&gt;
&lt;h3 id=&#34;31-镜像-tag回滚能力的地基&#34;&gt;3.1 镜像 tag：回滚能力的地基&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;environment &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    IMAGE &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;registry.cn-hangzhou.aliyuncs.com/mycompany/videomart-videocloud&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    TAG   &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${env.GIT_COMMIT.take(8)}&amp;#34;&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// git commit 短哈希：唯一、可追溯、可回滚
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;三条规矩：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;部署 tag 必须唯一可追溯&lt;/strong&gt;，杜绝 &lt;code&gt;latest&lt;/code&gt;——回滚时说不清「上一个 latest」是谁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生产仓库开 tag 不可变&lt;/strong&gt;，禁止覆盖推送，否则同 tag 不同代码，回滚失效。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;推送后 cosign 签名&lt;/strong&gt;，K8s 侧用 Kyverno 校验，未签名拒绝部署——镜像仓库凭证泄露时，这是最后一道闸。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;32-部署配置仓库里只存一份参数化模板&#34;&gt;3.2 部署配置：仓库里只存一份参数化模板&lt;/h3&gt;
&lt;p&gt;原则：&lt;strong&gt;代码仓库不按环境拆分配置&lt;/strong&gt;（没有 prod/、dev/ 目录）。同一份配置写三遍，改一处忘两处是必然结局。videocloud 的做法——部署模板随应用仓库，全部 &lt;code&gt;${占位符}&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;videocloud/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── k8s_deploy/                                    # 部署模板：环境无关，只有一份
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── k8s-deployment-videomart-videocloud.yaml   # 头部注释列出全部参数
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── k8s-service-videomart-videocloud.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── k8s-persistentvolumeclaim-videomart-videocloud.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── changelog/          # 数据库/Nacos 变更单（第四章）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── src/main/resources/bootstrap.properties   # 服务读 Nacos 的方式（第五章）&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;### 参数: APP_NAME, APP_PORT, NAMESPACE, VERSION&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${NAMESPACE}     &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 发布到哪个环境，由 CD 注入&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;ports&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - { &lt;span style=&#34;color:#f92672&#34;&gt;name: http-0, port: 9577, targetPort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;9577&lt;/span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;流水线在这一步把渲染结果（含镜像 tag、namespace、副本数的完整快照）提交到发布记录库留档。&lt;strong&gt;模板只有一份&lt;/strong&gt;保证「写法一致」，&lt;strong&gt;快照留档&lt;/strong&gt;保证「可审计、可回滚」，两件事分开解决。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四阶段三变更单驱动数据库与-nacos&#34;&gt;四、阶段三：变更单驱动——数据库与 Nacos&lt;/h2&gt;
&lt;p&gt;发布事故的头号根源不是工具，而是&lt;strong&gt;变更没落盘&lt;/strong&gt;：开发改了字段口头通知运维，配置加在 Nacos 控制台、测试环境忘了加。规范只有一条：&lt;strong&gt;一次迭代对「环境」的所有变更——表结构、Nacos 配置——必须作为 changelog 文件随代码提交、随代码评审&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;videocloud/changelog/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;├── database/    # Liquibase 格式
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   ├── videomart-videocloud-changelog-db-v6.0.4.0-01.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;│   └── ...-02.yaml ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;└── nacos/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    └── videomart-videocloud-changelog-nacos-v6.0.4.0-01.yaml&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;命名 &lt;code&gt;{服务}-changelog-{db|nacos}-{版本}-{序号}.yaml&lt;/code&gt;，序号定执行顺序。CD 的职责是按序、幂等地执行：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[扫描 changelog&amp;lt;br/&amp;gt;按版本+序号排序] --&amp;gt; B{changeSet id&amp;lt;br/&amp;gt;已执行?}
    B --&amp;gt;|是| C[跳过，幂等]
    B --&amp;gt;|否| D[&amp;#34;渲染占位符&amp;lt;br/&amp;gt;${VAR} ← Jenkins 凭据&amp;#34;]
    D --&amp;gt; E[执行&amp;lt;br/&amp;gt;DB: liquibase update&amp;lt;br/&amp;gt;Nacos: OpenAPI]
    E --&amp;gt; F[记录 id&amp;lt;br/&amp;gt;失败即终止流水线]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;41-数据库变更单liquibase&#34;&gt;4.1 数据库变更单（Liquibase）&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# changelog/database/videomart-videocloud-changelog-db-v6.0.4.0-01.yaml（节选）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;databaseChangeLog&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;changeSet&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud-changelog-db-v6.0.3.0-05&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;author&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;cline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;comment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Add stream task table for transcoding&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;changes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;sql&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;sql&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              CREATE TABLE `GTT_VIDEOMART_VIDEOCLOUD_STREAM_TASK` ( ... );&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;CD 用官方镜像执行，已执行的 changeSet 记在 &lt;code&gt;DATABASECHANGELOG&lt;/code&gt; 表里，天然幂等：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;    docker run --rm -v $WORKSPACE/changelog/database:/liquibase/changelog \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        liquibase/liquibase:4.29 \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        --url=jdbc:mysql://prod-mysql:3306/videomart \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        --username=$DBU --password=$DBP \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        --changelog-file=videomart-videocloud-changelog-db-${VERSION}.yaml \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        update
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;纪律三条：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;只增不改、向后兼容&lt;/strong&gt;：滚动更新期间新旧 Pod 同时在线，新代码要的表结构先就位，旧代码不能被新结构弄挂。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;破坏性变更拆两次发布&lt;/strong&gt;：先发「代码不再读写该字段」，观察一周，再发「删字段」。代码能回滚，删掉的列回不来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行过的 changeSet 绝不许改&lt;/strong&gt;（checksum 变了 Liquibase 直接拒绝），要改就写新变更单。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;42-nacos-变更单&#34;&gt;4.2 Nacos 变更单&lt;/h3&gt;
&lt;p&gt;自定义格式，与 DB 同一套约定（videocloud 真实文件）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# changelog/nacos/videomart-videocloud-changelog-nacos-v6.0.4.0-01.yaml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;nacosChangeLog&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;changeSet&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud-changelog-nacos-v6.0.4.0-01&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;comment&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videocloud drm configuration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;changes&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - &lt;span style=&#34;color:#f92672&#34;&gt;namespace&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${NAMESPACE}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;group&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videocloud&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;dataId&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gtt-videomart-videocloud.properties&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;format&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;properties&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;patchContent&lt;/span&gt;: |-&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              gtt.videomart.videocloud.db.username = ${VIDEOMART_VIDEOCLOUD_DATABASE_USERNAME}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              gtt.videomart.videocloud.aws.s3.accessKeyId = ${FUSIONSTORAGE_ACCESS_KEY}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;              gtt.videomart.videocloud.aws.s3.secretAccessKey = ${FUSIONSTORAGE_SECRET_KEY}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;注意：&lt;strong&gt;变更单里没有一个真实密钥&lt;/strong&gt;，全是 &lt;code&gt;${占位符}&lt;/code&gt;——「key 叫什么、引哪份凭据」可以进 Git，「值是什么」不可以。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;patchContent&lt;/code&gt; 是增量。CD 脚本逻辑：&lt;strong&gt;GET 现有配置 → envsubst 渲染增量 → 合并（增量覆盖同名 key）→ 整体发布 → 读回校验 → 记录 id&lt;/strong&gt;。发布用的是 Nacos OpenAPI（&lt;code&gt;POST /nacos/v1/cs/configs&lt;/code&gt;），Nacos 客户端长轮询秒级生效，不需要重启 Pod。生产 Nacos 必须开鉴权。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五凭证权限划分密钥只有-cd-管理员可见&#34;&gt;五、凭证权限划分：密钥只有 CD 管理员可见&lt;/h2&gt;
&lt;p&gt;占位符背后的真实值在哪、谁能碰？分三层：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    Dev[开发者] --&amp;gt;|只能看到| Git[Git 仓库&amp;lt;br/&amp;gt;changelog 占位符 / 模板]
    CD[CD 管理员] --&amp;gt;|独占维护| Cred[Jenkins 凭据&amp;lt;br/&amp;gt;DB 账号 / S3 密钥]
    CD --&amp;gt;|独占维护| Sec[K8s Secret&amp;lt;br/&amp;gt;videomart-videocloud-secret]
    Pipe[CD 流水线] --&amp;gt;|&amp;#34;withCredentials 临时注入&amp;#34;| Cred
    Pod[运行中的 Pod] --&amp;gt;|&amp;#34;envFrom / secretRef&amp;#34;| Sec&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;四条规矩：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Jenkins 分角色。&lt;/strong&gt; Role-Based Strategy 插件：开发者能触发构建、看日志；只有 CD 管理员能管凭据、改生产流水线。凭据按 Folder 隔离，prod 的 DB 账号对开发者连「存在」都不可见。流水线里 &lt;code&gt;withCredentials&lt;/code&gt; 临时注入，用完即销毁。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 服务只引用配置，绝不内嵌。&lt;/strong&gt; videocloud 的 &lt;code&gt;bootstrap.properties&lt;/code&gt; 节选：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-properties&#34; data-lang=&#34;properties&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.server-addr&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${NACOS_SERVER_ENDPOINT:localhost:8848}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.namespace&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${NACOS_NAMESPACE:videomart}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.group&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;videocloud&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.username&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${NACOS_USERNAME}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.password&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${NACOS_PASSWORD}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.cloud.nacos.config.refresh-enabled&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;spring.config.import&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;optional:nacos:gtt-videomart-videocloud.properties&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;服务只声明「连哪个 Nacos、读哪个 dataId」，账号全占位，生产由 K8s Secret 注入。默认值只给本地开发用，宁可留空让缺参时启动直接报错，也不要把内网地址当真默认值写进仓库。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 注入靠 Secret 引用。&lt;/strong&gt; 看 &lt;code&gt;k8s_deploy&lt;/code&gt; 的 Deployment 模板：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;envFrom&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;configMapRef&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gtt-common-config }       &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 公共非敏感配置&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;secretRef&lt;/span&gt;:    { &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud-secret } &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 本服务的 Nacos/DB 账号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  - &lt;span style=&#34;color:#f92672&#34;&gt;secretRef&lt;/span&gt;:    { &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gtt-common-secret }       &lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# 公共敏感配置&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;Secret 由 CD 管理员提前建好，开发者不需要、也不应该知道里面的值。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 轮换只动一处。&lt;/strong&gt; 换 DB 密码 = 更新 Jenkins 凭据 + K8s Secret + 触发一次流水线，代码和模板都不用动。这就是「引用配置」相对「携带配置」的收益。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六阶段四更新-configmap&#34;&gt;六、阶段四：更新 ConfigMap&lt;/h2&gt;
&lt;p&gt;《Kubernetes 学习总结与避坑指南》第七章讲透了两个坑，这里给结论：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;环境变量注入的 ConfigMap，改了不生效&lt;/strong&gt;，必须重启 Pod；文件挂载方式 kubelet 分钟级同步，但应用要自己支持 reload。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;subPath&lt;/code&gt; 挂载单文件的 ConfigMap 永远不自动更新&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;流水线的稳妥做法：&lt;code&gt;kubectl apply&lt;/code&gt; 更新 ConfigMap 后&lt;strong&gt;无脑跟一次滚动更新&lt;/strong&gt;——反正接下来要换镜像，合成一次滚动零成本。「要不要重启」交给脚本做死，比交给值班同学半夜判断可靠。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七阶段五部署与验证&#34;&gt;七、阶段五：部署与验证&lt;/h2&gt;
&lt;h3 id=&#34;71-部署配置长什么样&#34;&gt;7.1 部署配置长什么样&lt;/h3&gt;
&lt;p&gt;发布完成后集群里的对象关系：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    GW[Gateway&amp;lt;br/&amp;gt;order.example.com] --&amp;gt; RT[HTTPRoute&amp;lt;br/&amp;gt;/api/order/**]
    RT --&amp;gt; SVC[Service videomart-videocloud&amp;lt;br/&amp;gt;ClusterIP 9577]
    SVC --&amp;gt; P1[Pod ✅] &amp;amp; P2[Pod ✅] &amp;amp; P3[Pod ✅]
    DEP[Deployment&amp;lt;br/&amp;gt;replicas: 3&amp;lt;br/&amp;gt;RollingUpdate] -.管理.-&amp;gt; P1
    CM[ConfigMap] -.挂载.-&amp;gt; P1
    NC[(Nacos)] -.长轮询推送.-&amp;gt; P1
    NC -.长轮询推送.-&amp;gt; P2
    NC -.长轮询推送.-&amp;gt; P3
    P1 --&amp;gt; DB[(MySQL)]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;videocloud 的 Deployment 模板里有几处直接抄的规范：&lt;strong&gt;&lt;code&gt;podAntiAffinity&lt;/code&gt; 强制同服务 Pod 分散到不同节点&lt;/strong&gt;、&lt;code&gt;topologySpreadConstraints&lt;/code&gt; 跨可用区打散、&lt;code&gt;revisionHistoryLimit: 10&lt;/code&gt; 留回滚版本、探针端口与容器启动端口写死一致。对外暴露用 &lt;strong&gt;Gateway API&lt;/strong&gt;（已 GA，Ingress 停止演进），把「入口」（Gateway，运维管）和「路由」（HTTPRoute，开发管）拆开。&lt;/p&gt;
&lt;h3 id=&#34;72-部署与验证流水线里真正干活的命令&#34;&gt;7.2 部署与验证：流水线里真正干活的命令&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;部署与验证&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            # 1. 渲染模板并部署
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            envsubst &amp;lt; k8s_deploy/k8s-deployment-videomart-videocloud.yaml \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                | kubectl --kubeconfig=$KUBECONFIG apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            # 2. 阻塞等待滚动更新完成，超时/失败非零退出
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            kubectl --kubeconfig=$KUBECONFIG -n $NAMESPACE \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                rollout status deployment/videomart-videocloud --timeout=300s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            # 3. 冒烟测试：健康检查 + 核心业务接口
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;            curl -fsS --retry 5 http://videomart-videocloud:9577/healthz || exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;        &amp;#39;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    post &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        failure &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n $NAMESPACE \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;                rollout undo deployment/videomart-videocloud || true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#75715e&#34;&gt;// + 钉钉/邮件告警
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;再强调一遍：&lt;code&gt;apply&lt;/code&gt; 成功只代表 apiserver 收下了 YAML。&lt;strong&gt;rollout status 阻塞 → 冒烟测试 → 失败自动 undo&lt;/strong&gt; 三件套缺了，你的流水线只是「自动提交 YAML 的机器」。&lt;/p&gt;
&lt;h3 id=&#34;73-部署凭证别把-admin-kubeconfig-给-jenkins&#34;&gt;7.3 部署凭证：别把 admin kubeconfig 给 Jenkins&lt;/h3&gt;
&lt;p&gt;给 Jenkins 一个&lt;strong&gt;只能操作特定 namespace、只能 patch Deployment&lt;/strong&gt; 的 ServiceAccount（Role 限定 verbs &lt;code&gt;get/list/watch/patch/update&lt;/code&gt;，完整 RBAC 示例见笔记 12.3 节）。凭证泄露的爆炸半径锁死在一个 namespace，且随时可 &lt;code&gt;delete rolebinding&lt;/code&gt; 吊销。prod 部署再叠一个 &lt;code&gt;input&lt;/code&gt; 人工审批。&lt;/p&gt;
&lt;h3 id=&#34;74-弹性伸缩hpa-不是配上就完事&#34;&gt;7.4 弹性伸缩：HPA 不是配上就完事&lt;/h3&gt;
&lt;p&gt;前提：&lt;strong&gt;requests 必须配准&lt;/strong&gt;（按 P95 实际用量）——HPA 的利用率是 &lt;code&gt;实际用量 / requests&lt;/code&gt;，requests 瞎填，扩缩时机全错。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;autoscaling/v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;scaleTargetRef&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;apiVersion: apps/v1, kind: Deployment, name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videomart-videocloud }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;minReplicas&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# 生产最小副本 ≥ 2，单副本没资格谈弹性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;maxReplicas&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt;       &lt;span style=&#34;color:#75715e&#34;&gt;# 按下游容量倒推（DB 连接数 / 每 Pod 连接数）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;metrics&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Resource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;resource&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;name: cpu, target&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;type: Utilization, averageUtilization&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; } }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;behavior&lt;/span&gt;:             &lt;span style=&#34;color:#75715e&#34;&gt;# 快扩慢缩&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;scaleUp&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;stabilizationWindowSeconds&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;policies&lt;/span&gt;: [{ &lt;span style=&#34;color:#f92672&#34;&gt;type: Percent, value: 100, periodSeconds&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;60&lt;/span&gt; }]   &lt;span style=&#34;color:#75715e&#34;&gt;# 每分钟最多翻倍&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;scaleDown&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;stabilizationWindowSeconds&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;                                &lt;span style=&#34;color:#75715e&#34;&gt;# 缩容观察 5 分钟&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;policies&lt;/span&gt;: [{ &lt;span style=&#34;color:#f92672&#34;&gt;type: Pods, value: 1, periodSeconds&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;120&lt;/span&gt; }]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;配套三条：&lt;strong&gt;HPA 必搭 PDB&lt;/strong&gt;（防节点维护+缩容+发布叠加把服务缩没）；&lt;strong&gt;GitOps 场景 Deployment YAML 别写死 &lt;code&gt;replicas&lt;/code&gt;&lt;/strong&gt;（否则 ArgoCD 每次同步把 HPA 扩的副本压回去）；&lt;strong&gt;HPA 管 Pod、Cluster Autoscaler 管节点&lt;/strong&gt;，两层都要配，流量可预期的业务用 KEDA 定时伸缩提前备容量。&lt;/p&gt;
&lt;h3 id=&#34;75-灰度发布现有流程加两个-stage-就能支持&#34;&gt;7.5 灰度发布：现有流程加两个 stage 就能支持&lt;/h3&gt;
&lt;p&gt;前面几节的设计纪律已经埋好了灰度所需的前提：模板里 Deployment 命名带版本（&lt;code&gt;videomart-videocloud-${VERSION}&lt;/code&gt;）、Pod 打 &lt;code&gt;app + version&lt;/code&gt; 双标签，而 &lt;strong&gt;Service 的 selector 只选 &lt;code&gt;app&lt;/code&gt;&lt;/strong&gt;——新旧两个 Deployment 可以并行存在，Pod 同时挂在同一个 Service 下。在这个基础上，灰度有两级做法：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一级：副本级灰度（零新组件）。&lt;/strong&gt; 渲染同一份模板，只换 &lt;code&gt;VERSION&lt;/code&gt; 和 &lt;code&gt;REPLICAS&lt;/code&gt;，拉起 1 个新版本副本与旧版并存——流量比 ≈ 副本比（3 旧 + 1 新 ≈ 25% 流量进新版）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;VERSION&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;v6.0.5.0 REPLICAS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; IMAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$IMAGE:$TAG &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    envsubst &amp;lt; k8s_deploy/k8s-deployment-videomart-videocloud.yaml &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    | kubectl --kubeconfig&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$KUBECONFIG apply -f -&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;第二级：权重级灰度（精确比例）。&lt;/strong&gt; 新旧版本各起一个 Service（selector 分别锁定各自 &lt;code&gt;version&lt;/code&gt;），用 Gateway API 的 HTTPRoute &lt;code&gt;backendRefs&lt;/code&gt; 配 &lt;code&gt;weight: 95 / 5&lt;/code&gt;，或上 Istio VirtualService 按权重/请求头分流。要「只让内部员工进新版」这类按用户维度的灰度，只能走这一级。&lt;/p&gt;
&lt;p&gt;流水线形态——在「变更单执行」和「全量部署」之间插入灰度段：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[变更单执行] --&amp;gt; B[部署 canary&amp;lt;br/&amp;gt;1 副本或 5% 权重]
    B --&amp;gt; C[观察期&amp;lt;br/&amp;gt;指标对比 / 人工 input]
    C --&amp;gt;|正常| D[全量滚动更新&amp;lt;br/&amp;gt;删除 canary]
    C --&amp;gt;|异常| E[删除 canary&amp;lt;br/&amp;gt;流量即刻回稳]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;要点四条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;灰度期新旧版本同时在线，DB/Nacos 变更必须向后兼容&lt;/strong&gt;——changelog「只增不改」的纪律正是为此准备的；不兼容的变更没有资格灰度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;观察期看指标不看感觉&lt;/strong&gt;：canary 的错误率、P99 延迟与 stable 对比，Prometheus 按 &lt;code&gt;version&lt;/code&gt; 标签分组就能出数——这就是 Pod 要打 version 标签的另一个理由。观察期可以是定时（如 10 分钟）加 Jenkins &lt;code&gt;input&lt;/code&gt; 人工确认。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回滚动作极轻&lt;/strong&gt;：删掉 canary Deployment，Service selector 自动不再选中它，秒级完成，不用动旧版本一根毫毛。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Nacos 配置也能灰度&lt;/strong&gt;：配置支持按 IP/标签灰度下发，配置类变更先推给 canary 实例验证，再全量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;76-实操给新版本分-20-流量的完整跑法&#34;&gt;7.6 实操：给新版本分 20% 流量的完整跑法&lt;/h3&gt;
&lt;p&gt;以「线上 v6.0.4.0 共 4 副本，给 v6.0.5.0 分 20% 流量」为例，两种做法选一。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法 A：副本比例近似（4 旧 + 1 新 ≈ 20%）&lt;/strong&gt;。改动最小，适合服务间（东西向）流量或没有网关层权重能力的场景：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 1. 变更单已执行完毕（DB/Nacos，向后兼容）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 2. 起 1 个新版本副本（与旧版并存，同一个 Service 下）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;VERSION&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;v6.0.5.0 REPLICAS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; IMAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$IMAGE:$NEW_TAG &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    envsubst &amp;lt; k8s_deploy/k8s-deployment-videomart-videocloud.yaml | kubectl apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl rollout status deployment/videomart-videocloud-v6.0.5.0 --timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;300s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 3. 观察：Prometheus 按 version 分组对比错误率 / P99&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 4a. 正常 → 全量：新版本扩到满配，再删旧版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;VERSION&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;v6.0.5.0 REPLICAS&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt; IMAGE&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;$IMAGE:$NEW_TAG &lt;span style=&#34;color:#ae81ff&#34;&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    envsubst &amp;lt; k8s_deploy/k8s-deployment-videomart-videocloud.yaml | kubectl apply -f -
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl rollout status deployment/videomart-videocloud-v6.0.5.0 --timeout&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;300s
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl delete deployment videomart-videocloud-v6.0.4.0
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# 4b. 异常 → 回滚：删掉 canary 即可，旧版全程没动过&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl delete deployment videomart-videocloud-v6.0.5.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;做法 B：HTTPRoute 权重精确 20%&lt;/strong&gt;。适合入口（南北向）HTTP 流量。需要两个按 version 锁定 selector 的 Service：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# stable 和 canary 各一个 Service，selector 分别锁死版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# videomart-videocloud-stable:  selector { app: videomart-videocloud, version: v6.0.4.0 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# videomart-videocloud-canary:  selector { app: videomart-videocloud, version: v6.0.5.0 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;gateway.networking.k8s.io/v1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;kind&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;HTTPRoute&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;metadata&lt;/span&gt;: { &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;videocloud-route }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;spec&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;parentRefs&lt;/span&gt;: [{ &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;prod-gateway }]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;hostnames&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;videocloud.example.com&amp;#34;&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;rules&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;backendRefs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - { &lt;span style=&#34;color:#f92672&#34;&gt;name: videomart-videocloud-stable, port: 9577, weight&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        - { &lt;span style=&#34;color:#f92672&#34;&gt;name: videomart-videocloud-canary, port: 9577, weight&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;20&lt;/span&gt; }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;调比例就是改 weight（20% → 50% → 100%)，每次 &lt;code&gt;kubectl apply&lt;/code&gt; 即时生效。全量后把 stable Service 指向新版本、删掉 canary，路由权重归位。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;做法 B 有一个必踩的坑&lt;/strong&gt;：原来那个 selector 只选 &lt;code&gt;app&lt;/code&gt; 的 Service，灰度期间会同时选中新旧 Pod——HTTPRoute 里 20% 的权重之外，走老 Service 的内部调用方还在按副本比随机进新版，等于流量叠加失控。所以权重灰度前，必须把老 Service 的 selector 也加上 &lt;code&gt;version&lt;/code&gt; 锁到旧版，或者干脆只保留 stable/canary 两个 Service。&lt;/p&gt;
&lt;p&gt;对应的 Jenkinsfile 就是在「变更单执行」后加两个 stage：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-groovy&#34; data-lang=&#34;groovy&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;灰度 20%&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;deploy-canary.sh $NEW_TAG 20&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// 上面做法 A/B 的脚本化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;灰度观察&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;check-canary-metrics.sh&amp;#39;&lt;/span&gt;              &lt;span style=&#34;color:#75715e&#34;&gt;// 错误率/P99 对比，超阈值非零退出
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        input message: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;灰度指标正常，是否全量？&amp;#39;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 人工确认，prod 必备
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;stage&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;全量&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; steps &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;promote-canary.sh $NEW_TAG&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;post &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; failure &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;cleanup-canary.sh || true&amp;#39;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;//&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;任何失败自动清理&lt;/span&gt; canary&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;八配置治理文档化与三环境一致性&#34;&gt;八、配置治理：文档化与三环境一致性&lt;/h2&gt;
&lt;h3 id=&#34;81-配置文档化阈值不再是口口相传&#34;&gt;8.1 配置文档化：阈值不再是「口口相传」&lt;/h3&gt;
&lt;p&gt;症状：限流阈值为什么是 500 不是 1000，只有离职的那个人知道。解法：每个服务在仓库里维护 &lt;code&gt;CONFIG.md&lt;/code&gt;，强制四列——&lt;strong&gt;配置项 / 含义 / 当前值 / 取值依据与影响面&lt;/strong&gt;：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;配置项&lt;/th&gt;
					&lt;th&gt;含义&lt;/th&gt;
					&lt;th&gt;当前值&lt;/th&gt;
					&lt;th&gt;取值依据与影响面&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;upload.retryCount&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;上传重试次数&lt;/td&gt;
					&lt;td&gt;5&lt;/td&gt;
					&lt;td&gt;对象存储偶发抖动 P99 在 3 次内恢复；超过 5 次基本是故障，应快速失败&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;upload.chunkSize&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;分片大小&lt;/td&gt;
					&lt;td&gt;5MB&lt;/td&gt;
					&lt;td&gt;内网带宽实测拐点；调大加剧内存压力&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;纪律三条：&lt;strong&gt;配置项三处一致&lt;/strong&gt;（Git 里的值、CONFIG.md、代码注释，评审时卡点检查）；&lt;strong&gt;阈值必须写取值依据&lt;/strong&gt;（拍脑袋的值两个版本后就成了没人敢动的屎山）；&lt;strong&gt;开关必须有退役计划&lt;/strong&gt;（全量后一个迭代内删除）。进阶：用 JSON Schema 在推送 Nacos 前校验类型和取值范围，低级错误 CI 阶段拦住。&lt;/p&gt;
&lt;h3 id=&#34;82-三环境一致性写法一致取值注入&#34;&gt;8.2 三环境一致性：写法一致，取值注入&lt;/h3&gt;
&lt;p&gt;「在测试环境是好的」的根源：&lt;strong&gt;各环境配置是各自维护的副本&lt;/strong&gt;。解法前面已经埋下——仓库里只有一份参数化模板和变更单，差异压缩成发布时注入的参数：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;差异项&lt;/th&gt;
					&lt;th&gt;注入方式&lt;/th&gt;
					&lt;th&gt;维护者&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;namespace、集群地址&lt;/td&gt;
					&lt;td&gt;CD 环境参数&lt;/td&gt;
					&lt;td&gt;CD 管理员&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;副本数、资源 requests/limits&lt;/td&gt;
					&lt;td&gt;CD 环境参数&lt;/td&gt;
					&lt;td&gt;CD 管理员&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DB 账号、密钥、合作方地址&lt;/td&gt;
					&lt;td&gt;Jenkins 凭据 → envsubst&lt;/td&gt;
					&lt;td&gt;CD 管理员（开发者不可见）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;业务阈值、特性开关&lt;/td&gt;
					&lt;td&gt;changelog 变更单，三环境执行同一份&lt;/td&gt;
					&lt;td&gt;开发者（评审把关）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;「写法」必须三环境一致，「取值」本来就该不同&lt;/strong&gt;。再加两道保险：发 prod 前用各环境参数分别渲染同一份模板并 diff（源头只有一份，diff 干干净净只有几行）；每周漂移检测，发现有人手工改过线上配置就告警（上 ArgoCD 后免费获得）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九行业动态20252026-值得关注的变化&#34;&gt;九、行业动态：2025–2026 值得关注的变化&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;GitOps 成默认答案&lt;/strong&gt;：Jenkins 只构建推镜像，集群内 ArgoCD 从 Git 同步部署——CI 不再需要生产集群凭证，安全性质变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gateway API 取代 Ingress&lt;/strong&gt;：后者已停止演进，新项目别再用它起头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;供应链安全成必修&lt;/strong&gt;：cosign 签名、SBOM、SLSA 框架已进入金融政企招标要求。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI 引擎分化&lt;/strong&gt;：Tekton、Dagger 各有拥趸，GitHub Actions 吞噬中小团队；Jenkins 份额缓降，但存量企业和内网仍是主力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AI 进流水线&lt;/strong&gt;：CI 失败日志自动归因、pipeline 脚本生成评审已经实用；「AI 自动决定发布」不建议——爆炸半径需要人负责。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十思考与感悟&#34;&gt;十、思考与感悟&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. 流水线的本质是「把运气变成流程」。&lt;/strong&gt; 手工发布的质量取决于值班同学当晚的状态。流水线的价值不是快，是把「老司机会记得检查、新人一定会漏」的 20 个检查点固化成代码——先消灭方差，其次才是耗时。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 最贵的知识是顺序和兼容。&lt;/strong&gt; DB 先于应用、配置先于重启、破坏性变更拆两次——背后是同一个认知：&lt;strong&gt;发布中系统处于新旧混合的中间态，所有设计都要保证中间态可用&lt;/strong&gt;。蓝绿、金丝雀、滚动更新都只是它的具体实现。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 验证深度决定发布频次。&lt;/strong&gt; 没有可靠自动验证 → 发布靠人肉兜底 → 怕 → 少发 → 单次变更量大 → 更怕，恶性循环。打破循环的入口就是把验证做深：rollout 阻塞、冒烟、自动回滚。验证越可信，发布越敢小而频。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 配置有权限边界。&lt;/strong&gt; 「引用关系」进 Git，「真实值」进凭据系统，且只有 CD 管理员能碰。这条边界一开始不划，等密钥躺在几百人可见的仓库里再补救就晚了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一避坑清单速查&#34;&gt;十一、避坑清单（速查）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Webhook 触发，不用轮询；Controller 不跑构建；agent 用一次性 Pod + 依赖缓存 PVC + CI 专用 SSD 节点池；无依赖的部署步骤并行。&lt;/li&gt;
&lt;li&gt;镜像 tag = git commit 短哈希，杜绝 &lt;code&gt;latest&lt;/code&gt;；仓库开 tag 不可变；cosign 签名。&lt;/li&gt;
&lt;li&gt;DB 与 Nacos 变更一律走 changelog 变更单，随代码评审，CD 按序幂等执行；控制台手工改属违规。&lt;/li&gt;
&lt;li&gt;数据库变更只增不改；破坏性变更拆两次发布；执行过的 changeSet 绝不许改。&lt;/li&gt;
&lt;li&gt;变更单和模板只写 &lt;code&gt;${占位符}&lt;/code&gt;；真实密钥只在 Jenkins 凭据和 K8s Secret，只有 CD 管理员可见；服务一律 &lt;code&gt;envFrom/secretRef&lt;/code&gt; 引用。&lt;/li&gt;
&lt;li&gt;每个服务维护 CONFIG.md 四列（配置项/含义/当前值/取值依据）；开关有退役计划。&lt;/li&gt;
&lt;li&gt;仓库只存一份环境无关模板，环境差异由 CD 注入；发 prod 前渲染结果 diff；每周漂移检测。&lt;/li&gt;
&lt;li&gt;ConfigMap 更新后跟一次滚动更新（env 注入和 subPath 都不自动生效）。&lt;/li&gt;
&lt;li&gt;Deployment 必配 readinessProbe + &lt;code&gt;maxUnavailable: 0&lt;/code&gt; + preStop 延迟；&lt;code&gt;revisionHistoryLimit&lt;/code&gt; 别设 0。&lt;/li&gt;
&lt;li&gt;验证三件套：rollout status → 冒烟 → 失败自动 undo + 告警。&lt;/li&gt;
&lt;li&gt;HPA：requests 按 P95 配准；minReplicas ≥ 2、maxReplicas 按下游容量倒推；快扩慢缩 + PDB；GitOps 场景别写死 &lt;code&gt;replicas&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;灰度发布：Service selector 只选 app、Deployment 按版本并行，先副本级灰度，要精确比例再上 HTTPRoute 权重；观察期看 version 分组的指标；回滚 = 删 canary。&lt;/li&gt;
&lt;li&gt;Jenkins 用最小权限 ServiceAccount，prod 加 &lt;code&gt;input&lt;/code&gt; 审批；新系统对外暴露用 Gateway API；渲染快照进发布记录库，为 GitOps 铺路。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《Kubernetes 学习总结与避坑指南》（本站 kubernetes-authoritative-guide-notes.md）：ConfigMap 注入机制、RBAC 最小权限、rollout 验证的完整细节&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://www.jenkins.io/doc/&#34;&gt;Jenkins 官方文档&lt;/a&gt; / &lt;a href=&#34;https://docs.liquibase.com/&#34;&gt;Liquibase&lt;/a&gt; / &lt;a href=&#34;https://nacos.io/docs/latest/guide/user/open-api/&#34;&gt;Nacos OpenAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://gateway-api.sigs.k8s.io/&#34;&gt;Kubernetes Gateway API&lt;/a&gt; / &lt;a href=&#34;https://argo-cd.readthedocs.io/&#34;&gt;ArgoCD&lt;/a&gt; / &lt;a href=&#34;https://slsa.dev/&#34;&gt;SLSA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>《持续交付》笔记：每步可退，把集成风险日常化</title>
      <link>https://ftmao.com/blog/bookstore/continuous-delivery-book-notes/</link>
      <pubDate>Fri, 08 Sep 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/continuous-delivery-book-notes/</guid>
      <description>&lt;h1 id=&#34;持续交付笔记每步可退把集成风险日常化&#34;&gt;《持续交付》笔记：每步可退，把集成风险日常化&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;《持续交付》（Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation）是 Jez Humble 和 David Farley 2010 年的作品，拿过 2011 年的 Jolt 大奖。这本书可以说是 DevOps 运动的奠基文献之一——后来《DevOps Handbook》《Accelerate》这些书，根子上都能追溯到这里。&lt;/p&gt;
&lt;p&gt;全书 500 多页，核心其实就一个问题：&lt;strong&gt;怎么让&amp;quot;把软件放到生产环境&amp;quot;这件事，从一年几次的高风险仪式，变成随时可以按一下按钮的平常操作？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我读完最大的感受是：这本书表面讲工具和流程，骨子里讲的是一种风险管理哲学——&lt;strong&gt;不消除风险，而是把风险切碎、摊平到每一天&lt;/strong&gt;。这篇笔记是我消化后的版本，不是章节摘要。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一全书的核心主张一张图看懂&#34;&gt;一、全书的核心主张：一张图看懂&lt;/h2&gt;
&lt;p&gt;先把整本书的骨架画出来，后面每节都能对号入座：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph P[&amp;#34;核心哲学：把风险日常化&amp;#34;]
        PHIL[&amp;#34;&amp;#39;If it hurts,&amp;lt;br/&amp;gt;do it more often&amp;#39;&amp;#34;]
    end
    subgraph PL[&amp;#34;部署流水线 Deployment Pipeline&amp;#34;]
        S1[&amp;#34;① 提交阶段&amp;lt;br/&amp;gt;编译 + 单元测试&amp;lt;br/&amp;gt;分钟级反馈&amp;#34;]
        S2[&amp;#34;② 自动化验收&amp;lt;br/&amp;gt;验收测试 + 容量/安全测试&amp;lt;br/&amp;gt;小时级反馈&amp;#34;]
        S3[&amp;#34;③ 手工验证&amp;lt;br/&amp;gt;UAT / 探索性测试 / 演示&amp;#34;]
        S4[&amp;#34;④ 发布&amp;lt;br/&amp;gt;点按钮，随时可发&amp;#34;]
        S1 --&amp;gt; S2 --&amp;gt; S3 --&amp;gt; S4
    end
    subgraph F[&amp;#34;支撑地基&amp;#34;]
        F1[&amp;#34;配置管理&amp;lt;br/&amp;gt;一切皆版本控制&amp;#34;]
        F2[&amp;#34;自动化测试金字塔&amp;#34;]
        F3[&amp;#34;主干开发 + 特性开关&amp;#34;]
    end
    PHIL --&amp;gt; PL
    F --&amp;gt; PL&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这本书的立论链条是这样的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;软件发布之所以痛苦，是因为它&lt;strong&gt;罕见&lt;/strong&gt;——几个月才发一次，每次集成了几百个变更，出问题没法定位；&lt;/li&gt;
&lt;li&gt;解决思路不是&amp;quot;更小心地发布&amp;quot;，而是&lt;strong&gt;更频繁地发布&lt;/strong&gt;——频率高了，每次变更就小，小了就好定位、好回滚；&lt;/li&gt;
&lt;li&gt;要支撑高频发布，必须有一条自动化的&lt;strong&gt;部署流水线&lt;/strong&gt;，让每个提交自动走完所有质量关卡；&lt;/li&gt;
&lt;li&gt;流水线要可靠，底下得有配置管理、自动化测试、增量式开发三块地基。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：这个逻辑其实和健身一个道理——很久不运动，偶尔跑一次十公里肯定受伤；每天跑两公里反而没事。发布也一样，&amp;ldquo;少做&amp;quot;不是降低风险的办法，&amp;ldquo;把每次做小&amp;quot;才是。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二if-it-hurts-do-it-more-often全书最反直觉的一句话&#34;&gt;二、&amp;ldquo;If it hurts, do it more often&amp;rdquo;：全书最反直觉的一句话&lt;/h2&gt;
&lt;p&gt;书里有一句被广泛引用的话：&lt;strong&gt;&amp;ldquo;如果某件事让你痛苦，那就更频繁地去做它&amp;rdquo;&lt;/strong&gt;（If it hurts, do it more often, and bring the pain forward）。&lt;/p&gt;
&lt;p&gt;直觉上，痛苦的事应该少做。但作者的论证是：痛苦的根源是&lt;strong&gt;批量太大、间隔太久&lt;/strong&gt;。以集成为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;两个程序员各写两周代码再合并，冲突一大堆，合并花两天——于是大家说&amp;quot;少合并&amp;rdquo;；&lt;/li&gt;
&lt;li&gt;正确的解法恰恰相反：&lt;strong&gt;每天合并多次&lt;/strong&gt;，每次冲突只有几行，顺手就解了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同样的道理适用于：数据库迁移、发布上线、甚至和客户的沟通。频率提高会逼着你把流程自动化、把每一步做小，而&amp;quot;小&amp;quot;本身就是最好的风险对冲。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;踩坑记录（书外）&lt;/strong&gt;：我待过的一个团队，发布周期是一个月一次，每次发布日都要通宵，发布完还要留人值班一周&amp;quot;灭火&amp;rdquo;。所有人的共识是&amp;quot;发布太危险，得再慎重些&amp;quot;——于是变更冻结期越来越长，每次积压的变更越来越多，发布越来越危险。这是典型的&lt;strong&gt;用制造问题的方式解决问题&lt;/strong&gt;。后来换了 CTO 强推两周一次、再到一周一次，逼着团队把部署脚本自动化、把回滚预案做出来，半年后发布变成了一个普通下午就能做的事。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：&amp;ldquo;痛苦&amp;quot;是个信号，但它指示的方向经常和直觉相反。看到团队里某件事人人回避，正确的反应不是绕着走，而是问：能不能把它自动化、高频化、常态化？&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三部署流水线每个提交都是可发布候选&#34;&gt;三、部署流水线：每个提交都是可发布候选&lt;/h2&gt;
&lt;p&gt;这是全书最核心的工程概念。部署流水线（deployment pipeline）的正式定义是：&lt;strong&gt;从代码提交到生产发布之间，所有验证步骤的自动化呈现&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    C[&amp;#34;提交 Commit&amp;lt;br/&amp;gt;触发流水线&amp;#34;] --&amp;gt; B[&amp;#34;① 构建 + 单元测试&amp;lt;br/&amp;gt;编译 / 静态检查&amp;lt;br/&amp;gt;几分钟内出结果&amp;#34;]
    B --&amp;gt; A[&amp;#34;② 自动化验收测试&amp;lt;br/&amp;gt;面向业务的端到端场景&amp;lt;br/&amp;gt;在类生产环境跑&amp;#34;]
    A --&amp;gt; U[&amp;#34;③ 手工验证&amp;lt;br/&amp;gt;UAT / 探索性测试&amp;lt;br/&amp;gt;非功能需求确认&amp;#34;]
    U --&amp;gt; R[&amp;#34;④ 发布&amp;lt;br/&amp;gt;同一构建产物&amp;lt;br/&amp;gt;一键部署到生产&amp;#34;]
    B -.-&amp;gt;|&amp;#34;失败即止&amp;#34;| X[&amp;#34;❌ 流水线终止&amp;lt;br/&amp;gt;修复优先于新功能&amp;#34;]
    A -.-&amp;gt;|&amp;#34;失败即止&amp;#34;| X&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;几个关键点，都是容易理解偏的：&lt;/p&gt;
&lt;h3 id=&#34;31-每个阶段都是质量关卡失败即止&#34;&gt;3.1 每个阶段都是质量关卡，失败即止&lt;/h3&gt;
&lt;p&gt;流水线的本质是&lt;strong&gt;递进式的信心建设&lt;/strong&gt;：便宜的、快的测试放前面（单元测试几秒出结果），贵的、慢的放后面（验收测试、性能测试）。任何一个阶段失败，这个构建版本就&amp;quot;死&amp;quot;了，不进入下一阶段。&lt;/p&gt;
&lt;p&gt;这带来一条重要的团队纪律：&lt;strong&gt;流水线红了，修复它就是最高优先级&lt;/strong&gt;——比写新功能优先。因为流水线红着的时候，后面所有人的提交都拿不到反馈，等于团队集体失明。书里管这个叫&amp;quot;不要带着broken的构建回家&amp;rdquo;。&lt;/p&gt;
&lt;h3 id=&#34;32-每次提交都产生一个可发布候选&#34;&gt;3.2 每次提交都产生一个可发布候选&lt;/h3&gt;
&lt;p&gt;这句话值得单独强调：&lt;strong&gt;流水线的每个成功产出都是 release candidate&lt;/strong&gt;。注意是&amp;quot;候选&amp;quot;不是&amp;quot;一定发&amp;quot;——它保证了技术上随时可以发，发不发是业务决策（这点在第六节展开）。&lt;/p&gt;
&lt;p&gt;做到这一点有个硬要求：&lt;strong&gt;部署到每个环境的必须是同一个构建产物&lt;/strong&gt;。测试环境用 A 包、生产环境重新打一个 B 包，等于你在生产环境跑了一个从没被验证过的东西——测试全白做了。正确的做法是：构建只发生一次，产出的二进制包（jar、镜像）在流水线里逐级晋升（promote），环境差异全部由外部配置注入。&lt;/p&gt;
&lt;h3 id=&#34;33-流水线的反馈速度是硬指标&#34;&gt;3.3 流水线的反馈速度是硬指标&lt;/h3&gt;
&lt;p&gt;提交阶段要在几分钟内给出结果——慢了就没人愿意等，大家开始绕过流程。验收测试可以慢一些，但也要控制在小时级。如果一个阶段慢到天天被跳过，那它不是&amp;quot;严格&amp;quot;，是&amp;quot;无效&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：流水线画出来都不难，难的是维护它的&amp;quot;纪律成本&amp;quot;。红了几天的流水线比没有流水线更糟——它教会团队的是&amp;quot;告警可以无视&amp;quot;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四自动化测试金字塔而不是冰淇淋筒&#34;&gt;四、自动化测试：金字塔而不是冰淇淋筒&lt;/h2&gt;
&lt;p&gt;书里的测试分层思想，后来被大家画成了著名的&amp;quot;测试金字塔&amp;quot;（Mike Cohn 提出，本书把它发扬光大了）：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 金字塔[&amp;#34;自动化测试金字塔（数量：下多上少；成本：下低上高）&amp;#34;]
        E2E[&amp;#34;少量：端到端 UI 测试&amp;lt;br/&amp;gt;慢、脆、维护贵&amp;#34;]
        ACC[&amp;#34;适量：验收/服务层测试&amp;lt;br/&amp;gt;面向业务行为&amp;lt;br/&amp;gt;不依赖 UI&amp;#34;]
        UNIT[&amp;#34;大量：单元测试&amp;lt;br/&amp;gt;毫秒级、定位精准&amp;#34;]
    end
    UNIT --&amp;gt; ACC --&amp;gt; E2E&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;书中几个至今仍然尖锐的观点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;验收测试要用业务语言写，由开发者、测试、业务三方共同拥有&lt;/strong&gt;。它不只是测试，更是&amp;quot;可执行的规格说明书&amp;quot;——需求变了，先改验收测试；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;别把测试挂在 UI 上&lt;/strong&gt;。UI 是系统里最不稳定的层，通过 UI 驱动的测试又慢又脆。验收测试应该打在 UI 之下的服务/API 层；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;手工测试不会被消灭，但角色变了&lt;/strong&gt;：从&amp;quot;重复执行回归用例&amp;quot;变成&amp;quot;探索性测试、可用性评估&amp;quot;——机器做重复的，人做需要判断力的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;反面形态叫&amp;quot;冰淇淋筒&amp;quot;：大量手工测试 + 一堆脆弱的 UI 自动化 + 几乎没有单元测试。症状是测试跑一遍要一整天、失败了大半天定位不到问题、最后团队对测试结果失去信心开始无视红灯。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：测试金字塔真正的价值不在&amp;quot;比例&amp;quot;，在它背后的经济学：&lt;strong&gt;反馈越快、定位越准的测试越便宜，应该最多&lt;/strong&gt;。所有&amp;quot;冰淇淋筒&amp;quot;团队的问题，本质都是把测试成本结构搞反了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五增量式发布把风险一刀一刀切小&#34;&gt;五、增量式发布：把风险一刀一刀切小&lt;/h2&gt;
&lt;p&gt;这部分是全书我最服气的一章——它回答了一个实际问题：大功能开发要好几个月，总不能这几个月不发版吧？书里给的答案是&lt;strong&gt;把&amp;quot;部署&amp;quot;和&amp;quot;发布&amp;quot;解耦&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;51-主干开发--特性开关和长寿命分支说再见&#34;&gt;5.1 主干开发 + 特性开关：和长寿命分支说再见&lt;/h3&gt;
&lt;p&gt;业界常见的做法是&amp;quot;功能分支&amp;quot;：每个大功能拉一个分支，开发几个月再合并。书里的态度很明确：&lt;strong&gt;长寿命分支是持续交付的头号敌人&lt;/strong&gt;。分支活得越久，合并那天的集成爆炸越大——你只是把集成风险攒起来，并没有消除它。&lt;/p&gt;
&lt;p&gt;替代方案是&lt;strong&gt;主干开发（trunk-based development）&lt;/strong&gt;：所有人都频繁提交到主干（至少每天），没做完的大功能用**特性开关（feature toggle）**藏起来——代码已经在生产环境，但功能入口被开关挡住，用户看不见。&lt;/p&gt;
&lt;p&gt;这样做的好处是双重的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集成每天都在发生，冲突永远是小的；&lt;/li&gt;
&lt;li&gt;&amp;ldquo;部署新代码&amp;quot;和&amp;quot;向用户开放功能&amp;quot;变成了两件独立的事。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;代价也要认账：开关本身是技术债，上线后要记得清理；半成品代码要保证&amp;quot;编译得过、不破坏现有行为&amp;rdquo;，对代码组织（抽象分支、按业务边界拆分）有要求。&lt;/p&gt;
&lt;h3 id=&#34;52-蓝绿部署与金丝雀发布&#34;&gt;5.2 蓝绿部署与金丝雀发布&lt;/h3&gt;
&lt;p&gt;发布阶段的两个经典模式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;蓝绿部署（blue-green deployment）&lt;/strong&gt;：维护两套生产环境。当前流量在&amp;quot;蓝&amp;quot;，新版本部署到&amp;quot;绿&amp;quot;，验证没问题就把流量切过去。出问题？切回来就是秒级回滚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;金丝雀发布（canary release）&lt;/strong&gt;：先把新版本暴露给一小部分流量（比如 1% 的用户或一台服务器），盯着错误率和关键指标，没问题再逐步扩大范围。名字来自煤矿里的金丝雀——鸟先下井，出事人有时间撤。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两者的共同点：&lt;strong&gt;回滚能力内建在发布机制里&lt;/strong&gt;，而不是出事后临时想办法。书里有句话大意是：发布计划里最重要的一条是回滚计划，而回滚计划必须&lt;strong&gt;真的演练过&lt;/strong&gt;才算数——没演练过的回滚方案约等于没有。&lt;/p&gt;
&lt;h3 id=&#34;53-数据库变更expand-contract-模式&#34;&gt;5.3 数据库变更：expand-contract 模式&lt;/h3&gt;
&lt;p&gt;蓝绿部署最怕的拦路虎是数据库：两套环境共用一个库，schema 一变，新旧代码没法同时跑。书里的解法是&lt;strong&gt;expand-contract（扩张-收缩）模式&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Expand&lt;/strong&gt;：先做一次只做&amp;quot;加&amp;quot;的变更——加新列、加新表，旧的什么都不动。这次变更对旧代码完全兼容，可以安全部署；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迁移&lt;/strong&gt;：应用代码改为同时读写新旧结构（或双写），后台任务把存量数据搬过去；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contract&lt;/strong&gt;：等所有在用版本都不依赖旧结构了，再单独做一次变更删掉旧列、旧表。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;核心思想一句话：&lt;strong&gt;每一次变更都向前向后兼容，让 schema 演进和应用部署可以分开走&lt;/strong&gt;。这也解释了为什么&amp;quot;上线一个大变更顺便重构一下表结构&amp;quot;是事故高发操作——你把两个本可以解耦的风险捆在了一起。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六配置与环境把环境也当代码管&#34;&gt;六、配置与环境：把环境也当代码管&lt;/h2&gt;
&lt;p&gt;这本书反复强调的一个纪律是：&lt;strong&gt;一切进版本控制&lt;/strong&gt;——不只是源代码，还包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建脚本、部署脚本；&lt;/li&gt;
&lt;li&gt;应用配置（环境相关的那部分参数化）；&lt;/li&gt;
&lt;li&gt;数据库迁移脚本；&lt;/li&gt;
&lt;li&gt;环境本身的定义（后来演化成了 Infrastructure as Code，IaC）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理由很朴素：如果环境的搭建靠&amp;quot;运维同学的记忆和一台手工调出来的服务器&amp;quot;，那你的测试环境和生产环境一定存在没人知道的差异，而每个差异都是一颗雷。书里对这种手工维护的服务器有个后来的流行叫法——&lt;strong&gt;宠物（pets） vs 牲畜（cattle）&lt;/strong&gt;：宠物有名字，病了要治；牲畜只有编号，病了换掉重来。环境应该朝&amp;quot;牲畜&amp;quot;方向走：不可变、可重建、随时销毁再拉起。&lt;/p&gt;
&lt;p&gt;配置管理上的一条实用原则：&lt;strong&gt;把&amp;quot;什么变&amp;quot;和&amp;quot;什么不变&amp;quot;分开&lt;/strong&gt;。代码和各环境共用的配置进版本库，环境差异（连接串、密钥、开关值）由部署流程注入。同一份代码 + 不同的配置 = 不同的环境，而不是各环境维护不同的代码分支。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七发布是业务决策不是技术决策&#34;&gt;七、发布是业务决策，不是技术决策&lt;/h2&gt;
&lt;p&gt;这是全书观点中我认为最容易被低估、也最改变组织行为的一条。&lt;/p&gt;
&lt;p&gt;传统模式下，&amp;ldquo;什么时候发布&amp;quot;是技术团队说了算的——因为只有他们觉得&amp;quot;差不多了&amp;quot;才敢发，发布本身是高危操作，自然由承担风险的人拍板。&lt;/p&gt;
&lt;p&gt;持续交付把这件事倒过来：当流水线保证了&lt;strong&gt;每个提交技术上都可发布&lt;/strong&gt;之后，&amp;ldquo;何时发布、对谁发布&amp;quot;就纯粹变成了业务问题——市场时机、合规窗口、用户沟通节奏，都应该由产品和业务来定。技术团队的职责是保证这个按钮&lt;strong&gt;随时可以按、按了不出事、出了事能秒回&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这也是特性开关、金丝雀发布的深层价值：它们把&amp;quot;发布&amp;quot;从技术动作变成了业务杠杆——按客户群分批开放、给内部员工先试用、关键客户定制节奏，都是业务手段而不是工程手段。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：很多团队的技术债，根子其实是&amp;quot;发布权力错配&amp;rdquo;：业务想要的速度给不了，因为发布在技术上做不到随时可做。先把工程能力补到位，权责自然归位。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八案例正面与反面&#34;&gt;八、案例：正面与反面&lt;/h2&gt;
&lt;h3 id=&#34;正面高频部署的先行者&#34;&gt;正面：高频部署的先行者&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Flickr&lt;/strong&gt;：2009 年 Velocity 大会上，Flickr 的 John Allspaw 和 Paul Hammond 做了那场著名演讲《10+ Deploys Per Day: Dev and Ops Cooperation at Flickr》——每天 10 次以上部署。这个演讲被公认为 DevOps 运动的引爆点之一。他们的经验核心正是本书的思想：小批量、特性开关、完善的监控和一键回滚，让高频发布不是冒险而是常态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Amazon&lt;/strong&gt;：以部署频率著称。后来 Amazon 高管在公开分享中提到过，2011 年前后 Amazon 平均每隔十几秒就有一处变更部署到生产（这个数字随年份增长，到 2014 年的分享里已经是秒级）。支撑它的是高度自动化的流水线和&amp;quot;谁构建谁运行&amp;quot;的服务负责制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些公司证明了一件事：&lt;strong&gt;部署频率和生产稳定性不是此消彼长，而是可以同向增长的&lt;/strong&gt;——前提是自动化、小批量、快速回滚三件套齐全。后来 DORA 的年度行业报告用数据反复验证了这一点：部署频率最高的那批组织，变更失败率反而最低、恢复最快。&lt;/p&gt;
&lt;h3 id=&#34;反面knight-capital45-分钟-44-亿美元&#34;&gt;反面：Knight Capital，45 分钟 4.4 亿美元&lt;/h3&gt;
&lt;p&gt;2012 年 8 月 1 日，美国做市商 Knight Capital 发生了一起教科书级的部署事故，大约 45 分钟内亏损约 4.4 亿美元，公司险些倒闭，最终被收购。&lt;/p&gt;
&lt;p&gt;事后的公开报道和美国 SEC 的调查文件还原了链条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;他们为新的&amp;quot;零售流动性计划&amp;rdquo;（Retail Liquidity Program）功能做部署，生产环境共 &lt;strong&gt;8 台服务器，人工部署只更新了 7 台&lt;/strong&gt;，漏了一台；&lt;/li&gt;
&lt;li&gt;新代码&lt;strong&gt;复用了一个旧的标志位（flag）&lt;/strong&gt;，而这个标志位多年前曾用于一个早已弃用的功能&amp;quot;Power Peg&amp;quot;——沉睡的旧代码没有被删除，只是被开关挡着；&lt;/li&gt;
&lt;li&gt;那台漏更新的服务器上，旧标志位被请求触发，&lt;strong&gt;沉睡多年的 Power Peg 代码被唤醒&lt;/strong&gt;，开始以错误逻辑疯狂下单；&lt;/li&gt;
&lt;li&gt;现场&lt;strong&gt;没有一键回滚机制&lt;/strong&gt;，工程师手忙脚乱，花了 40 多分钟才止住。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用本书的概念框架看，这个事故几乎踩中了所有反面：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;部署靠人工&lt;/strong&gt;——8 台漏 1 台是人工部署的必然概率问题，不是偶发失误；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;死代码 + 标志位复用&lt;/strong&gt;——特性开关没被治理，旧开关复用、死代码不清理，开关从保护伞变成了定时炸弹；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有快速回滚&lt;/strong&gt;——如果有一键回滚，损失可能是零头。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;感悟&lt;/strong&gt;：这个案例我逢人就讲，因为它完美诠释了本书的中心论点：&lt;strong&gt;事故的根源不是&amp;quot;发布太多&amp;quot;，而是发布过程不可重复、不可回滚&lt;/strong&gt;。Knight Capital 一年也发不了几次版，&amp;ldquo;少发布&amp;quot;没有保护他们。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九思考与感悟&#34;&gt;九、思考与感悟&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;持续&amp;quot;是方法，&amp;ldquo;交付信心&amp;quot;才是目的。&lt;/strong&gt; 读完回头看，这本书所有技术手段——流水线、金字塔、特性开关、蓝绿——都服务于同一件事：让团队在任何时候都能有底气地说&amp;quot;现在的版本是可发布的&amp;rdquo;。这种底气本身会改变团队的开发方式：敢重构、敢提交、敢试验。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;纪律比工具难买。&lt;/strong&gt; 流水线的搭建一个 Sprint 就能做完，难的是&amp;quot;红了就停下手头一切去修&amp;quot;这条纪律。工具解决的是&amp;quot;能不能&amp;rdquo;，纪律解决的是&amp;quot;做不做&amp;rdquo;。后者只能靠团队共识和管理者背书。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;和客户开发方法论同构。&lt;/strong&gt; 这是我读完联想最多的一点：之前读的创业方法论里，客户开发（Customer Development）讲的是市场侧的小步试错——别闭门造车一年再赌一把大的，把假设拆小，快速放到真实客户面前验证。持续交付是&lt;strong&gt;工程侧的同一件事&lt;/strong&gt;：别把代码攒三个月再赌一次大发布，把变更拆小，快速推到真实环境里验证。一个在验证&amp;quot;需求假设&amp;quot;，一个在验证&amp;quot;代码正确性假设&amp;quot;，底层的认识论完全一致：&lt;strong&gt;小批量、快反馈、可回退，用高频试错替代单次豪赌&lt;/strong&gt;。精益创业（Lean Startup）把这两条线拧在了一起，不是巧合——Eric Ries 本人就是持续部署的早期实践者。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;每步可退，才能每步敢进。&lt;/strong&gt; &amp;ldquo;可回滚&amp;quot;听起来是防御性能力，实际上是进攻性能力——只有确信每一步都退得回去，团队才敢把步子迈快。这本书最深的洞察也许就这一句：&lt;strong&gt;速度和安全不是天平的两端，可回滚性把它们焊在了同一侧。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;本篇小结&#34;&gt;本篇小结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;部署流水线是核心骨架：提交 → 构建+单元测试 → 自动化验收 → 手工验证/UAT → 发布，每个阶段是质量关卡，失败即止，修复红灯优先于新功能；&lt;/li&gt;
&lt;li&gt;哲学上是 &amp;ldquo;If it hurts, do it more often&amp;rdquo;：痛苦的事要做小、做勤，风险切碎摊平，而不是攒起来赌一次大的；&lt;/li&gt;
&lt;li&gt;每次提交产出可发布候选；同一构建产物逐级晋升，环境差异靠外部配置注入；&lt;/li&gt;
&lt;li&gt;测试要金字塔不要冰淇淋筒：单元测试打底，验收测试讲业务语言，UI 测试最少；&lt;/li&gt;
&lt;li&gt;主干开发 + 特性开关替代长寿命分支，把&amp;quot;部署&amp;quot;和&amp;quot;发布&amp;quot;解耦；&lt;/li&gt;
&lt;li&gt;蓝绿部署和金丝雀发布把回滚能力内建在发布机制里；数据库变更走 expand-contract，每步向前向后兼容；&lt;/li&gt;
&lt;li&gt;配置和环境定义全部进版本控制，环境朝&amp;quot;可重建的牲畜&amp;quot;方向走；&lt;/li&gt;
&lt;li&gt;发布是业务决策，技术团队的职责是让那个按钮随时可按、按了能回；&lt;/li&gt;
&lt;li&gt;收尾的同构关系：客户开发是市场侧的小步试错，持续交付是工程侧的小步试错，精益创业把两者拧成一股。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>《凤凰项目》读书笔记：用一本小说搞懂 DevOps 的三步工作法</title>
      <link>https://ftmao.com/blog/bookstore/phoenix-project-book-notes/</link>
      <pubDate>Tue, 08 Aug 2023 10:40:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/phoenix-project-book-notes/</guid>
      <description>&lt;h1 id=&#34;凤凰项目读书笔记用一本小说搞懂-devops-的三步工作法&#34;&gt;《凤凰项目》读书笔记：用一本小说搞懂 DevOps 的三步工作法&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;目标读者：写代码的、搞运维的、带团队的，尤其是觉得&amp;quot;我们部门天天救火、需求永远延期、发布必出事故&amp;quot;的人。&lt;br&gt;
参考书籍：Gene Kim, Kevin Behr, George Spafford《凤凰项目：一个 IT 运维的传奇故事》（The Phoenix Project）&lt;br&gt;
阅读目标：不用啃方法论大部头，通过一个故事建立 DevOps 的完整直觉——为什么 IT 问题本质上是&amp;quot;工厂管理问题&amp;quot;，以及三步工作法到底怎么用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面为什么是小说体的-devops-入门&#34;&gt;0. 写在前面：为什么是&amp;quot;小说体&amp;quot;的 DevOps 入门&lt;/h2&gt;
&lt;p&gt;市面上讲 DevOps 的书大多是清单式的：CI/CD、IaC、监控、文化……知识点都对，但读完你还是不知道&lt;strong&gt;从哪一步开始改&lt;/strong&gt;，更不知道怎么跟老板解释&amp;quot;为什么要改&amp;quot;。&lt;/p&gt;
&lt;p&gt;《凤凰项目》的作者显然也意识到了这一点，所以他们写了一本小说。这本书的思路来自一个更老的先例：高德拉特（Eliyahu Goldratt）1984 年的《目标》（The Goal）——一本用工厂经理拯救濒临倒闭工厂的故事来讲&lt;strong&gt;约束理论&lt;/strong&gt;（TOC, Theory of Constraints）的小说。《凤凰项目》就是把《目标》的工厂换成了一家公司的 IT 部门，把生产线换成了&amp;quot;需求 → 开发 → 测试 → 部署 → 运维&amp;quot;这条价值流。&lt;/p&gt;
&lt;p&gt;一句话理解全书：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IT 部门就是一座工厂，代码从流水线的这头进去、从部署那头出来。工厂的毛病 IT 全有：在制品堆积、瓶颈工位、紧急插单、返工报废。所以工厂管理那套成熟的方法（约束理论、精益生产、丰田生产方式），搬到 IT 里就是 DevOps。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下面我们按&amp;quot;剧情主线 → 核心概念 → 现实对照&amp;quot;的顺序展开。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-剧情主线一个烂摊子和一个临危受命的运维-vp&#34;&gt;1. 剧情主线：一个烂摊子和一个临危受命的运维 VP&lt;/h2&gt;
&lt;p&gt;故事发生在一家虚构的汽车零部件制造公司 &lt;strong&gt;Parts Unlimited（零件公司）&lt;/strong&gt;。公司正在被竞争对手用互联网打法吊打，董事会给管理层下了死命令：两年内把公司救回来，否则 IT 部门整体外包。&lt;/p&gt;
&lt;p&gt;救亡图存的希望寄托在一个叫 &lt;strong&gt;&amp;ldquo;凤凰项目&amp;rdquo;（Project Phoenix）&lt;/strong&gt; 的大项目上——一个面向电商化的核心系统改造，号称要让公司重新获得和对手正面竞争的能力。问题是：这个项目已经严重超支、延期数年，功能越堆越多，离上线遥遥无期。&lt;/p&gt;
&lt;p&gt;开篇第一天，主角 &lt;strong&gt;Bill Palmer（比尔·帕尔默）&lt;/strong&gt; 还是个小运维经理，正在处理公司工资系统（Payroll）宕机的大事故——全公司发不出工资。就在这一天，两位高管（CIO 和运维 VP）先后被炒，CEO &lt;strong&gt;Steve Masters&lt;/strong&gt; 直接把 Bill 拎上来：从今天起，你就是 IT 运维副总裁。&lt;/p&gt;
&lt;p&gt;Bill 接手的局面堪称教科书级的灾难现场：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;凤凰项目&lt;/strong&gt;像吞金兽，吃掉全公司最优秀的工程师，却连一次完整的测试部署都做不起来；&lt;/li&gt;
&lt;li&gt;业务部门绕过 IT 自己买 SaaS、插紧急需求，IT 完全不知道有多少工作在流动；&lt;/li&gt;
&lt;li&gt;所有关键知识都集中在一个叫 &lt;strong&gt;Brent&lt;/strong&gt; 的超级工程师脑子里，任何系统的任何故障最后都得找他；&lt;/li&gt;
&lt;li&gt;每次发布都是通宵加班 + 回滚 + 更多通宵加班的恶性循环；&lt;/li&gt;
&lt;li&gt;安全审计部天天发整改单，没人理，直到差点被监管和董事会抓住把柄。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;书里还有一个关键人物：&lt;strong&gt;Erik&lt;/strong&gt;，一个董事会的准董事候选人，平时蹲在离公司总部最近的一个制造工厂里，看起来像退休技术宅，实际上是整个故事的&amp;quot;导师&amp;quot;角色。他每次出场都把 Bill 拉到工厂里参观，讲一些看似答非所问的工厂管理道理——&lt;strong&gt;Erik 就是高德拉特在书里的化身&lt;/strong&gt;，他说的每句话都是约束理论。&lt;/p&gt;
&lt;p&gt;剧情的高潮和转折大致是：一次为了赶董事会死线的&lt;strong&gt;灾难性发布&lt;/strong&gt;（凤凰被强行上线，引发支付数据泄露级别的混乱）之后，Steve 祭出狠招——&lt;strong&gt;冻结一切发布&lt;/strong&gt;。在被迫停下来的这段时间里，Bill 团队才真正看清自己的工厂长什么样：堆积如山的在制品、四处流动的紧急变更、以及那个被所有人依赖的 Brent。于是他们开始一步步疏解瓶颈、限制在制品、建立反馈回路，最后孵化出代号 &lt;strong&gt;&amp;ldquo;独角兽&amp;rdquo;（Unicorn）&lt;/strong&gt; 的独立小团队，用持续部署的方式交付电商功能，让公司起死回生。&lt;/p&gt;
&lt;p&gt;Bill 本人在故事结尾升任 CIO——这个安排也是作者的态度：&lt;strong&gt;DevOps 不是某个团队的局部优化，而是 IT 一把手工程。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;主要人物和概念可以对照着记：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;mindmap
  root((&amp;#34;凤凰项目&amp;#34;))
    人物
      Bill Palmer：运维 VP→CIO（主角视角）
      Steve Masters：CEO，冻结发布的拍板人
      Erik：导师角色，Goldratt 的化身
      Brent：超级工程师，全公司的瓶颈
      Wes/Patty：运维总监/变更管理负责人
    核心概念
      四种工作类型
      三步工作法
      约束理论 TOC
      精益与丰田生产方式
    关键事件
      工资系统宕机（开篇）
      凤凰灾难性发布（谷底）
      冻结发布（转折）
      独角兽项目（翻身）&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;11-根因开发与运维的对立目标&#34;&gt;1.1 根因：开发与运维的&amp;quot;对立目标&amp;quot;&lt;/h3&gt;
&lt;p&gt;书里把 IT 一切混乱的总根源归结为一个结构性矛盾，值得单独拎出来：&lt;strong&gt;开发和运维被两套相反的目标函数考核。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开发的 KPI 是&amp;quot;快&amp;quot;：更多功能、更快上线、响应业务变化；&lt;/li&gt;
&lt;li&gt;运维的 KPI 是&amp;quot;稳&amp;quot;：可用性、不出事故、别动我的生产环境。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是开发把代码&amp;quot;扔过墙&amp;quot;就算交差，运维为了自保层层设卡（审批、冻结窗口、几十页的变更单），结果是部署越来越难、间隔越来越长、每次部署的批量越来越大——&lt;strong&gt;批量越大风险越高，风险越高越想少部署&lt;/strong&gt;，形成一个不断恶化的&amp;quot;向下的螺旋&amp;quot;。书里的凤凰项目几年憋一发，就是这条螺旋的终点。三步工作法的本质，就是把两套目标函数统一成&amp;quot;&lt;strong&gt;安全地、频繁地把价值送到用户手里&lt;/strong&gt;&amp;ldquo;这一件事——开发对生产负责，运维为交付提速。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-四种工作类型先看清你的工厂里在流什么&#34;&gt;2. 四种工作类型：先看清你的工厂里在流什么&lt;/h2&gt;
&lt;p&gt;Erik 给 Bill 上的第一课是：&lt;strong&gt;在解决任何问题之前，先搞清楚工厂里到底有几种工作在流动。&lt;/strong&gt; IT 里的工作只有四种：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;业务项目（Business Projects）&lt;/strong&gt;&lt;br&gt;
业务部门要的功能：凤凰项目、新的促销系统、给销售做的报表。这类工作有明确的外部客户，是&amp;quot;公司存在的原因&amp;rdquo;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;IT 内部项目（IT Internal Projects）&lt;/strong&gt;&lt;br&gt;
IT 自己发起的工作：服务器升级、数据库迁移、安全补丁、技术债偿还。它们不产生直接收入，但没有它们，业务项目迟早死在烂地基上。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;变更（Changes）&lt;/strong&gt;&lt;br&gt;
上面两类项目落地时产生的具体操作：改配置、发版本、加字段。书里的关键洞察是——&lt;strong&gt;大多数事故由变更引起，而变更的流程往往既没人看见、也没人管理&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;计划外工作（Unplanned Work）&lt;/strong&gt;&lt;br&gt;
救火、故障恢复、临时支援、&amp;ldquo;帮我看一眼这个报错&amp;rdquo;。它的特点是：&lt;strong&gt;不请自来、优先级永远最高、而且会把前三类工作的时间偷走。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;一句话理解：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;计划外工作是四种工作里唯一的&amp;quot;负资产&amp;quot;——它不光消耗资源，还打断所有正在进行的工作，制造新的错误，从而制造更多计划外工作。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;书里有个很扎心的细节：Bill 让团队统计自己的工作去向，发现几乎没有人在做&amp;quot;计划内的事&amp;quot;——每个人都在被随机的紧急请求打断。这在工厂里叫&amp;quot;紧急插单&amp;quot;，在 IT 里我们叫&amp;quot;@所有人 快帮忙看下&amp;quot;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    subgraph 流入
        A[&amp;#34;业务项目&amp;lt;br/&amp;gt;（赚钱的功能）&amp;#34;] --&amp;gt; WIP
        B[&amp;#34;IT 内部项目&amp;lt;br/&amp;gt;（打地基）&amp;#34;] --&amp;gt; WIP
        C[&amp;#34;变更&amp;lt;br/&amp;gt;（落地动作）&amp;#34;] --&amp;gt; WIP
        D[&amp;#34;计划外工作&amp;lt;br/&amp;gt;（救火·打断一切）&amp;#34;] -.-&amp;gt;|&amp;#34;插队抢占&amp;#34;| WIP
    end

    WIP[&amp;#34;在制品队列&amp;lt;br/&amp;gt;WIP 越堆越高&amp;lt;br/&amp;gt;周期时间越来越长&amp;#34;]

    WIP --&amp;gt; E[&amp;#34;部署上线&amp;lt;br/&amp;gt;产生业务价值&amp;#34;]
    WIP -.-&amp;gt;|&amp;#34;缺陷与事故&amp;#34;| D

    style D fill:#ffcccc
    style WIP fill:#fff3cd&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;注意图里那条反向虚线：&lt;strong&gt;计划外工作不但插队，还会生出新的事故&lt;/strong&gt;——急匆匆的救火往往是又一个 bug 的种子。这就是&amp;quot;越忙越乱&amp;quot;的数学本质。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;小数字例子&lt;/strong&gt;：假设一个工程师每天 8 小时，原本排满 2 个任务。每被插入 1 次救火，打断成本大约是&amp;quot;上下文切换 + 排查 + 修复 + 切回来&amp;quot;——书里借 Erik 之口引用了一个经典结论：一个人同时挂在手上的事越多，每件事完成的周期时间就按非线性膨胀。这不是态度问题，是排队论（利特尔法则：周期时间 = 在制品数量 ÷ 吞吐率）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-约束理论找到你的-brent&#34;&gt;3. 约束理论：找到你的 Brent&lt;/h2&gt;
&lt;h3 id=&#34;31-一句话理解-toc&#34;&gt;3.1 一句话理解 TOC&lt;/h3&gt;
&lt;p&gt;约束理论（TOC）来自高德拉特的《目标》，核心一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;任何系统的整体产能，只由系统中最慢的那个环节（瓶颈/约束）决定。优化瓶颈之外的任何环节，都是在浪费钱，甚至帮倒忙。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;工厂例子很直观：一条流水线 5 个工位，第 3 个工位每小时只能加工 10 个零件，其余工位都能做 50 个。那你给工位 1 提速到每小时 100 个有意义吗？没有——零件只会在工位 3 前面堆成山，&lt;strong&gt;在制品增加，但出货一件都不会多&lt;/strong&gt;。工厂里的真金白银只发生在&amp;quot;零件卖出厂门&amp;quot;那一刻；IT 里对应的就是&amp;quot;功能部署到生产环境并被用户使用&amp;quot;那一刻。没上线的代码不是资产，是库存，而且是会腐烂的库存（需求变了、上下文忘了、合并冲突了）。&lt;/p&gt;
&lt;h3 id=&#34;32-brent那个什么都懂的人就是瓶颈&#34;&gt;3.2 Brent：那个什么都懂的人，就是瓶颈&lt;/h3&gt;
&lt;p&gt;《凤凰项目》里最精妙的设计就是 Brent 这个角色。Brent 是 Parts Unlimited 的传奇工程师：他知道每个系统埋在哪个角落、每条配置为什么长那样、每次故障的历史前因。于是公司里形成了一个铁律——&lt;strong&gt;重要的事情都得 Brent 参与&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;凤凰项目要部署？只有 Brent 懂环境配置，找他。&lt;/li&gt;
&lt;li&gt;工资系统挂了？只有 Brent 知道那段祖传代码，找他。&lt;/li&gt;
&lt;li&gt;网络要改造？只有 Brent 记得十年前为什么这么布线，找他。&lt;/li&gt;
&lt;li&gt;甚至业务部门自己想买软件，也要&amp;quot;Brent 看一下能不能集成&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结果 Brent 的工作队列永远排在几个月之后，而&lt;strong&gt;所有关键路径都从 Brent 的工位前经过&lt;/strong&gt;——他就是整条流水线的瓶颈工位。更糟的是，因为 Brent 太能干，大家救火也找他，计划外工作进一步抢占他的时间，瓶颈被救火放大。&lt;/p&gt;
&lt;p&gt;识别瓶颈的方法书里也给了，非常朴素：&lt;strong&gt;看谁面前的队列最长，看谁的名字出现在所有任务的依赖列表里，看谁永远约不到会议时间。&lt;/strong&gt; 工厂里的瓶颈工位有个特征——它前面永远堆着最多的待加工零件。Brent 前面堆的，是全公司的工单。&lt;/p&gt;
&lt;h3 id=&#34;33-疏解瓶颈toc-五步法&#34;&gt;3.3 疏解瓶颈：TOC 五步法&lt;/h3&gt;
&lt;p&gt;约束理论的改进套路是五个步骤，书里完整走了一遍：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;识别约束&lt;/strong&gt;：确认 Brent（以及少数关键岗位）是系统瓶颈，而不是去怪&amp;quot;团队不努力&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;挖尽约束&lt;/strong&gt;：瓶颈的每一分钟都不能浪费。让 Brent 的时间只花在&amp;quot;只有 Brent 能做的事&amp;quot;上——别再让他回工单、开无关的会、修不需要他的机器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迁就约束&lt;/strong&gt;：整个系统的节奏向瓶颈看齐。在 Brent 消化完之前，不许再往上堆新工作（这在工厂里叫&amp;quot;绳子&amp;quot;机制，在 IT 里就是 WIP 限制）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;打破约束&lt;/strong&gt;：给约束扩容。书里的做法是让 Brent 的工作&lt;strong&gt;文档化、标准化&lt;/strong&gt;：每次 Brent 修东西，旁边必须有第二个人观摩记录，把他的隐性知识变成显性 runbook，逐步培养出&amp;quot;第二个、第三个 Brent&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;回到第一步&lt;/strong&gt;：约束疏解后，瓶颈会转移到别处（比如自动化测试、比如部署流程），继续循环。&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 疏解前
        direction TB
        A1[&amp;#34;所有需求&amp;#34;] --&amp;gt; B1[&amp;#34;Brent&amp;lt;br/&amp;gt;队列无限长&amp;lt;br/&amp;gt;周期时间失控&amp;#34;]
        B1 --&amp;gt; C1[&amp;#34;上线&amp;#34;]
        D1[&amp;#34;救火请求&amp;#34;] -.-&amp;gt;|&amp;#34;随时打断&amp;#34;| B1
    end

    subgraph 疏解后
        direction TB
        A2[&amp;#34;需求按优先级进入&amp;#34;] --&amp;gt; W2[&amp;#34;WIP 限制&amp;lt;br/&amp;gt;排队可见&amp;#34;]
        W2 --&amp;gt; B2[&amp;#34;Brent 只做&amp;lt;br/&amp;gt;只有他能做的事&amp;#34;]
        W2 --&amp;gt; B3[&amp;#34;文档化 + 带教&amp;lt;br/&amp;gt;复制出更多 Brent&amp;#34;]
        B2 --&amp;gt; C2[&amp;#34;上线&amp;#34;]
        B3 --&amp;gt; C2
        D2[&amp;#34;救火请求&amp;#34;] --&amp;gt; W2
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这一条是全书对工程管理最有杀伤力的观点，值得单独抄一遍：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&amp;ldquo;把最强的工程师从所有救火中撤出来&amp;quot;听起来反直觉，但恰恰是让系统恢复流动的唯一办法。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现实映射也很常见：几乎每个团队都有一个&amp;quot;Brent&amp;rdquo;——核心系统的唯一懂行人、所有 MR 的指定 reviewer、线上问题的最终兜底人。这个角色的存在本身就是风险（业界叫 bus factor = 1），而管理他的正确姿势不是&amp;quot;多招几个一样强的人&amp;quot;（招不到），而是&lt;strong&gt;把他的知识变成流程和文档，让他只干真正不可复制的那部分&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-三步工作法devops-的全部内容&#34;&gt;4. 三步工作法：DevOps 的全部内容&lt;/h2&gt;
&lt;p&gt;三步工作法（The Three Ways）是《凤凰项目》的骨架，也是后来《DevOps Handbook》的理论内核。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph S1[&amp;#34;第一步：流动原则（从左到右）&amp;#34;]
        direction LR
        Dev[&amp;#34;开发&amp;#34;] --&amp;gt; Test[&amp;#34;测试&amp;#34;] --&amp;gt; Ops[&amp;#34;运维/部署&amp;#34;] --&amp;gt; Cust[&amp;#34;客户价值&amp;#34;]
    end

    subgraph S2[&amp;#34;第二步：反馈原则（从右到左）&amp;#34;]
        direction RL
        Ops2[&amp;#34;运维/生产&amp;#34;] --&amp;gt;|监控/告警/数据| Test2[&amp;#34;测试&amp;#34;]
        Test2 --&amp;gt;|质量信号| Dev2[&amp;#34;开发&amp;#34;]
    end

    subgraph S3[&amp;#34;第三步：持续学习与实验&amp;#34;]
        L[&amp;#34;刻意练习&amp;lt;br/&amp;gt;复盘文化&amp;lt;br/&amp;gt;为改进留时间&amp;#34;]
    end

    S1 &amp;lt;--&amp;gt;|快速、可见、小批量| S2
    S2 --&amp;gt;|知识沉淀为实验| S3&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;41-第一步流动原则flow让工作从左到右永远不要停不要回流&#34;&gt;4.1 第一步：流动原则（Flow）——让工作从左到右，永远不要停、不要回流&lt;/h3&gt;
&lt;p&gt;目标是&lt;strong&gt;缩短从&amp;quot;需求提出&amp;quot;到&amp;quot;价值交付&amp;quot;的周期时间&lt;/strong&gt;。要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;让工作可见&lt;/strong&gt;：书里第一个改革动作就是建看板（kanban）——把所有正在进行的工作贴在墙上/板上，任何插队需求必须先&amp;quot;有卡&amp;quot;再讨论。看不见的东西无法管理，&amp;ldquo;我才知道你手上还有这个&amp;quot;是大部分混乱的根源。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;限制在制品（WIP）&lt;/strong&gt;：同时开工的事越少，完成得越快。这是利特尔法则的直接推论，也是最容易被管理者忽视的一条——&amp;ldquo;开始新工作&amp;quot;有快感，&amp;ldquo;完成工作&amp;quot;才产生价值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;小批量&lt;/strong&gt;：大项目（凤凰那种几年憋一发的）是大批量生产的 IT 版，风险随批量指数膨胀。拆小、频繁交付，风险才能线性化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;减少交接&lt;/strong&gt;：每一次&amp;quot;扔过墙&amp;rdquo;（开发扔给测试、测试扔给运维）都是信息损失和等待。书里凤凰项目最痛的环节就是开发环境、测试环境、生产环境三套配置永远对不上，每次交接都从头踩坑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;识别并疏解约束&lt;/strong&gt;：即上一节的 TOC 五步法，持续盯住系统里最慢的环节。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;42-第二步反馈原则feedback从右到左越早发现越便宜&#34;&gt;4.2 第二步：反馈原则（Feedback）——从右到左，越早发现、越便宜&lt;/h3&gt;
&lt;p&gt;目标是&lt;strong&gt;让质量信号在生产源头就被制造出来，而不是在用户投诉时才收到&lt;/strong&gt;。要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;把测试和安全&amp;quot;左移&amp;rdquo;&lt;/strong&gt;：书里 Wes（运维总监）和 Bill 的最大冲突之一，就是安全审计的整改单永远是事后几百页地砸下来。改革方向是让安全要求变成自动化检查，在提交代码的那一刻就反馈，而不是发布前一周开会吵架。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;在生产中看见问题&lt;/strong&gt;：监控、日志、告警是&amp;quot;工厂的质检摄像头&amp;rdquo;。书里 IT 的至暗时刻是&amp;quot;用户先发现支付出错了，我们才知道&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;出问题就停线&lt;/strong&gt;：这条直接来自丰田的&lt;strong&gt;安灯绳（Andon Cord）&lt;/strong&gt;——流水线上任何工人发现缺陷，可以拉绳停掉整条线，全组停下来先修问题。IT 的对应物是&amp;quot;构建红了就没人能提交新代码&amp;quot;、是&amp;quot;出事故先恢复再追责&amp;quot;、是书里后来建立的规则：部署失败自动回滚，而不是边生产边抢修。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享的遥测与同理心&lt;/strong&gt;：开发和运维看同一套指标。书里 Bill 和 Wes 关系的转折点，是运维开始理解开发的交付压力、开发开始理解运维的稳定性压力——&lt;strong&gt;目标函数统一了，反馈才有意义&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;43-第三步持续学习与实验continual-learning高风险低信任的反面是习惯成自然的日常&#34;&gt;4.3 第三步：持续学习与实验（Continual Learning）——高风险、低信任的反面是习惯成自然的日常&lt;/h3&gt;
&lt;p&gt;目标是&lt;strong&gt;让组织把&amp;quot;改进&amp;quot;本身当成日常工作的一部分&lt;/strong&gt;。要点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;为改进偿还技术债留预算&lt;/strong&gt;：书里 Bill 争取到的关键资源之一是&amp;quot;把一部分产能固定给内部项目&amp;quot;——否则地基永远没人修，直到塌掉。业界常见的对应做法是预留 20% 左右产能给工程改进（Google SRE 书里有类似的配额思想）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;让实验变便宜&lt;/strong&gt;：能快速试错的前提是&amp;quot;错一次代价小&amp;quot;——小批量、自动回滚、灰度发布、特性开关（feature flag），全是降低实验成本的工具。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;复盘文化&lt;/strong&gt;：事故复盘（postmortem）对事不对人。书里的反面教材是每次事故后管理层的第一反应是&amp;quot;谁干的&amp;quot;，于是人人都学会隐瞒，反馈回路彻底失效——这就是为什么第三步必须建在前两步之上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;刻意练习故障&lt;/strong&gt;：主动制造故障来训练系统韧性。这条思想后来由 Netflix 的 Chaos Monkey 发扬光大——在工作日随机关生产环境实例，逼所有服务具备自愈能力。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;44-变更管理从审批的墙到自动化的管道&#34;&gt;4.4 变更管理：从&amp;quot;审批的墙&amp;quot;到&amp;quot;自动化的管道&amp;quot;&lt;/h3&gt;
&lt;p&gt;书里 Patty（变更管理负责人）这条线是第二原则的最佳注脚。改革前，Parts Unlimited 的变更流程是典型的&lt;strong&gt;审批驱动&lt;/strong&gt;：一张变更单层层签字、CAB（变更咨询委员会）开会逐条过——看上去严谨，实际上有两个致命问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;审批者根本不了解变更内容&lt;/strong&gt;，签字只是分摊责任，出事了照样出事；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;流程太重，大家就绕开流程&lt;/strong&gt;——大量&amp;quot;紧急变更&amp;quot;和&amp;quot;标准变更&amp;quot;在暗处发生，比没有流程更危险。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;改革方向书里给得很明确：&lt;strong&gt;把人力审批换成自动验证&lt;/strong&gt;。变更信息从工单系统自动采集（谁改的、改了什么、影响哪个系统），风险低的标准变更直接自动放行，高风险的才人工介入；部署本身交给流水线，测试、安全扫描、回滚预案全部自动化。一句话：&lt;strong&gt;变更管理的目的是让正确的变更更快、让危险的变更更难，而不是让所有变更都更慢。&lt;/strong&gt; 这也是现实中 Google 等公司&amp;quot;用错误预算和 SLO 代替 CAB&amp;quot;的逻辑。&lt;/p&gt;
&lt;h3 id=&#34;45-与-sre-的关系&#34;&gt;4.5 与 SRE 的关系&lt;/h3&gt;
&lt;p&gt;如果你读过本博客的 SRE 笔记，会发现三步工作法和 SRE 是同源思想的两套表述：SRE 的&amp;quot;错误预算（error budget）&amp;ldquo;本质上是给第二步的反馈回路装了一个量化阀门——预算花光就冻结发布（这正是书里 Steve 的&amp;quot;冻结发布&amp;rdquo;）；SRE 的&amp;quot;消除琐事（toil）&amp;ldquo;就是限制计划外工作；SRE 的&amp;quot;用工程方法解决运维问题&amp;quot;就是第三步的持续学习。可以把《凤凰项目》理解为&lt;strong&gt;讲&amp;quot;为什么&amp;rdquo;&lt;/strong&gt;，《SRE 书》理解为&lt;strong&gt;讲&amp;quot;怎么做&amp;quot;&lt;/strong&gt;，先读前者再读后者，体感会好很多。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-devops-与精益丰田的渊源这套东西不是-it-发明的&#34;&gt;5. DevOps 与精益/丰田的渊源：这套东西不是 IT 发明的&lt;/h2&gt;
&lt;p&gt;《凤凰项目》反复提醒读者：DevOps 的每一个核心概念，在制造业都有原型，而且已经被验证了几十年。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;DevOps 概念&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;制造业原型&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;作用&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;看板（Kanban）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;丰田的看板卡&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;让工作可见、控制 WIP&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;持续集成/小批量&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;单件流（One-piece flow）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;降低批量风险、缩短周期&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;出故障就停构建&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;安灯绳（Andon Cord）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;质量内建于源头，不让缺陷流向下游&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;价值流映射&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;价值流图（Value Stream Mapping）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;找出等待与浪费藏在哪里&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;部署自动化&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;快速换模（SMED）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;把&amp;quot;切换/部署&amp;quot;从小时级压到分钟级&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;复盘与改善&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Kaizen（持续改善）&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;改进是日常工作，不是运动式整改&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;理解这层渊源有一个很实际的好处：&lt;strong&gt;当你需要向管理层解释 DevOps 时，不用发明新词&lt;/strong&gt;。&amp;ldquo;我们的发布流程就像一条有质检摄像头和停线机制的流水线&amp;quot;比&amp;quot;我们要做 CI/CD 文化建设&amp;quot;有说服力得多。书里 Erik 带 Bill 参观工厂、指着流水线讲 IT 的那些场景，就是在替读者做这层翻译。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-关键转折从冻结发布到独角兽&#34;&gt;6. 关键转折：从冻结发布到独角兽&lt;/h2&gt;
&lt;p&gt;书的中后段有几个决定性动作，连起来就是一条完整的&amp;quot;IT 翻身路径&amp;rdquo;，值得单独梳理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;灾难性发布&lt;/strong&gt;：凤凰项目被董事会死线强推上线，结果支付系统大面积故障、敏感数据风险暴露。这是全书的谷底——&lt;strong&gt;大批量 + 弱反馈 + 瓶颈过载同时引爆&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;冻结发布（change freeze）&lt;/strong&gt;：Steve Masters 顶着业务压力叫停一切变更。这个动作极其反直觉（&amp;ldquo;业务等不了！&amp;quot;），但它强制系统&amp;quot;停下来看清自己&amp;rdquo;。类比丰田：线停下来不是损失，&lt;strong&gt;带病运转才是损失&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;盘点在制品与约束&lt;/strong&gt;：冻结期间团队把全部工作摊到看板上，才发现&amp;quot;正在做的事&amp;quot;比大家以为的多一个数量级；同时正式确认 Brent 是核心约束。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;疏解 Brent&lt;/strong&gt;：Brent 的时间被保护起来，每次排障强制带教、强制文档化；不相干的请求被挡在队列外。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;孵化独角兽（Unicorn）&lt;/strong&gt;：把凤凰项目里最急迫的电商功能拆出来，成立一个小而全的独立团队，独立代码库、独立部署流水线，绕开历史包袱，&lt;strong&gt;从&amp;quot;几年一发布&amp;quot;做到&amp;quot;每天多次发布&amp;quot;&lt;/strong&gt;。独角兽不是否定凤凰，而是用实践证明新方式可行，再反向改造凤凰。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;胜利与晋升&lt;/strong&gt;：电商功能帮公司在旺季翻身，Bill 升任 CIO，Steve 升任 COO——DevOps 的胜利最终以&lt;strong&gt;组织形态&lt;/strong&gt;的胜利收场：开发、运维、安全不再是三个互相扔锅的部门，而是围绕价值流协作的一体。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这条路径里最容易被忽略的是第 2 步：&lt;strong&gt;停下来的勇气&lt;/strong&gt;。几乎所有改进失败都死在&amp;quot;边开车边换轮子&amp;quot;——系统过载时，任何改进动作都会变成新的在制品，进一步压低吞吐。先止血，再手术。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-现实对照三个真实事件&#34;&gt;7. 现实对照：三个真实事件&lt;/h2&gt;
&lt;p&gt;书中的情节是虚构的，但每一个都有现实的镜像。&lt;/p&gt;
&lt;h3 id=&#34;71-knight-capital45-分钟亏掉-44-亿美元2012反面教材&#34;&gt;7.1 Knight Capital：45 分钟亏掉 4.4 亿美元（2012，反面教材）&lt;/h3&gt;
&lt;p&gt;2012 年 8 月 1 日，美国高频交易公司 Knight Capital 上线新代码。部署过程需要手动在 8 台服务器上更新，结果&lt;strong&gt;只部署了 7 台&lt;/strong&gt;——第 8 台还留着旧代码，而新旧代码复用了同一个标志位，旧代码里一个废弃多年的测试功能（Power Peg）被意外激活。这台服务器开始以每分钟数百万美元的速度下错单，&lt;strong&gt;45 分钟内亏损约 4.4 亿美元&lt;/strong&gt;，公司股价单日崩盘，数月后被收购。&lt;/p&gt;
&lt;p&gt;对照《凤凰项目》，Knight Capital 几乎踩中了书里所有的雷：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;部署是&lt;strong&gt;手工的、不一致的&lt;/strong&gt;（8 台机器人工操作，漏一台没有任何人发现）——违反第一步的自动化与小批量；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;没有自动验证与回滚&lt;/strong&gt;（旧代码带着活弹药上线，出事只能靠人肉拔网线）——违反第二步的反馈；&lt;/li&gt;
&lt;li&gt;变更过程&lt;strong&gt;不可见、无审计&lt;/strong&gt;——变更管理形同虚设。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;书里凤凰的灾难性发布如果拍成纪录片，大概就是这个样子。&lt;/p&gt;
&lt;h3 id=&#34;72-etsy每天几十次部署的软工程正面教材&#34;&gt;7.2 Etsy：每天几十次部署的&amp;quot;软工程&amp;quot;（正面教材）&lt;/h3&gt;
&lt;p&gt;Etsy 是持续部署的早期标杆，其工程师在 2010 年前后公开的实践（后被《持续交付》等书引用）几乎就是&amp;quot;独角兽项目&amp;quot;的现实版：全站每天部署几十次，每次部署只是少量提交；用特性开关把&amp;quot;部署&amp;quot;和&amp;quot;发布&amp;quot;解耦；给发布工程师配一键部署工具和全量监控大屏，新人入职第一天就完成一次真实上线（用小改动建立&amp;quot;部署不可怕&amp;quot;的肌肉记忆）。这就是第一步（流动）+ 第二步（反馈）的教科书落地。&lt;/p&gt;
&lt;h3 id=&#34;73-amazon从单体到面向服务与-two-pizza-team&#34;&gt;7.3 Amazon：从单体到面向服务与 two-pizza team&lt;/h3&gt;
&lt;p&gt;Amazon 在 2000 年代初的增长瓶颈同样是&amp;quot;单体系统 + 部门墙&amp;quot;：代码库互相纠缠、发布互相阻塞。解法是大名鼎鼎的两条——架构上转向&lt;strong&gt;面向服务的架构&lt;/strong&gt;（团队通过稳定 API 交互，可独立部署，这就是后来 AWS 内部文化和 SOA/微服务浪潮的源头之一），组织上推行 &lt;strong&gt;two-pizza team&lt;/strong&gt;（团队小到两个披萨能喂饱，端到端拥有自己的服务：谁构建，谁运行）。这正是书里独角兽团队的原型逻辑：&lt;strong&gt;把约束从&amp;quot;共享的单体&amp;quot;变成&amp;quot;自治的小团队&amp;quot;&lt;/strong&gt;，流动自然恢复。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;8-避坑指南读完书最容易犯的四个错误&#34;&gt;8. 避坑指南：读完书最容易犯的四个错误&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;把 DevOps 理解成买工具&lt;/strong&gt;&lt;br&gt;
Jenkins、Kubernetes、Terraform 都是好工具，但书里从头到尾讲的不是工具，是&lt;strong&gt;工作如何流动&lt;/strong&gt;。先画出自己的价值流、找到自己的 Brent，再谈工具选型。工具解决不了&amp;quot;计划外工作吃掉所有人&amp;quot;的问题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;把瓶颈理解成&amp;quot;那个最忙的人&amp;quot;，然后让他更忙&lt;/strong&gt;&lt;br&gt;
给 Brent 升职加薪、让他带更多项目，只会把系统卡得更死。正确的动作是保护他的时间、榨出他的知识、复制他的能力。判断标准很简单：&lt;strong&gt;他面前那堆活变少了，全公司的交付反而变快了。&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;不敢停线&lt;/strong&gt;&lt;br&gt;
&amp;ldquo;业务等不了，我们不能冻结发布&amp;quot;是改进最大的拦路虎。但书里和丰田的经验是一致的：系统带病运转时，止血（freeze）不是成本，是投资。当然，冻结要有明确的目标和时间盒——它是手术，不是疗养。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;只要前两步，不要第三步&lt;/strong&gt;&lt;br&gt;
只做流动和反馈，组织会变成一个&amp;quot;高效的救火队&amp;rdquo;：事故发现得快、恢复得快，但同样的坑反复踩。复盘、技术债预算、刻意练习，是把&amp;quot;经验&amp;quot;变成&amp;quot;组织能力&amp;quot;的唯一通道。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;9-带走的清单&#34;&gt;9. 带走的清单&lt;/h2&gt;
&lt;p&gt;如果只能记住几件事，记这些：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IT 是工厂&lt;/strong&gt;：价值只在&amp;quot;部署到生产并被使用&amp;quot;时产生，未上线的工作是库存。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;四种工作&lt;/strong&gt;：业务项目、IT 内部项目、变更、计划外工作。&lt;strong&gt;计划外工作是隐形杀手&lt;/strong&gt;，它插队、打断、还制造更多事故。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;先让工作可见&lt;/strong&gt;（看板），再限制 WIP，周期时间才会下降——利特尔法则是物理，不是态度。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;找到你的 Brent&lt;/strong&gt;：系统吞吐由瓶颈决定，疏解瓶颈（保护时间、文档化、带教复制）的优先级高于一切局部优化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;三步工作法&lt;/strong&gt;：流动（小批量、少交接、可见化）→ 反馈（测试左移、生产遥测、安灯绳式停线）→ 持续学习（复盘、技术债预算、混沌实验）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DevOps 与 SRE 同源&lt;/strong&gt;：错误预算就是量化的&amp;quot;发布冻结阀门&amp;quot;，消除琐事就是消灭计划外工作。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现实的学费很贵&lt;/strong&gt;：Knight Capital 45 分钟 4.4 亿美元，买的是&amp;quot;手工部署 + 无回滚&amp;quot;的教训；Etsy 和 Amazon 证明另一条路走得通。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改进从止血开始&lt;/strong&gt;：必要时冻结发布，停下来看清系统，再动手手术。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;10-延伸阅读&#34;&gt;10. 延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;高德拉特《目标》（The Goal）：《凤凰项目》的思想源头，约束理论的工厂原版故事。&lt;/li&gt;
&lt;li&gt;Gene Kim 等《DevOps Handbook》：《凤凰项目》的方法论展开版，三步工作法逐条落地。&lt;/li&gt;
&lt;li&gt;Google《Site Reliability Engineering》：与本博客 SRE 笔记配合食用，从&amp;quot;为什么&amp;quot;走到&amp;quot;怎么做&amp;quot;。&lt;/li&gt;
&lt;li&gt;《持续交付》（Continuous Delivery, Humble &amp;amp; Farley）：把第一步&amp;quot;流动原则&amp;quot;展开成完整的工程实践。&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Google SRE 工作手册详细总结：写给从未接触过 SRE 的人</title>
      <link>https://ftmao.com/blog/bookstore/google-sre-workbook-summary/</link>
      <pubDate>Tue, 26 Oct 2021 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/google-sre-workbook-summary/</guid>
      <description>&lt;h1 id=&#34;google-sre-工作手册详细总结写给从未接触过-sre-的人&#34;&gt;Google SRE 工作手册详细总结：写给从未接触过 SRE 的人&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;参考书籍：Google SRE 团队《The Site Reliability Workbook》（《SRE 工作手册》，2018）
目标读者：第一次听说 &amp;ldquo;SRE&amp;rdquo; 这个词，想知道它到底是什么、解决什么问题、能不能用在自己团队的人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;这本书是什么&#34;&gt;〇、这本书是什么？&lt;/h2&gt;
&lt;p&gt;Google 在 2016 年出版了《Site Reliability Engineering》（第一本 SRE 书），讲的是 &lt;strong&gt;SRE 的原理&lt;/strong&gt;——Google 是怎么想的。2018 年这本《Workbook》（工作手册）是它的姊妹篇，讲的是 &lt;strong&gt;怎么落地&lt;/strong&gt;——两本书的章节基本一一对应（比如第一本书第 4 章&amp;quot;服务级别目标&amp;quot;对应本书第 2 章&amp;quot;实施 SLO&amp;quot;）。&lt;/p&gt;
&lt;p&gt;这本书最有价值的一点是：它故意收录了大量 &lt;strong&gt;Google 之外的公司&lt;/strong&gt; 的落地案例——Evernote、Home Depot（家得宝）、《纽约时报》、Spotify、Waze、PagerDuty——就是为了破除&amp;quot;SRE 只在 Google 那种规模和文化下才玩得转&amp;quot;的误解。全书由 100 多人贡献完成。&lt;/p&gt;
&lt;p&gt;用前言里 Mark Burgess 的话说，整本书可以浓缩成三句话：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;承诺：明确地设定服务的目标、期望和水平；&lt;/li&gt;
&lt;li&gt;评估：根据指标和预算约束持续评估这些承诺；&lt;/li&gt;
&lt;li&gt;反应：快速做出反应以兑现承诺。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;一sre-到底是什么和-devops-什么关系&#34;&gt;一、SRE 到底是什么？和 DevOps 什么关系？&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SRE = Site Reliability Engineering，网站可靠性工程&lt;/strong&gt;。这个词由 Google 工程副总裁 Ben Treynor Sloss 创造。一句话概括：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SRE 就是让软件工程师去做运维团队的工作&lt;/strong&gt;——&amp;ldquo;运维是一个软件问题&amp;rdquo;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;传统模式里，开发团队（追求快速出新功能）和运维团队（追求稳定别出事）天然对立：开发想&amp;quot;快发&amp;quot;，运维想&amp;quot;别动&amp;quot;。SRE 用一套&lt;strong&gt;量化的工程方法&lt;/strong&gt;消解这个矛盾，而不是靠开会吵架。&lt;/p&gt;
&lt;p&gt;那它和 DevOps 是什么关系？第 1 章的标题本身就是一行代码，堪称全书最著名的梗：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SRE&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; DevOps&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;DevOps 是一套文化和哲学，SRE 是它的一种具体实现&lt;/strong&gt;——就像极限编程（XP）是敏捷（Agile）的一种具体实现。Puppet 前 VP Andrew Clay Shafer 在前言里甚至说：如果第一本 SRE 书早几年出版，社区会团结在&amp;quot;消除琐事&amp;quot;的旗帜下，&amp;ldquo;DevOps&amp;quot;这个词可能根本不会存在。&lt;/p&gt;
&lt;p&gt;DevOps 的五个关键思想与 SRE 的对应实现：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;DevOps 思想&lt;/th&gt;
					&lt;th&gt;SRE 的具体做法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;不再有孤岛&lt;/td&gt;
					&lt;td&gt;SRE 与开发共享所有权、用相同的工具&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;事故是正常的，指责无用&lt;/td&gt;
					&lt;td&gt;对事不对人的事后总结（blameless postmortem）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;变更应该小步快跑&lt;/td&gt;
					&lt;td&gt;金丝雀发布、CI/CD、降低失败成本（MTTR）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;工具和文化相互关联&lt;/td&gt;
					&lt;td&gt;统一的工具链本身就是文化载体&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;度量至关重要&lt;/td&gt;
					&lt;td&gt;SLI / SLO / 错误预算&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;SRE 的七条核心原则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;运维是一个软件问题&lt;/strong&gt;——用写代码的方式消灭重复劳动；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;按 SLO 管理&lt;/strong&gt;——不追求 100% 可用性（为什么，下面细讲）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;努力减少琐事（toil）&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动化今年的工作&lt;/strong&gt;——Google 硬性规定 SRE 花在琐事上的时间不超过 50%，而且要把它当成&amp;quot;保证&amp;quot;而不是&amp;quot;上限&amp;rdquo;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;通过降低失败成本来加快开发速度&lt;/strong&gt;——恢复快（MTTR 低）比不出事更重要；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;与开发者共享所有权&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无论头衔如何，都使用相同的工具&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有一个值得记住的反面案例：2017 年 5 月英国航空大停运，被书中引为&amp;quot;把运维当成本中心拼命压缩&amp;quot;的失败典型——一场大事故抵消了低成本运营省下的所有钱。&lt;/p&gt;
&lt;h2 id=&#34;二全书的地基slislosla-和错误预算&#34;&gt;二、全书的地基：SLI、SLO、SLA 和错误预算&lt;/h2&gt;
&lt;p&gt;第 2 章被作者称为&amp;quot;本书最重要的章节&amp;quot;，金句是：&lt;strong&gt;&amp;ldquo;没有 SLO，就不需要 SRE&amp;rdquo;&lt;/strong&gt;。这一节是全书的地基，务必看懂。&lt;/p&gt;
&lt;h3 id=&#34;21-三个容易混淆的概念&#34;&gt;2.1 三个容易混淆的概念&lt;/h3&gt;
&lt;p&gt;用一个&amp;quot;外卖小哥&amp;quot;的比喻：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SLI（Service Level Indicator，服务级别指标）&lt;/strong&gt;：你实际测出来的数字。比如&amp;quot;过去 30 天，99.2% 的外卖在 40 分钟内送达&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLO（Service Level Objective，服务级别目标）&lt;/strong&gt;：你给自己定的目标。比如&amp;quot;我们承诺 99% 的订单 40 分钟内送达&amp;quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLA（Service Level Agreement，服务级别协议）&lt;/strong&gt;：和客户签的、&lt;strong&gt;违约要赔钱&lt;/strong&gt;的商业合同。通常比 SLO 宽松，SLO 是内部&amp;quot;提前预警线&amp;quot;，SLA 是&amp;quot;最后底线&amp;quot;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;工程上的标准写法：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;SLI = 好事件数 / 总事件数        （0% ~ 100% 之间）
错误预算 = 100% − SLO&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;错误预算（Error Budget）&lt;/strong&gt; 是全书最重要的发明。它的逻辑是：既然 100% 不可靠也不必要，那么&amp;quot;允许出错的额度&amp;quot;就是一种&lt;strong&gt;可以花掉的预算&lt;/strong&gt;——你可以用它来快速发布新功能、做架构实验；预算花光了，就冻结发布，全员回头修可靠性。开发和运维的矛盾，从&amp;quot;立场之争&amp;quot;变成了&amp;quot;看数据说话&amp;quot;。&lt;/p&gt;
&lt;h3 id=&#34;22-为什么不追求-100-可用性&#34;&gt;2.2 为什么不追求 100% 可用性？&lt;/h3&gt;
&lt;p&gt;书中给了四个理由：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;组件同时故障的概率永远不为零，100% 在数学上就做不到；&lt;/li&gt;
&lt;li&gt;用户访问你的服务要经过很长的链路（手机 → 运营商 → 公网 → 机房），你把可用性从 99.9% 提到 99.99%，用户的实际体验提升微乎其微——边际效用递减；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100% 意味着你不能做任何变更&lt;/strong&gt;，而变更是功能迭代的前提；&lt;/li&gt;
&lt;li&gt;100% 的 SLO 只是&amp;quot;运维团队的 SLO&amp;quot;，不是一种工程文化。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;还有一个很直观的乘法效应：如果你的服务依赖 9 个可用性 99.99% 的关键上游，你的总可用性 ≈ 0.9999¹⁰ ≈ &lt;strong&gt;99.9%&lt;/strong&gt;——4 个 9 直接掉到 3 个 9。第 19 章还有一个更扎心的数字：客户在 99.999% 的平台上自己只做到 99%，最终用户感受到的是 99% × 99.999% = &lt;strong&gt;98.99901%&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;23-一个完整的例子手机游戏后端&#34;&gt;2.3 一个完整的例子：手机游戏后端&lt;/h3&gt;
&lt;p&gt;第 2 章用一个手机游戏案例贯穿始终，非常适合入门者理解。架构如下：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[&amp;#34;📱 手机 App&amp;#34;] --&amp;gt; B[&amp;#34;HTTP API 服务&amp;#34;]
    B --&amp;gt; C[(&amp;#34;永久存储&amp;lt;br/&amp;gt;游戏状态/排行榜&amp;#34;)]
    A --&amp;gt; D[(&amp;#34;用户数据存储&amp;lt;br/&amp;gt;头像等&amp;#34;)]
    C --&amp;gt; E[&amp;#34;⏰ 定时管道&amp;lt;br/&amp;gt;生成今日/本周/历史排行榜&amp;#34;]
    E --&amp;gt; F[(&amp;#34;排行榜数据存储&amp;#34;)]
    G[&amp;#34;🌐 网站&amp;#34;] --&amp;gt; F&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;先量现状：过去 4 周 API 总请求 3,663,253 次，成功 3,557,865 次（可用性 97.123%），P90 延迟 432ms，P99 延迟 891ms。据此定出&lt;strong&gt;入门级 SLO&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可用性：97% 请求成功&lt;/li&gt;
&lt;li&gt;延迟：90% 请求 &amp;lt; 450ms，99% 请求 &amp;lt; 900ms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对应的错误预算：每四周允许失败约 109,897 个请求。&lt;/p&gt;
&lt;p&gt;然后看两个真实事故的&amp;quot;预算消耗&amp;quot;对比，这是全书最有说服力的算账方式：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;事故&lt;/th&gt;
					&lt;th&gt;错误数&lt;/th&gt;
					&lt;th&gt;消耗预算&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;一次坏发布，NullPointerException 持续 4 小时&lt;/td&gt;
					&lt;td&gt;14,066&lt;/td&gt;
					&lt;td&gt;13%&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;唯一的状态数据库故障，从备份恢复花了 20 小时&lt;/td&gt;
					&lt;td&gt;~72,000&lt;/td&gt;
					&lt;td&gt;65%&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;结论很反直觉：&lt;strong&gt;五年只发生一次的数据库故障，远没有每年 2~3 次的坏发布值得投入&lt;/strong&gt;——改进发布流程的优先级应该高于调查服务器故障。这就是错误预算的意义：它把&amp;quot;该把工程资源投到哪&amp;quot;从拍脑袋变成了算术题。&lt;/p&gt;
&lt;h3 id=&#34;24-落地-slo-的完整流程&#34;&gt;2.4 落地 SLO 的完整流程&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;绘制服务架构图&amp;#34;] --&amp;gt; B[&amp;#34;组件分类：&amp;lt;br/&amp;gt;请求驱动 / 管道 / 存储&amp;#34;]
    B --&amp;gt; C[&amp;#34;为每类组件选 SLI 规范&amp;lt;br/&amp;gt;（不超过 5 种）&amp;#34;]
    C --&amp;gt; D[&amp;#34;选择 SLI 实现方式&amp;lt;br/&amp;gt;（日志 → 探针 → 客户端插桩，精度递增）&amp;#34;]
    D --&amp;gt; E[&amp;#34;基于历史数据定初始 SLO&amp;#34;]
    E --&amp;gt; F{&amp;#34;三方达成一致？&amp;lt;br/&amp;gt;产品：对用户够用&amp;lt;br/&amp;gt;开发：预算耗尽愿意行动&amp;lt;br/&amp;gt;SRE：不靠英雄主义守得住&amp;#34;}
    F -- 否 --&amp;gt; E
    F -- 是 --&amp;gt; G[&amp;#34;白纸黑字写错误预算政策&amp;lt;br/&amp;gt;（预算耗尽怎么办）&amp;#34;]
    G --&amp;gt; H[&amp;#34;监控 + 告警上线&amp;#34;]
    H --&amp;gt; I[&amp;#34;定期审查迭代 SLO&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三类组件各自推荐的 SLI：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;请求驱动型&lt;/strong&gt;（API 服务）：可用性、延迟、品质；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管道/流水线型&lt;/strong&gt;（数据处理）：新鲜度、正确性、覆盖范围；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;存储型&lt;/strong&gt;：耐用性（数据丢不丢）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;错误预算政策里要事先写清楚&amp;quot;预算耗尽怎么办&amp;quot;：可靠性 bug 最优先、冻结生产变更（P0 问题和安全补丁除外），极端时宣布&amp;quot;紧急状态&amp;quot;，直到服务回到 SLO 以内。附录 B 还有一个值得抄的细节：&lt;strong&gt;单个事件消耗超过四周预算的 20% → 必须做事后调查，且至少包含一个 P0 行动项；对错误预算计算有分歧 → 上报 CTO 裁决&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;三非-google-公司怎么落地两个完整案例&#34;&gt;三、非 Google 公司怎么落地？两个完整案例&lt;/h2&gt;
&lt;h3 id=&#34;31-evernote和云厂商打破-slo-的墙&#34;&gt;3.1 Evernote：和云厂商&amp;quot;打破 SLO 的墙&amp;quot;&lt;/h3&gt;
&lt;p&gt;背景：全球 2.2 亿用户、存储 120 亿条信息、750+ 个 MySQL 实例，从自建数据中心迁往 GCP。传统&amp;quot;开发 vs 运维&amp;quot;两种模式（&amp;ldquo;you build it, you run it&amp;rdquo; 和 &amp;ldquo;you build it, we run it&amp;rdquo;）都试过了，都不满意，于是在 Google CRE（客户可靠性工程）团队帮助下落地 SLO。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首版 SLO：按月窗口、99.95% 正常运行时间；用第三方探针 Pingdom 每分钟轮询，需两个地理独立的探针连续失败才算&amp;quot;停机&amp;quot;；&lt;strong&gt;维护窗口也算停机&lt;/strong&gt;（用户不知情就是不可用）；&lt;/li&gt;
&lt;li&gt;治理节奏：每月与 Google 团队审查一次 SLO，每 6 个月修订一次，9 个月迭代了 3 版；指导原则是&amp;quot;&lt;strong&gt;完美是良好的敌人&lt;/strong&gt;&amp;quot;——先有再优；&lt;/li&gt;
&lt;li&gt;一个漂亮的故事：2017 年讨论是否压缩发布窗口时，双方用 SLO 计算把发布窗口从 5 个减到 2 个，&lt;strong&gt;量化了客户影响，消除了主观争论&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;最创新的一点：与 Google 共享实时 SLO 仪表板。于是 Google 的告警从泛泛的&amp;quot;GCP 负载均衡今天有点慢&amp;quot;变成&amp;quot;该问题影响 Evernote SLO 的 5%&amp;quot;，重大事件双方作为联合 P1 故障单处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;32-home-depot家得宝传统企业的规模化推广&#34;&gt;3.2 Home Depot（家得宝）：传统企业的规模化推广&lt;/h3&gt;
&lt;p&gt;背景：北美 2,200+ 门店、近 40 万员工、每年 15 亿+ 交易——证明 SRE 不是互联网公司专利。&lt;/p&gt;
&lt;p&gt;他们发明了 &lt;strong&gt;VALET 框架&lt;/strong&gt;（呼应 Google 的四大黄金信号，连批处理系统都能套用）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;V&lt;/strong&gt;olume 容量（流量/处理记录数）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A&lt;/strong&gt;vailability 可用性&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;L&lt;/strong&gt;atency 延迟&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;E&lt;/strong&gt;rrors 错误&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;T&lt;/strong&gt;ickets 故障单（人工介入次数）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;推广靠四板斧：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 推广四支柱
        A[&amp;#34;通用语言&amp;lt;br/&amp;gt;VALET 缩写，人人会说&amp;#34;]
        B[&amp;#34;布道&amp;lt;br/&amp;gt;内部博客/路演/T恤贴纸&amp;lt;br/&amp;gt;FiRE 学院培训&amp;#34;]
        C[&amp;#34;自动化&amp;lt;br/&amp;gt;TPS Report 自动采集指标&amp;lt;br/&amp;gt;VALET 仪表板 + 聊天机器人&amp;#34;]
        D[&amp;#34;激励&amp;lt;br/&amp;gt;SLO 纳入开发经理年度目标&amp;#34;]
    end
    A &amp;amp; B &amp;amp; C &amp;amp; D --&amp;gt; E[&amp;#34;一年从 0 到 800 个服务&amp;lt;br/&amp;gt;每月新增约 50 个&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;给产品经理的简化分级表也很实用：99.5% = MVP/内部工具；99.9% = 大多数非销售系统；99.95% = 销售系统；99.99% = 共享基础设施。他们的原话值得引用：&amp;ldquo;我们是一家传统企业；如果我们能成功引入这么大的变化，您也可以。&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;四监控与告警在预算烧光之前叫醒你&#34;&gt;四、监控与告警：在预算烧光之前叫醒你&lt;/h2&gt;
&lt;h3 id=&#34;41-监控的基本盘&#34;&gt;4.1 监控的基本盘&lt;/h3&gt;
&lt;p&gt;监控的五大用途：告警、诊断、可视化、长期趋势/容量规划、变更前后对比。&lt;strong&gt;指标和日志要分工&lt;/strong&gt;：指标近实时但粒度低（告警、仪表板用它），日志有延迟但精确（根因分析用它）。数据新鲜度很关键——超过 4~5 分钟的失效数据会严重影响事件响应。&lt;/p&gt;
&lt;p&gt;现代监控栈是松散耦合的：Prometheus（采集/规则）→ InfluxDB（长期存储）→ Alertmanager（告警聚合）→ Grafana（仪表板）。&lt;/p&gt;
&lt;h3 id=&#34;42-告警的正确姿势盯着错误预算的消耗率&#34;&gt;4.2 告警的正确姿势：盯着错误预算的&amp;quot;消耗率&amp;quot;&lt;/h3&gt;
&lt;p&gt;新手最常见的告警写法是&amp;quot;错误率超过 X% 就报警&amp;quot;，书第 5 章用一整章论证这有多糟糕，并给出了演进路径：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    M1[&amp;#34;方法1：错误率≥SLO 就告警&amp;lt;br/&amp;gt;❌ 太敏感：10 分钟 0.1% 错误率就告警&amp;lt;br/&amp;gt;实际只消耗月预算 0.02%，每天可收 144 条&amp;#34;]
    M2[&amp;#34;方法2：加大告警窗口&amp;lt;br/&amp;gt;❌ 检测太慢，大事故发现太晚&amp;#34;]
    M3[&amp;#34;方法3：加持续时间参数 for:1h&amp;lt;br/&amp;gt;❌ 每 10 分钟一次 5 分钟的 100% 错误尖峰&amp;lt;br/&amp;gt;累计烧掉 35% 预算却永不触发&amp;#34;]
    M4[&amp;#34;方法4：按消耗率 burn rate 告警&amp;lt;br/&amp;gt;⚠️ 进步明显，但重置时间长&amp;#34;]
    M5[&amp;#34;方法5：多消耗率分档&amp;lt;br/&amp;gt;⚠️ 快烧/慢烧分开告警&amp;#34;]
    M6[&amp;#34;方法6：多窗口多消耗率 ✅&amp;lt;br/&amp;gt;长窗保证精确度，短窗保证灵敏度&amp;#34;]
    M1 --&amp;gt; M2 --&amp;gt; M3 --&amp;gt; M4 --&amp;gt; M5 --&amp;gt; M6&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;消耗率（burn rate）&lt;/strong&gt; = 当前错误率相对 SLO 允许错误率的倍数。对照表（99.9% SLO，30 天窗口）：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;消耗率&lt;/th&gt;
					&lt;th&gt;错误率&lt;/th&gt;
					&lt;th&gt;耗尽预算需时&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;0.1%&lt;/td&gt;
					&lt;td&gt;30 天&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;10&lt;/td&gt;
					&lt;td&gt;1%&lt;/td&gt;
					&lt;td&gt;3 天&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;1000&lt;/td&gt;
					&lt;td&gt;100%&lt;/td&gt;
					&lt;td&gt;43 分钟&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;最终推荐的&amp;quot;多窗口多消耗率&amp;quot;参数（针对 99.9% SLO，可直接抄）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;呼叫值班（紧急）&lt;/strong&gt;：1 小时长窗 + 5 分钟短窗，消耗率 14.4（约 2% 预算/小时）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;呼叫值班（次紧急）&lt;/strong&gt;：6 小时 + 30 分钟，消耗率 6&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工单（不紧急）&lt;/strong&gt;：3 天 + 6 小时，消耗率 1&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;短窗口取长窗口的 1/12。两个极端情况的提醒：低流量服务（每小时 10 个请求时，1 个失败就是 10% 错误率）要合并请求或拉长时间窗；99.999% 的服务 100% 中断 &lt;strong&gt;26 秒&lt;/strong&gt;就耗尽预算——比大多数监控采集间隔还短，只能靠自愈。&lt;/p&gt;
&lt;h2 id=&#34;五消除琐事toilsre-的时间去哪了&#34;&gt;五、消除琐事（Toil）：SRE 的时间去哪了&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;琐事（toil）&lt;/strong&gt; 的六大特征：手动、重复、可自动化、被动应对、无持久价值、随服务规模线性增长。Google 规定 SRE 花在琐事上的时间不超过 50%，保证至少一半时间做&amp;quot;让明天比今天更好&amp;quot;的工程。&lt;/p&gt;
&lt;p&gt;书中几个精彩案例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;John Looney 修内核&lt;/strong&gt;：2005 年他入职 Google 时的活儿是修坏机器——任何时候都有超过 20,000 台坏机器。发现大量机器是日志把根分区填满，他本想写个脚本&amp;quot;截断大日志 + 重启 syslog&amp;quot;了事，被队友拦下要求修根因。最终他向 Linux 内核提交补丁，并用&amp;quot;这个问题导致 Google 每小时损失约 1000 美元&amp;quot;的成本分析说服了内核团队。&lt;strong&gt;修症状是琐事，修根因才是工程。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;不完美的自动化就够了&amp;rdquo;&lt;/strong&gt;：Google 数据中心网络维修自动化（Saturn/Jupiter 项目）中，内存错误诊断被过度分析了三年（收集了 650+ 个离散问题数据），后来发现多数错误是暂时性的，重启重装即可恢复——完美的自动化不值得，&amp;ldquo;足够好&amp;quot;的自动化先跑起来。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&amp;ldquo;买下那个侏儒&amp;rdquo;&lt;/strong&gt;：早年 Froogle 搜索&amp;quot;跑鞋&amp;quot;会返回一个穿跑鞋的花园侏儒（eBay 商品），修不掉这个 bug，团队干脆在 eBay 上把侏儒买了下来。引申义：&lt;strong&gt;有时改变现实去适应代码，比反过来便宜。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;遗留系统的四阶段处理路径：&lt;strong&gt;避免 → 封装/增强（高息技术债转低息）→ 替换/重构 → 退役/监护&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;与琐事相关的第 7 章&amp;quot;简单化&amp;quot;还有两个名案例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Borg vs Omega&lt;/strong&gt;：Omega 本想做成&amp;quot;更干净的 Borg&amp;quot;重写版，但 Borg 持续演进让 Omega 永远追不上移动的目标，迁移成本高到不可行。结局：Omega 的想法回流 Borg，概念最终催生了开源的 &lt;strong&gt;Kubernetes&lt;/strong&gt;。教训：重写前先算一算&amp;quot;同样的精力投入改进现有系统会怎样&amp;rdquo;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pDNS 循环依赖&lt;/strong&gt;：客户端查服务 IP 依赖 Svelte，Svelte 依赖 pDNS，pDNS 的负载均衡器又依赖 Svelte——&amp;ldquo;我们就像洞穴居民，只能用上一次篝火点燃的火炬点火&amp;rdquo;（系统冷启动成为不可能）。解法：低级组件本地缓存关键 IP 列表，打破环路。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;六值班与事件响应像消防队一样打仗&#34;&gt;六、值班与事件响应：像消防队一样打仗&lt;/h2&gt;
&lt;h3 id=&#34;61-可持续的值班体系&#34;&gt;6.1 可持续的值班体系&lt;/h3&gt;
&lt;p&gt;关键数字：轮班不超过 12 小时；&lt;strong&gt;每 12 小时班次最多 2 个事件&lt;/strong&gt;（再多就处理不过来了）；多站点 24/7 值班每站点至少 5 人；保证 ≥50% 的时间留给项目工作。警惕&amp;quot;温水煮青蛙&amp;quot;式的运维过载——值班负载用 21 天移动平均来看趋势。&lt;/p&gt;
&lt;p&gt;Evernote 的事件分级很清爽：&lt;strong&gt;P1 立即处理&lt;/strong&gt;（呼叫值班、影响 SLO）、&lt;strong&gt;P2 下一工作日&lt;/strong&gt;（邮件通知）、&lt;strong&gt;P3 仅供参考&lt;/strong&gt;（只看仪表板）。&lt;/p&gt;
&lt;h3 id=&#34;62-事件指挥体系icsimag&#34;&gt;6.2 事件指挥体系（ICS/IMAG）&lt;/h3&gt;
&lt;p&gt;大规模事件的打法直接借用了消防系统：1968 年消防员为应对野火创建的&lt;strong&gt;事件指挥系统（ICS）&lt;/strong&gt;，Google 内部叫 IMAG。核心思想：混乱中最大的敌人不是故障本身，而是&amp;quot;谁说了算都不知道&amp;quot;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
    IC[&amp;#34;🎖️ 事件指挥官 IC&amp;lt;br/&amp;gt;总负责，做决策，默认承担未委派的角色&amp;#34;]
    CL[&amp;#34;📢 沟通主管 CL&amp;lt;br/&amp;gt;对用户/领导/媒体发通告&amp;#34;]
    OL[&amp;#34;🔧 运维主管 OL&amp;lt;br/&amp;gt;带领团队执行技术操作&amp;#34;]
    IC --&amp;gt; CL
    IC --&amp;gt; OL&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;事件管理围绕&amp;quot;三个 C&amp;quot;：&lt;strong&gt;Coordinate（协调）、Communicate（沟通）、Control（控制）&lt;/strong&gt;。四条基本原则：保持命令清晰、明确角色、实时记录、&lt;strong&gt;尽早且频繁地宣布事件&lt;/strong&gt;。行动顺序永远是&amp;quot;缓解优先&amp;quot;：评估影响 → 缓解影响 → 根因分析 → 修复与事后报告。&lt;/p&gt;
&lt;p&gt;三个真实案例，一正一反一个惊险：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;反例：Google Home/Assistant 事件（2017）&lt;/strong&gt;：一个 bug 让文件获取频率比预期高 50 倍，5 月 22 日就发现 QPS 异常，但误判了根因，而且&lt;strong&gt;在周六把灰度推到了 100%&lt;/strong&gt;（违反&amp;quot;首次上线仅在工作日&amp;quot;的铁律），用户丢了一半请求，靠开发者周末&amp;quot;英雄主义&amp;quot;硬扛。教训：没有尽早宣布事件、缓解后没定根因就继续推送。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;惊险：GKE CreateCluster 中断&lt;/strong&gt;：欧洲集群创建失败率超 60%，根因竟是从 DockerHub 拉取的一个镜像损坏（美国正常）。伦敦 SRE 6:41 被呼叫，9:10 才正式建立 IMAG 结构（太晚了），最后发现用现成的 GCR 镜像站点（&lt;code&gt;--registry-mirror=https://mirror.gcr.io&lt;/code&gt;）就能缓解——&lt;strong&gt;镜像其实早就就位了&lt;/strong&gt;。总中断 6 小时 40 分钟，41 人参与，事后报告列出 28 个行动项。最大教训：发现大致位置后就该先用&amp;quot;通用缓解&amp;quot;（回滚到已知良好状态），而不是继续深挖根因。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正面：比利时数据中心雷击（2015）&lt;/strong&gt;：两分钟内 4 次雷击，UPS 电池在第 3、4 次雷击间隔太近时未能切换，磁盘托盘断电。事件指挥得当：IC 定了三个明确目标，沟通主管对四类受众（公司领导、依赖团队、外部客户、工单客户）分别通报。最终数据丢失仅 &lt;strong&gt;0.000001%&lt;/strong&gt;——管理良好的事件，损失可以极小。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;七事后总结文化从失败中学习&#34;&gt;七、事后总结文化：从失败中学习&lt;/h2&gt;
&lt;p&gt;SRE 文化的灵魂是&lt;strong&gt;对事不对人的事后总结（blameless postmortem）&lt;/strong&gt;。第 10 章用同一个真实事故的&amp;quot;坏版本&amp;quot;和&amp;quot;好版本&amp;quot;两版报告做对比，极其有教育意义。&lt;/p&gt;
&lt;p&gt;事故本身（2014-08-11，Google 卫星擦除事故）：Google 在托管机房部署的代理/缓存机架叫&amp;quot;卫星&amp;quot;。一次机架退役流程中，自动化中途失败，工程师手动重跑——重跑时 &lt;code&gt;GetMachines()&lt;/code&gt; 返回了空列表，而 API 有个 bug：&lt;strong&gt;空列表被当作&amp;quot;无过滤条件&amp;quot;，而不是&amp;quot;不操作任何机器&amp;quot;&lt;/strong&gt;。于是全球所有卫星机器在几分钟内被磁盘擦除。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;机架退役自动化失败&amp;#34;] --&amp;gt; B[&amp;#34;工程师手动重跑流程&amp;#34;]
    B --&amp;gt; C[&amp;#34;GetMachines() 返回空列表&amp;#34;]
    C --&amp;gt; D[&amp;#34;API bug：空列表 = 无过滤条件&amp;lt;br/&amp;gt;（而不是不操作任何机器）&amp;#34;]
    D --&amp;gt; E[&amp;#34;全球所有卫星进入退役流程&amp;#34;]
    E --&amp;gt; F[&amp;#34;磁盘被全部擦除 💥&amp;#34;]
    F --&amp;gt; G[&amp;#34;边缘流量切到核心集群&amp;lt;br/&amp;gt;用户延迟上升约两天&amp;#34;]
    G -.-&amp;gt;|&amp;#34;容量规划良好，&amp;lt;br/&amp;gt;用户几乎无感知&amp;#34;| H[&amp;#34;几天后恢复&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;两版事后报告的对比：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;&lt;/th&gt;
					&lt;th&gt;坏版本&lt;/th&gt;
					&lt;th&gt;好版本&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;发布时间&lt;/td&gt;
					&lt;td&gt;拖了 4 个月&lt;/td&gt;
					&lt;td&gt;4 天内（8/11 出事，8/15 发布）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;根本原因&lt;/td&gt;
					&lt;td&gt;&amp;ldquo;dylanfour@ 粗心大意忽略自动化&amp;rdquo;&lt;/td&gt;
					&lt;td&gt;深入分析 API 语义缺陷与流程漏洞&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;行动项&lt;/td&gt;
					&lt;td&gt;全是 P2，措辞含糊（&amp;ldquo;使自动化更好&amp;rdquo;）&lt;/td&gt;
					&lt;td&gt;每项有唯一所有者、优先级、bug 号，含 P0 级&amp;quot;大红色按钮&amp;quot;（紧急停止开关）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;分享范围&lt;/td&gt;
					&lt;td&gt;团队小范围&lt;/td&gt;
					&lt;td&gt;全公司工程师&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;后续更有说服力：三年后类似事件重演，但第一次事后总结落实的行动项大幅缩小了爆炸半径。Google 运维副总裁 Ben Treynor Sloss 的原话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;没有后续行动的事后总结等于没写事后总结。所有影响用户的停机事后总结必须至少关联一个 P0/P1 bug，例外由我亲自审批，例外极少。&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;事后总结文化崩塌的信号也要警惕：工程师回避参与、高管说指责性语言、没时间写、同类事故重复发生。&lt;/p&gt;
&lt;h2 id=&#34;八管理负载pokémon-go-的惊险一课&#34;&gt;八、管理负载：Pokémon GO 的惊险一课&lt;/h2&gt;
&lt;p&gt;Google 管理流量的三层工具：&lt;strong&gt;负载均衡 + 自动扩缩容 + 减载（load shedding）&lt;/strong&gt;。核心警告是：&lt;strong&gt;必须把它们当作一个整体系统设计，否则工具之间会形成灾难性的反馈回路。&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&#34;81-案例pokémon-go2016真实&#34;&gt;8.1 案例：Pokémon GO（2016，真实）&lt;/h3&gt;
&lt;p&gt;上线前按最乐观估计的 5 倍做了负载测试，实际流量是估计的近 &lt;strong&gt;50 倍&lt;/strong&gt;。随后发生的级联故障堪称教科书：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;真实流量超预期 50 倍&amp;#34;] --&amp;gt; B[&amp;#34;Niantic 前端代理跟不上入站连接&amp;#34;]
    B --&amp;gt; C[&amp;#34;后端变慢但不拒绝请求&amp;#34;]
    C --&amp;gt; D[&amp;#34;负载均衡层自动重试 GET&amp;#34;]
    D --&amp;gt; E[&amp;#34;GFE 的 SSL 客户端代码被打到极限&amp;#34;]
    E --&amp;gt; F[&amp;#34;GCLB 全球容量实际下降 50% ⚠️&amp;#34;]
    C --&amp;gt; G[&amp;#34;客户端立即重试 + 固定退避&amp;lt;br/&amp;gt;错误时所有客户端同步重试（雷群）&amp;#34;]
    G --&amp;gt; H[&amp;#34;同步峰值达此前全球 RPS 峰值的 20 倍&amp;#34;]
    F &amp;amp; H --&amp;gt; I[&amp;#34;Google SRE 三步止血：&amp;lt;br/&amp;gt;①隔离 Pokémon GO 专用池 ②扩容 ③限流&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;事后双方各自改进：Niantic 给客户端加了&lt;strong&gt;抖动（jitter）+ 截断指数退避&lt;/strong&gt;；Google 学会把 GFE 后端当潜在负载源、建立准入负载测试。共同教训：&lt;strong&gt;尽可能靠近客户端测量负载&lt;/strong&gt;。&lt;/p&gt;
&lt;h3 id=&#34;82-案例dressy-减载反馈回路虚构但典型&#34;&gt;8.2 案例：Dressy 减载反馈回路（虚构但典型）&lt;/h3&gt;
&lt;p&gt;三区域部署，A 区流量上涨先触发减载（CPU 超阈值直接拒绝请求）→ 被拒请求几乎不耗 CPU → 负载均衡器一看&amp;quot;A 区每个请求成本只有 B/C 的 1/10，效率最高！&amp;quot;→ 把更多流量发给 A → A 区 503 爆炸。恶性循环的根源：&lt;strong&gt;配减载的和配负载均衡的是两拨人，没人把它们当统一的负载管理系统检查&lt;/strong&gt;。修法很巧妙：让被拒绝的请求按 120% CPU 计入利用率，A 区立刻&amp;quot;显得&amp;quot;过载，流量自然流向 B/C。&lt;/p&gt;
&lt;h2 id=&#34;九nalsd把白板上的架构图翻译成数字&#34;&gt;九、NALSD：把白板上的架构图翻译成数字&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;NALSD（Non-Abstract Large System Design，非抽象大型系统设计）&lt;/strong&gt; 是 Google 要求的系统设计方法：从问题出发迭代设计，每一步都必须把架构图翻译成具体的资源估算（多少台机器、多少字节、多少 IOPS），并回答四个问题——&amp;ldquo;可能吗？能做得更好吗？可行吗（钱和硬件）？有弹性吗（故障怎么办）？&amp;rdquo;&lt;/p&gt;
&lt;p&gt;书中用 AdWords 点击率统计系统做了五轮完整迭代：每秒 50 万次搜索查询、1 万次广告点击，为数百万广告主出实时仪表板（SLO：99.9% 的查询 1 秒内完成，数据延迟不超过 5 分钟）。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    I1[&amp;#34;迭代1 单机方案&amp;lt;br/&amp;gt;100TB/天日志&amp;lt;br/&amp;gt;需 2500 块 HDD 或 1563 台机器&amp;lt;br/&amp;gt;❌ 单点故障一串&amp;#34;] --&amp;gt; I2[&amp;#34;迭代2 MapReduce 批处理&amp;lt;br/&amp;gt;❌ 做不到 5 分钟内可见&amp;#34;]
    I2 --&amp;gt; I3[&amp;#34;迭代3 LogJoiner&amp;lt;br/&amp;gt;流式 join 查询日志与点击日志&amp;lt;br/&amp;gt;⚠️ IOPS 单机扛不住&amp;#34;]
    I3 --&amp;gt; I4[&amp;#34;迭代4 分片 + 副本&amp;lt;br/&amp;gt;hash(query_id) mod N&amp;lt;br/&amp;gt;⚠️ 单数据中心仍是单点&amp;#34;]
    I4 --&amp;gt; I5[&amp;#34;迭代5 多数据中心 + Paxos ✅&amp;lt;br/&amp;gt;每 DC 约 4TB RAM、64 台机器&amp;lt;br/&amp;gt;网络带宽只用 25%&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;给面试和日常设计的启发：每轮迭代不是&amp;quot;画个更复杂的图&amp;quot;，而是&lt;strong&gt;算一笔账&lt;/strong&gt;——日数据量 = QPS × 86400 × 条目大小；磁盘数 = QPS ÷ 单盘 IOPS；Paxos 顺序吞吐 = 1000ms ÷ 跨机房 RTT。假设不必完美，有价值的是把多个不完美但合理的估算组合起来做决策。&lt;/p&gt;
&lt;h2 id=&#34;十数据处理管道配置与金丝雀发布&#34;&gt;十、数据处理管道、配置与金丝雀发布&lt;/h2&gt;
&lt;h3 id=&#34;101-把管道当服务运营spotify-案例&#34;&gt;10.1 把管道当服务运营（Spotify 案例）&lt;/h3&gt;
&lt;p&gt;数据管道也要定 SLO：新鲜度（&amp;ldquo;99% 的数据 30 分钟内处理完&amp;rdquo;）、正确性、完整性。Spotify 的事件传递系统每天收集&lt;strong&gt;数千亿条事件&lt;/strong&gt;（听歌、点广告……），用于向艺术家支付版税——可靠性是财务级要求。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;Spotify 客户端事件&amp;#34;] --&amp;gt; B[&amp;#34;Cloud Pub/Sub 主题&amp;lt;br/&amp;gt;（按事件类型完全隔离）&amp;#34;]
    B --&amp;gt; C[&amp;#34;消费微服务&amp;#34;]
    C --&amp;gt; D[&amp;#34;分区微服务&amp;lt;br/&amp;gt;分配到小时桶&amp;#34;]
    D --&amp;gt; E[&amp;#34;Dataproc 批作业去重&amp;#34;]
    E --&amp;gt; F[(&amp;#34;GCS 小时桶&amp;lt;br/&amp;gt;与下游的唯一接口&amp;#34;)]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个值得抄的设计决策：时间戳用&lt;strong&gt;服务器接收时间&lt;/strong&gt;而非客户端时间（用户可以离线缓冲 30 天、可以改设备时间）；只给&amp;quot;财务相关&amp;quot;事件承诺偏度/完整性 SLO，&lt;strong&gt;刻意不提供数据质量 SLO&lt;/strong&gt;——&amp;ldquo;我们是邮政服务：负责按时完整送达，包裹里的内容质量归寄件人&amp;rdquo;；容量规划留安全边际，高峰 CPU 不超 50%。&lt;/p&gt;
&lt;h3 id=&#34;102-配置最好的人机界面是没有配置&#34;&gt;10.2 配置：最好的人机界面是&amp;quot;没有配置&amp;quot;&lt;/h3&gt;
&lt;p&gt;配置三要素哲学：理想配置是&lt;strong&gt;完全没有配置&lt;/strong&gt;（系统自动识别正确值）；以用户为中心（用户说&amp;quot;我要热的绿茶&amp;quot;，而不是指定水量/温度/浸泡时间）；动态默认值（线程数默认 = CPU 核数）。任何配置变更必须满足安全三属性：&lt;strong&gt;逐步部署、可回滚、失控时自动回滚&lt;/strong&gt;（想想多少人用防火墙规则把自己锁在服务器外面过）。&lt;/p&gt;
&lt;h3 id=&#34;103-金丝雀发布用小部分错误预算买安全感&#34;&gt;10.3 金丝雀发布：用小部分错误预算买安全感&lt;/h3&gt;
&lt;p&gt;术语来自煤矿里的金丝雀。核心是一笔账：一个缺陷导致 20% 请求出错，直接全量发布就是 20% 总错误率；先部署到 5% 的金丝雀集群，&lt;strong&gt;总错误率只有 5% × 20% = 1%&lt;/strong&gt;。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;新版本部署到 5% 金丝雀集群&amp;#34;] --&amp;gt; B[&amp;#34;细分指标对比：&amp;lt;br/&amp;gt;金丝雀组 vs 对照组&amp;#34;]
    B --&amp;gt; C{&amp;#34;错误率/延迟显著偏离？&amp;#34;}
    C -- 是 --&amp;gt; D[&amp;#34;暂停发布 + 回滚&amp;lt;br/&amp;gt;通知人工介入&amp;#34;]
    C -- 否 --&amp;gt; E[&amp;#34;逐步扩大：1%→10%→50%→100%&amp;#34;]
    E --&amp;gt; F[&amp;#34;全量发布 ✅&amp;#34;]
    D -.-&amp;gt;|&amp;#34;修复后重来&amp;#34;| A&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;两个容易踩的坑：监控指标的聚合间隔必须&lt;strong&gt;小于等于金丝雀时长&lt;/strong&gt;（30 分钟的金丝雀用&amp;quot;每小时错误数&amp;quot;评估，噪声会淹没信号）；金丝雀指标首选 SLI，且&lt;strong&gt;总数不要超过 12 个&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;十一人的部分过载恢复团队建设与组织变革&#34;&gt;十一、人的部分：过载恢复、团队建设与组织变革&lt;/h2&gt;
&lt;p&gt;第三部分&amp;quot;流程&amp;quot;处理的是书中最&amp;quot;软&amp;quot;也最难的部分。&lt;/p&gt;
&lt;h3 id=&#34;111-识别过载并恢复第-17-章&#34;&gt;11.1 识别过载并恢复（第 17 章）&lt;/h3&gt;
&lt;p&gt;过载分两种：&lt;strong&gt;真实过载&lt;/strong&gt;（任务量客观超支）和&lt;strong&gt;感知过载&lt;/strong&gt;（沟通不足导致的主观感受）——书中强调，感知过载的实际危害和真实过载一样大，而且会传染。恶性循环是这样的：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;运维过载&amp;#34;] --&amp;gt; B[&amp;#34;心理压力&amp;#34;]
    B --&amp;gt; C[&amp;#34;犯错增多&amp;#34;]
    C --&amp;gt; D[&amp;#34;可靠性下降&amp;#34;]
    D --&amp;gt; E[&amp;#34;更多运维工作&amp;#34;]
    E --&amp;gt; A
    B --&amp;gt; F[&amp;#34;心理安全下降&amp;lt;br/&amp;gt;协作与信息共享停滞&amp;#34;]
    F --&amp;gt; G[&amp;#34;效率更低&amp;#34;]
    G --&amp;gt; E&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;真实案例：Google 一个支撑 Gmail/Drive 后端的存储 SRE 团队，2016 年&lt;strong&gt;三分之二的成员&lt;/strong&gt;短期相继转岗。恢复方法朴素得出奇：全队进一个房间把所有职责写上白板分类，两天时间（一天分类、一天写文档/做自动化）清理了几个月的积压；之后每两周分类一次，&lt;strong&gt;每人最多 10 张打开的工单&lt;/strong&gt;，超了就有五种干预动作。另一个感知过载团队（悉尼/苏黎世两地）的恢复路线是分三档：短期先减压（圆桌吐槽会、删掉垃圾警报、&amp;ldquo;慷慨地静音&amp;rdquo;）、中期减负（把一项服务交还开发团队、补员）、长期治本（SLO 对齐、服务统一化）。约一年后匿名调查显示团队重回&amp;quot;高效且安全&amp;quot;。&lt;/p&gt;
&lt;h3 id=&#34;112-sre-怎么参与一个服务第-18-章&#34;&gt;11.2 SRE 怎么参与一个服务（第 18 章）&lt;/h3&gt;
&lt;p&gt;SRE 永远是稀缺资源（Google 把 SRE:开发比例控制在 1:10 左右），所以要主动选择参与模式。服务的生命周期：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[&amp;#34;架构与设计&amp;#34;] --&amp;gt; B[&amp;#34;积极开发&amp;lt;br/&amp;gt;（生产化）&amp;#34;] --&amp;gt; C[&amp;#34;有限可用 Beta&amp;lt;br/&amp;gt;GA 前必须定 SLO&amp;#34;] --&amp;gt; D[&amp;#34;一般可用 GA&amp;lt;br/&amp;gt;通过 PRR 生产准备审查&amp;#34;] --&amp;gt; E[&amp;#34;弃用&amp;lt;br/&amp;gt;SRE 同时运维新旧两套&amp;#34;] --&amp;gt; F[&amp;#34;放弃&amp;lt;br/&amp;gt;运维交还开发团队&amp;#34;] --&amp;gt; G[&amp;#34;不支持&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;两个一正一反的收场案例：&lt;strong&gt;Ares 团队（好聚）&lt;/strong&gt;——反滥用团队 CAT 内部建立了具备生产知识的 Ares 小组，SRE 通过&amp;quot;交流计划&amp;quot;转移知识后体面退出，CAT 开发速度反而提升；&lt;strong&gt;数据分析管道（好散）&lt;/strong&gt;——合作十年后关系恶化（旧系统到极限、沟通破裂），最终解散 SRE 团队、平稳移交，教训是&amp;quot;如果关系健康，本可以暂时交还再取回&amp;quot;。&lt;/p&gt;
&lt;h3 id=&#34;113-sre-团队生命周期第-20-章&#34;&gt;11.3 SRE 团队生命周期（第 20 章）&lt;/h3&gt;
&lt;p&gt;从 0 到 1 的路径，三条原则贯穿：&lt;strong&gt;①SRE 需要有结果的 SLO；②SRE 必须有时间让明天比今天更好；③SRE 团队必须能调节自身工作量。&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[&amp;#34;没有 SRE？&amp;lt;br/&amp;gt;先用 SLO：承认不要 100% → 定 SLO → 写错误预算政策 → 测量并执行&amp;#34;] --&amp;gt; B[&amp;#34;招第一个 SRE&amp;lt;br/&amp;gt;考察：运维/软件工程/监控/自动化/架构&amp;#34;]
    B --&amp;gt; C[&amp;#34;组建 SRE 团队&amp;lt;br/&amp;gt;Tuckman 模型：组建 → 震荡 → 规范 → 表现&amp;#34;]
    C --&amp;gt; D[&amp;#34;多团队：按产品域或技术栈拆分&amp;lt;br/&amp;gt;⚠️ 警惕无人认领的组件&amp;#34;]
    D --&amp;gt; E[&amp;#34;地理分布式&amp;lt;br/&amp;gt;时区间隔 6-8 小时最佳，避开凌晨值班&amp;#34;]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;警告：&lt;strong&gt;不要在还没实践 SRE 原则之前，把&amp;quot;运维团队&amp;quot;改名成&amp;quot;SRE 团队&amp;quot;&lt;/strong&gt;——品牌一旦失败，组织会彻底否定 SRE 这个概念。&lt;/p&gt;
&lt;h3 id=&#34;114-组织变革管理waze-的逆袭第-21-章&#34;&gt;11.4 组织变革管理：Waze 的逆袭（第 21 章）&lt;/h3&gt;
&lt;p&gt;Waze（2013 年被 Google 收购）的消息队列是全公司组件的通信命脉。吞吐量暴增后可靠性崩溃，最糟时&lt;strong&gt;整个 SRE 团队连续两周 24/7 救火，每小时手动重启部分组件&lt;/strong&gt;才能维持数千万用户的服务。&lt;/p&gt;
&lt;p&gt;变革路径（完美对应 Kotter 八步模型）：2 名 SRE + 1 名高级工程师组成&amp;quot;指导联盟&amp;quot;；评估现成产品后决定自研；招募志愿军先给占 30% 以上流量的单个组件建专用队列，挤出两个月的原型窗口；重写客户端库实现新旧系统流量切换；先迁低流量高重要性的消息流，双写提供备份路径。最终新系统承载旧系统 &lt;strong&gt;1000 倍以上&lt;/strong&gt;的负载，且几乎无需人工干预。第二轮变革是部署流程：引入 Spinnaker 持续交付，如今 &lt;strong&gt;Waze 超过 95% 的服务持续部署&lt;/strong&gt;，几乎无人工干预。&lt;/p&gt;
&lt;p&gt;书里还有一条推广通用工具的朴素经验：迁移成本必须接近零（&amp;ldquo;重新编译就能用上&amp;rdquo;），收益必须明确（&amp;ldquo;现在你已经不受某某漏洞影响&amp;rdquo;），衡量推广成功的第一指标是&lt;strong&gt;采用率&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id=&#34;十二结语把这本书带走的五句话&#34;&gt;十二、结语：把这本书带走的五句话&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;没有 SLO，就不需要 SRE&lt;/strong&gt;——先量化&amp;quot;什么叫足够可靠&amp;quot;，再谈可靠性工程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;错误预算是开发速度与稳定性的通用货币&lt;/strong&gt;——预算充足就大胆发布，花光就冻结变更，不用吵架。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;琐事不超过 50%，修根因不修症状&lt;/strong&gt;——每小时损失多少钱，把账算给决策的人听。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓解优先于根因，对事不对人&lt;/strong&gt;——先把用户救回来，再写一份有 P0 行动项的事后总结。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SRE 不是 Google 专利&lt;/strong&gt;——Home Depot 一年从 0 到 800 个服务，&amp;ldquo;如果我们能做到，您也可以。&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后用全书结尾的祝福语收个尾：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;愿你的查询流畅，愿你的传呼机保持沉默。&lt;/strong&gt;
（May your queries flow and your pagers stay silent.）&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>
