<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>云原生 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E4%BA%91%E5%8E%9F%E7%94%9F/</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/%E4%BA%91%E5%8E%9F%E7%94%9F/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>Netflix 如何用 Kueue 简化批计算：一次百万级任务的换心手术</title>
      <link>https://ftmao.com/blog/posts/netflix-batch-compute-kueue/</link>
      <pubDate>Mon, 22 Jun 2026 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/netflix-batch-compute-kueue/</guid>
      <description>&lt;h1 id=&#34;netflix-如何用-kueue-简化批计算一次百万级任务的换心手术&#34;&gt;Netflix 如何用 Kueue 简化批计算：一次百万级任务的换心手术&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;原文：&lt;a href=&#34;https://netflixtechblog.com/how-netflix-simplified-batch-compute-with-kueue-87860682629c&#34;&gt;How Netflix Simplified Batch Compute with Kueue&lt;/a&gt;
目标读者：维护过或正在维护自研任务调度/排队系统的工程师；以及所有纠结过「自研轮子 vs 换开源方案」的人。
阅读目标：搞清楚 Netflix 为什么敢把百万级批任务的排队调度层整体换成 Kueue、怎么换的、换完拿到了什么。重点不在 Kueue 的用法，在这次迁移的&lt;strong&gt;姿势&lt;/strong&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;先把背景摊开cmb-和-titus&#34;&gt;〇、先把背景摊开：CMB 和 Titus&lt;/h2&gt;
&lt;p&gt;Netflix 的容器平台叫 Titus，跑批任务用的是自建方案 &lt;strong&gt;CMB（Compute Managed Batch）&lt;/strong&gt;，2018 年建的。CMB 负责的事：用户提交跑到结束的批工作负载，按租户层级排队、按优先级有序执行、按租户管理容量；真正跑任务的活交给 Titus。&lt;/p&gt;
&lt;p&gt;Titus 对 CMB 有两个关键能力：&lt;strong&gt;跨 cell 联邦&lt;/strong&gt;（一个 cell 就是一个 K8s 集群）和&lt;strong&gt;联邦容量预留&lt;/strong&gt;。所以 CMB 只需要对 Titus 的一个端点说话，不用关心底下集群的拓扑——这一层抽象是后面整次迁移能「无感」的地基。&lt;/p&gt;
&lt;h3 id=&#34;租户层级与两种容量&#34;&gt;租户层级与两种容量&lt;/h3&gt;
&lt;p&gt;CMB 的租户是一棵树，分两类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;内部租户（Internal Tenant）&lt;/strong&gt;：只做组织，不接收任务，没有队列；子节点可以是内部或叶子租户。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;叶子租户（Leaf Tenant）&lt;/strong&gt;：能接收任务，挂队列，不能再有子节点。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;容量也分两种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;预留容量（Reserved）&lt;/strong&gt;：内部租户的预留在其子树内公平共享；叶子租户的预留则是独占切分，别的租户碰不了，保证吞吐。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;共享容量（Shared）&lt;/strong&gt;：Compute 团队维护一个全局池子，谁都可以 burst 进去。池子按租户公平共享——但注意，&lt;strong&gt;CMB 的公平只在准入（admission）那一刻生效&lt;/strong&gt;：它没有抢占，任务一旦放行就跑到结束，哪怕公平份额已经变了。这个语义缺口后面会被 Kueue 填上。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一为什么是-kueue自研轮子的折旧时刻&#34;&gt;一、为什么是 Kueue：自研轮子的折旧时刻&lt;/h2&gt;
&lt;p&gt;CMB 2018 年建的时候，开源界还没有像样的批计算方案。但 K8s 生态滚了几年之后，CMB 有的和想要的功能——公平共享、层级租户、容量管理、优先级队列——开源项目全都有了。反过来，CMB 想加新功能（比如抢占）越来越费劲，因为它离底层的 K8s 集群隔得太远。这就是自研系统的经典折旧曲线：&lt;strong&gt;当年它填补空白，现在它阻碍你吃到生态红利。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;团队评估了怎么把批抽象现代化，最后选了 Kueue，理由五条，第一条最值得琢磨：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Kueue 不替换 kube-scheduler。&lt;/strong&gt; 和 YuniKorn、Volcano 不同，Kueue 只管排队和准入，Pod 调度还是 kube-scheduler 干，所以能和 Titus 已有的调度画像（scheduling profiles）集成。换掉 Titus 的调度画像会让任务摆放（placement）碎片化，损害效率。这条的工程含义：选组件时，&lt;strong&gt;「它不管哪一段」和「它管哪一段」同样重要&lt;/strong&gt;——Kueue 把调度和排队拆开，只拿走排队这一段，接入面就小得多。&lt;/li&gt;
&lt;li&gt;社区采用势头和创新速度。&lt;/li&gt;
&lt;li&gt;支持异构硬件上的多租户配额管理。&lt;/li&gt;
&lt;li&gt;能操作 &lt;code&gt;v1.Pod&lt;/code&gt;、&lt;code&gt;batch/v1.Job&lt;/code&gt; 这类原生原语，也支持 RayJob / RayCluster 这类高层抽象，留好了扩展空间。&lt;/li&gt;
&lt;li&gt;自带团队本来想在 CMB 里自己造的功能：抢占、all-or-nothing 调度、拓扑感知调度。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二迁移netflix-batch-的三条军规&#34;&gt;二、迁移：Netflix Batch 的三条军规&lt;/h2&gt;
&lt;p&gt;这次把 CMB 工作负载迁到 Kueue 的项目，内部叫 &lt;strong&gt;Netflix Batch&lt;/strong&gt;。军规三条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;对 CMB 终端用户零负担、完全透明。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容器启动速率和整体最大吞吐不允许回退。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;用 Kueue 替换 CMB 的排队与调度。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;新旧提交流程的关键差别：排队和调度被下放给每个启用了 Kueue 的 Titus cell 里的 Kueue，Titus 联邦层通过一个&lt;strong&gt;自研 Kueue 路由器&lt;/strong&gt;把任务路由到 Kueue cell。注意这里没说换成 K8s 的通用调度入口——联邦这层壳还在，换的是壳里面的引擎。&lt;/p&gt;
&lt;p&gt;租户管理这边，对运维来说迁移简单到「在 UI 上给租户点一个按钮」，出问题也能一键回滚。按钮底下的映射关系是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内部租户 → &lt;strong&gt;Cohort&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;叶子租户 → &lt;strong&gt;ClusterQueue + LocalQueue&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;租户上的容量配置 → &lt;strong&gt;resource flavor + 名义配额（nominal quota）&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&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 OLD[&amp;#34;迁移前 CMB&amp;#34;]
        U1[&amp;#34;用户与应用&amp;#34;] --&amp;gt;|&amp;#34;提交批任务&amp;#34;| CMBQ[&amp;#34;CMB 自研排队与调度&amp;lt;br/&amp;gt;租户层级加公平共享&amp;#34;]
        CMBQ --&amp;gt; FED[&amp;#34;Titus 联邦层&amp;#34;]
        FED --&amp;gt; CELL[&amp;#34;Titus cell 各 K8s 集群&amp;#34;]
    end
    subgraph NEW[&amp;#34;迁移后 Netflix Batch&amp;#34;]
        U2[&amp;#34;用户与应用 同一 API&amp;#34;] --&amp;gt;|&amp;#34;提交批任务 无感&amp;#34;| ROUTER[&amp;#34;自研 Kueue 路由器&amp;#34;]
        ROUTER --&amp;gt; KQ[&amp;#34;各 cell 内的 Kueue&amp;lt;br/&amp;gt;ClusterQueue 排队与准入&amp;#34;]
        KQ --&amp;gt; KS[&amp;#34;kube-scheduler 负责 Pod 摆放&amp;#34;]
    end
    OLD -.-&amp;gt;|&amp;#34;只换排队调度引擎&amp;lt;br/&amp;gt;API 与联邦层不动&amp;#34;| NEW
    style KQ fill:#fff3cd&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这张图是整篇文章的骨架：&lt;strong&gt;对上一寸不动，对下一寸不动，只把中间自研的那层换成开源组件。&lt;/strong&gt; 用户侧的 API、Titus 的联邦与调度画像都是旧资产，全保留了。&lt;/p&gt;
&lt;h3 id=&#34;三条经验教训全文最有信息量的部分&#34;&gt;三条经验教训（全文最有信息量的部分）&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;先保 API 平价，先换底层组件。&lt;/strong&gt; 不给用户暴露新 API 面，而是把底下组件换掉作为第一步。原文的说法是 unstacking bets——把赌注拆开：不让「用户迁新 API」和「底层换引擎」两件事耦合成一次豪赌。项目风险降了，客户体验也没断。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;别把最复杂的客户留到最后。&lt;/strong&gt; 他们一上来就迁最大最复杂的那个客户。信心是先难后易建起来的，后面其他客户的迁移就顺了——结果整个生产迁移只花了 &lt;strong&gt;4 周&lt;/strong&gt;。多数迁移的直觉是「先易后难、小步快跑」，Netflix 反着来：最难的客户先过，因为它把所有边角情况一次性暴露完，剩下的全是子集。这和「先打最硬的仗」是一个道理——前提是第 1 条保证了输得起。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kueue 的默认参数顶不住 Netflix 的量。&lt;/strong&gt; 必须把 QPS、Burst、groupKindConcurrency 调得比默认高很多才能满足吞吐。这个坑是提前在仿 Titus 的开发环境跑负载测试趟出来的，不是生产上撞出来的。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三换完之后抢占式公平共享补上了语义缺口&#34;&gt;三、换完之后：抢占式公平共享补上了语义缺口&lt;/h2&gt;
&lt;p&gt;Kueue 已在生产全量铺开，管理着&lt;strong&gt;数百万级&lt;/strong&gt;批工作负载。前面说的 CMB「公平只在准入时生效、无抢占」的语义缺口，现在用**基于抢占的公平共享（Preemption-based Fair Sharing）**补上了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;预留容量没人用的时候可以借给其他租户，主人回来再抢回来——&lt;strong&gt;预留语义保住，闲置不再浪费&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;高优先级任务可以抢占低优先级任务——关键业务的周转时间变快，且不怕饿死。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原文给的 ClusterQueue 抢占配置示例：&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;kueue.x-k8s.io/v1beta2&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;ClusterQueue&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:#e6db74&#34;&gt;&amp;#34;team-a-cq&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;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;preemption&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;reclaimWithinCohort&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Any&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;withinClusterQueue&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;LowerPriority&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;reclaimWithinCohort: Any&lt;/code&gt; 意思是：同 Cohort 里谁借了我的份额，我随时可以抢回来（不限优先级）；&lt;code&gt;withinClusterQueue: LowerPriority&lt;/code&gt; 意思是：队列内部只抢优先级比我低的。两条规则分别对应「租户之间」和「租户之内」两个维度的公平。&lt;/p&gt;
&lt;p&gt;效果：Compute 团队观察到&lt;strong&gt;平均资源利用率显著提升&lt;/strong&gt;（原文没给具体数字，只有这个定性结论）。另外这套经验正在外溢到其他内部团队，包括搭 K8s 原生训练基础设施的团队——批计算和 ML 训练的调度问题，本质上是同一个。&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 COHORT[&amp;#34;Cohort 对应内部租户 子树&amp;#34;]
        CQA[&amp;#34;ClusterQueue 团队 A&amp;lt;br/&amp;gt;有预留配额&amp;#34;] 
        CQB[&amp;#34;ClusterQueue 团队 B&amp;lt;br/&amp;gt;有预留配额&amp;#34;]
    end
    CQA --&amp;gt;|&amp;#34;配额闲置时&amp;lt;br/&amp;gt;借给同 Cohort 成员&amp;#34;| CQB
    CQB --&amp;gt;|&amp;#34;主人回来 reclaimWithinCohort Any&amp;lt;br/&amp;gt;抢占借出去的资源&amp;#34;| CQA
    CQA --&amp;gt;|&amp;#34;队列内部&amp;lt;br/&amp;gt;高优先级抢占低优先级&amp;#34;| PRE[&amp;#34;withinClusterQueue LowerPriority&amp;#34;]
    PRE --&amp;gt; R[&amp;#34;预留语义不丢&amp;lt;br/&amp;gt;闲置容量不浪费&amp;lt;br/&amp;gt;利用率上升&amp;#34;]
    style R fill:#e8f5e9&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;四工程实践联想&#34;&gt;四、工程实践联想&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;迁移的黄金姿势：API 不动，引擎换心。&lt;/strong&gt; Netflix 把「用户迁新 API」和「底层换引擎」拆成两个独立的赌注，先只赌后者。回到我们自己的项目：存储换引擎、队列换实现、框架换内核，第一件事永远是把门面焊死。门面动了，你就同时在做两个项目。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;先迁最难的客户，但要先有回滚按钮。&lt;/strong&gt; 「最难的先来」之所以成立，是因为他们有 UI 一键切换加一键回滚托底。没有廉价回滚能力之前，先难后易是鲁莽，不是勇气。顺序是：先造回滚，再挑硬骨头。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;调度与排队分层，是值得抄的架构决策。&lt;/strong&gt; Kueue 只做排队准入，Pod 摆放留给 kube-scheduler。这个切分让 Netflix 保住了 Titus 多年的调度调优积累。评估任何「大而全」的调度系统（YuniKorn、Volcano 都是全替换派）时，先问：它接管的那一层，我原有的积累值多少钱？&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开源组件的默认参数是按「示例集群」调的，不是按你的量。&lt;/strong&gt; QPS、Burst、并发数这类旋钮，上生产前必须在仿真环境里压出来。Netflix 的负载测试环境「模仿 Titus」，这笔钱花在了迁移前，不是事故后。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自研系统要定期做折旧审计。&lt;/strong&gt; CMB 不是烂系统，是时代变了：它独有的功能被生态追平，它缺失的功能（抢占）自研成本却越来越高。每两年问一次「这轮子如果今天立项，我还会自己造吗」，答案变了就该动手了。顺带一提，做 AI 训练平台的可以直接抄作业：批任务的排队、配额、抢占、多租户公平，和 GPU 训练集群的调度问题是同构的，Kueue 支持 RayJob/RayCluster 就是 Netflix 明说的扩展点。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;本篇小结&#34;&gt;本篇小结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;背景&lt;/strong&gt;：CMB（2018 年自研托管批处理）跑在 Titus 上，租户分内部/叶子两级，容量分预留/共享两种；共享容量只在准入时公平，无抢占。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;动机&lt;/strong&gt;：K8s 生态追平了 CMB 的功能，而自研新功能（如抢占）成本越来越高；Kueue 不替换 kube-scheduler、支持异构配额和 Pod/Job/RayJob，且有抢占、all-or-nothing、拓扑感知调度等原生能力。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;迁移（Netflix Batch）&lt;/strong&gt;：用户零感知、吞吐不回退、排队调度换 Kueue；内部租户映射为 Cohort，叶子租户映射为 ClusterQueue + LocalQueue，容量配置映射为 resource flavor + nominal quota；UI 一键切换、可回滚。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;经验&lt;/strong&gt;：API 平价先行拆分赌注；最大最复杂的客户最先迁，生产迁移仅 4 周；Kueue 需调高 QPS/Burst/groupKindConcurrency，靠仿真环境负载测试提前摸底。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现状&lt;/strong&gt;：生产全量、数百万批工作负载；基于抢占的公平共享（&lt;code&gt;reclaimWithinCohort: Any&lt;/code&gt; + &lt;code&gt;withinClusterQueue: LowerPriority&lt;/code&gt;）让预留容量可出借可回收，平均资源利用率显著提升。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话收束：这篇文章表面讲 Kueue，实际讲的是一个成熟组织怎么给自研系统办一场体面的退休——不办追悼会（大 rewrite），办的是工作交接（API 不变、逐层替换、最难的先交）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考资料&#34;&gt;参考资料&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://netflixtechblog.com/how-netflix-simplified-batch-compute-with-kueue-87860682629c&#34;&gt;How Netflix Simplified Batch Compute with Kueue&lt;/a&gt;（原文，作者 Alvin Bao、Alex Petrov、Jennifer Lai、Aidan Sherr、Samartha Chandrashekar，2026-06-22）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kueue.sigs.k8s.io/&#34;&gt;Kueue 官方文档&lt;/a&gt;（K8s 原生的批任务排队系统，原文提到的 Cohort、ClusterQueue、LocalQueue、resource flavor、抢占语义的权威定义）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://yunikorn.apache.org/&#34;&gt;YuniKorn&lt;/a&gt; 与 &lt;a href=&#34;https://volcano.sh/&#34;&gt;Volcano&lt;/a&gt;（原文对比中提到的全替换派调度器）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://netflix.github.io/titus/&#34;&gt;Netflix Titus&lt;/a&gt;（Netflix 容器平台，CMB 与 Netflix Batch 的运行底座）&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>☸️ Kubernetes 学习总结与避坑指南</title>
      <link>https://ftmao.com/blog/bookstore/kubernetes-authoritative-guide-notes/</link>
      <pubDate>Wed, 12 Feb 2025 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/bookstore/kubernetes-authoritative-guide-notes/</guid>
      <description>&lt;h1 id=&#34;-kubernetes-学习总结与避坑指南&#34;&gt;☸️ Kubernetes 学习总结与避坑指南&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;参考书籍：龚正 等《Kubernetes 权威指南：从 Docker 到 Kubernetes 实践全接触》、Marko Lukša《Kubernetes in Action》
目标读者：会一点 Docker、刚接触 K8s，或者「YAML 会抄但一出问题就懵」的同学。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kubernetes（简称 K8s，因为 K 和 s 之间有 8 个字母）是 Google 开源的容器编排系统，解决了「成百上千个容器跑在成百上千台机器上，谁来调度、谁来扩容、挂了谁来拉起」的问题。它不是一门「背完概念就会」的技术——新人最常见的状态是：每个名词都听过，但它们之间怎么串起来的说不清楚，Pod 一挂就不知道该看哪里。&lt;/p&gt;
&lt;p&gt;这篇文章帮你建立&lt;strong&gt;主干知识框架&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;K8s 集群整体长什么样？Master 和 Node 各自干什么？&lt;/li&gt;
&lt;li&gt;Pod、Node、Service、Ingress、ConfigMap 这些核心对象到底是什么关系？&lt;/li&gt;
&lt;li&gt;Istio 服务网格在解决什么问题？&lt;/li&gt;
&lt;li&gt;共享存储（PV/PVC/NAS）怎么用，有哪些坑？&lt;/li&gt;
&lt;li&gt;排查 K8s 问题需要补哪些 Unix 网络编程基础知识？&lt;/li&gt;
&lt;li&gt;生产环境如何做内外网访问隔离（私网端点 / 内网 LB / NetworkPolicy）？&lt;/li&gt;
&lt;li&gt;在阿里云 ACK（以及 AWS EKS）上落地时要注意什么？&lt;/li&gt;
&lt;li&gt;Jenkins 如何与 K8s 打通，实现自动化部署、验证与失败回滚？&lt;/li&gt;
&lt;li&gt;遇到 CrashLoopBackOff、OOMKilled、502、DNS 慢这些经典问题，该按什么思路排查？&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一k8s-整体架构一个中心化大脑--一群干活的节点&#34;&gt;一、K8s 整体架构：一个「中心化大脑 + 一群干活的节点」&lt;/h2&gt;
&lt;p&gt;K8s 集群在逻辑上分为两大部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;控制平面（Control Plane，俗称 Master）&lt;/strong&gt;：集群的大脑，负责「决策」——调度、扩缩容、故障自愈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;工作节点（Node）&lt;/strong&gt;：真正跑容器的机器，负责「执行」。&lt;/li&gt;
&lt;/ul&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
    subgraph 控制平面[&amp;#34;控制平面 Control Plane（阿里云 ACK 托管，用户不可见）&amp;#34;]
        APIServer[kube-apiserver&amp;lt;br/&amp;gt;集群唯一入口，所有操作都经过它]
        ETCD[(etcd&amp;lt;br/&amp;gt;分布式 KV 存储&amp;lt;br/&amp;gt;集群所有状态都在这里)]
        Scheduler[kube-scheduler&amp;lt;br/&amp;gt;决定 Pod 调度到哪个 Node]
        CM[kube-controller-manager&amp;lt;br/&amp;gt;各种控制器：副本数维持、节点监控等]
        CCM[cloud-controller-manager&amp;lt;br/&amp;gt;对接云厂商：SLB/EBS/路由]
    end

    subgraph 节点1[&amp;#34;Worker Node 1&amp;#34;]
        K1[kubelet&amp;lt;br/&amp;gt;节点上的「包工头」]
        P1[kube-proxy&amp;lt;br/&amp;gt;维护 Service 网络规则]
        C1[容器运行时&amp;lt;br/&amp;gt;containerd]
        Pod1[Pod A] ~~~ Pod2[Pod B]
    end

    subgraph 节点2[&amp;#34;Worker Node 2&amp;#34;]
        K2[kubelet]
        P2[kube-proxy]
        C2[containerd]
        Pod3[Pod C]
    end

    APIServer --&amp;gt; ETCD
    Scheduler --&amp;gt; APIServer
    CM --&amp;gt; APIServer
    CCM --&amp;gt; APIServer
    K1 --&amp;gt; APIServer
    K2 --&amp;gt; APIServer
    User[kubectl / CI/CD] --&amp;gt; APIServer&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;记住这条主线：&lt;strong&gt;所有组件只跟 apiserver 说话，apiserver 把状态写进 etcd&lt;/strong&gt;。你敲的每一条 &lt;code&gt;kubectl&lt;/code&gt; 命令、每一个控制器的决策，都是「读/写 apiserver」这个动作。理解了这一点，后面所有概念都好串了。&lt;/p&gt;
&lt;p&gt;K8s 的核心设计思想是&lt;strong&gt;声明式 API + 控制器循环&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;graph LR
    A[你提交 YAML&amp;lt;br/&amp;gt;期望状态：3 个副本] --&amp;gt; B[apiserver 存进 etcd]
    B --&amp;gt; C{控制器不断对比&amp;lt;br/&amp;gt;期望状态 vs 实际状态}
    C --&amp;gt;|实际只有 2 个| D[再创建 1 个 Pod]
    D --&amp;gt; C
    C --&amp;gt;|一致| E[什么都不做]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这就是「自愈」的本质：不是有人盯着修，而是控制器永远在循环纠偏。所以 K8s 里的对象是「期望状态」的描述，不是一条条命令。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二node干活的机器&#34;&gt;二、Node：干活的机器&lt;/h2&gt;
&lt;h3 id=&#34;21-node-上有什么&#34;&gt;2.1 Node 上有什么&lt;/h3&gt;
&lt;p&gt;每个 Node 上必装三个组件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;kubelet&lt;/strong&gt;：节点代理。接收 apiserver 下发的 Pod 定义（PodSpec），调用容器运行时把容器拉起来，并持续上报节点和 Pod 状态。Pod 挂了重启、探针检测，都是 kubelet 干的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;容器运行时&lt;/strong&gt;：真正拉镜像、跑容器的软件。Docker 已在 K8s 1.24 后被移除，现在主流是 &lt;strong&gt;containerd&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;kube-proxy&lt;/strong&gt;：维护节点上的网络规则（iptables/IPVS），让 Service 的虚拟 IP 能转发到真实 Pod。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-节点相关的避坑点&#34;&gt;2.2 节点相关的避坑点&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;节点 NotReady&lt;/strong&gt;：最常见的原因是 kubelet 挂了、节点磁盘满（DiskPressure）、内存不足（MemoryPressure）或网络不通。排查顺序：先 &lt;code&gt;kubectl describe node &amp;lt;名字&amp;gt;&lt;/code&gt; 看 Conditions，再登节点看 &lt;code&gt;systemctl status kubelet&lt;/code&gt; 和日志 &lt;code&gt;journalctl -u kubelet&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;磁盘压力&lt;/strong&gt;：容器日志、镜像、emptyDir 都在吃节点磁盘。务必给应用日志配轮转（logrotate 或运行时自带的日志上限 &lt;code&gt;containerLogMaxSize&lt;/code&gt;），否则磁盘写满会把节点上所有 Pod 驱逐（Evicted）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不要随意登节点手工改东西&lt;/strong&gt;：K8s 期望节点是「牛」不是「宠物」——可替换、不特殊。手工装的 Agent、改的 iptables 规则，节点一重建全没了。需要节点级能力请用 DaemonSet。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三podk8s-的最小调度单元&#34;&gt;三、Pod：K8s 的最小调度单元&lt;/h2&gt;
&lt;h3 id=&#34;31-为什么不是直接调度容器&#34;&gt;3.1 为什么不是直接调度容器&lt;/h3&gt;
&lt;p&gt;Pod 是一组&lt;strong&gt;共享网络和存储&lt;/strong&gt;的容器集合。同一个 Pod 里的容器：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;共享同一个 IP 和端口空间（通过 &lt;code&gt;localhost&lt;/code&gt; 直接互通）&lt;/li&gt;
&lt;li&gt;可以共享 Volume&lt;/li&gt;
&lt;/ul&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
    subgraph Pod[&amp;#34;Pod（10.0.1.15）&amp;#34;]
        direction LR
        C1[主容器&amp;lt;br/&amp;gt;app:8080]
        C2[边车容器&amp;lt;br/&amp;gt;日志采集]
        V[(共享 Volume&amp;lt;br/&amp;gt;日志目录)]
        C1 --&amp;gt; V
        C2 --&amp;gt; V
    end
    C1 -.-&amp;gt;|localhost:8080| C2&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;这个设计的经典用途就是 &lt;strong&gt;Sidecar（边车）模式&lt;/strong&gt;：日志采集、流量代理（Istio 的 Envoy）都以边车容器形式注入 Pod，主容器无感知。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;新人直觉「一个 Pod 跑一个容器」，这对 90% 的场景是对的。只有容器之间必须同生共死、共享 localhost 或共享文件时，才放进同一个 Pod。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-pod-生命周期与探针&#34;&gt;3.2 Pod 生命周期与探针&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;stateDiagram-v2
    [*] --&amp;gt; Pending: 创建，等调度/拉镜像
    Pending --&amp;gt; Running: 调度成功，容器启动
    Running --&amp;gt; Succeeded: 正常退出（Job 类）
    Running --&amp;gt; Failed: 异常退出
    Running --&amp;gt; Running: 探针失败→kubelet 重启容器&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;code&gt;livenessProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;判断容器是否活着，失败则&lt;strong&gt;重启容器&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;太灵敏 → 无限重启循环&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;readinessProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;判断是否能接流量，失败则从 Service 摘除&lt;/td&gt;
					&lt;td&gt;不配 → 启动期请求打到没 ready 的 Pod，报 502&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;startupProbe&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;启动探针，成功前禁用另外两个&lt;/td&gt;
					&lt;td&gt;慢启动应用（如老 Java 服务）不配 → 被 liveness 误杀&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;33-资源-requestslimits-与-qos最容易被忽视的重灾区&#34;&gt;3.3 资源 requests/limits 与 QoS：最容易被忽视的重灾区&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:#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:#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;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;250m&amp;#34;&lt;/span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 0.25 核&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;memory&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;512Mi&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;limits&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;cpu&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;500m&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;memory&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1Gi&amp;#34;&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;requests 是调度的唯一依据&lt;/strong&gt;：节点剩余可分配资源 ≥ Pod 的 requests 总和，Pod 才调度得上去。不配 requests，调度就是瞎排，节点超载。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CPU 超 limit → 被限流（throttle），容器变慢但不死；内存超 limit → OOMKilled，直接杀&lt;/strong&gt;。这是两种完全不同的失败模式，排查时先分清。&lt;/li&gt;
&lt;li&gt;QoS 等级：requests=limits 为 &lt;code&gt;Guaranteed&lt;/code&gt;（最后被驱逐）；都不配为 &lt;code&gt;BestEffort&lt;/code&gt;（节点资源紧张时最先被杀）。生产核心服务至少配 &lt;code&gt;Burstable&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;四工作负载不要直接创建裸-pod&#34;&gt;四、工作负载：不要直接创建裸 Pod&lt;/h2&gt;
&lt;p&gt;实际工作中你几乎不会手写 Pod，而是使用「管理 Pod 的控制器」：&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;Deployment&lt;/td&gt;
					&lt;td&gt;无状态服务（Web/API）&lt;/td&gt;
					&lt;td&gt;副本数 + 滚动更新 + 回滚&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;StatefulSet&lt;/td&gt;
					&lt;td&gt;有状态服务（MySQL/Kafka）&lt;/td&gt;
					&lt;td&gt;稳定的名字和网络标识、按序启停&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DaemonSet&lt;/td&gt;
					&lt;td&gt;节点级 Agent（日志/监控）&lt;/td&gt;
					&lt;td&gt;每个 Node 跑一个&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Job / CronJob&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;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;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant U as 用户
    participant D as Deployment
    participant RS1 as ReplicaSet v1
    participant RS2 as ReplicaSet v2

    U-&amp;gt;&amp;gt;D: kubectl set image 更新镜像
    D-&amp;gt;&amp;gt;RS2: 创建新 RS，先拉起 1 个新 Pod
    RS2--&amp;gt;&amp;gt;D: 新 Pod ready
    D-&amp;gt;&amp;gt;RS1: 缩掉 1 个旧 Pod
    Note over D,RS2: 循环：新 +1 → 旧 -1
    D-&amp;gt;&amp;gt;RS1: 缩到 0（旧 RS 保留，可回滚）
    U-&amp;gt;&amp;gt;D: kubectl rollout undo（随时回滚）&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;避坑点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;maxSurge / maxUnavailable&lt;/strong&gt; 控制滚动节奏。默认 25%/25%，意味着更新中可能瞬间少 25% 的可用副本——容量紧张的服务要调小 maxUnavailable。&lt;/li&gt;
&lt;li&gt;回滚靠旧 ReplicaSet 保留的 Pod 模板，&lt;code&gt;revisionHistoryLimit&lt;/code&gt; 默认 10，别设成 0 否则无法回滚。&lt;/li&gt;
&lt;li&gt;镜像 tag 不要用 &lt;code&gt;latest&lt;/code&gt;：无法回滚、无法审计，且 &lt;code&gt;imagePullPolicy&lt;/code&gt; 行为反直觉。用 git commit 号或语义化版本。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;五k8s-网络模型与必备的-unix-网络编程知识&#34;&gt;五、K8s 网络模型与必备的 Unix 网络编程知识&lt;/h2&gt;
&lt;p&gt;K8s 的网络问题占了线上故障的一半以上。而很多「K8s 网络问题」本质上是 &lt;strong&gt;Unix 网络编程/TCP 协议问题&lt;/strong&gt;，只是发生在容器环境里。这一节把两边打通。&lt;/p&gt;
&lt;h3 id=&#34;51-k8s-网络的三条铁律&#34;&gt;5.1 K8s 网络的三条铁律&lt;/h3&gt;
&lt;p&gt;K8s 对网络模型只有三个基本要求（具体实现交给 CNI 插件，如 Flannel、Calico、阿里云 Terway）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;每个 Pod 有自己的独立 IP&lt;/li&gt;
&lt;li&gt;任意两个 Pod 之间可以直接通信（不经过 NAT）&lt;/li&gt;
&lt;li&gt;Node 和 Pod 之间可以直接通信&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;graph LR
    subgraph 节点A[&amp;#34;Node A&amp;#34;]
        PA[&amp;#34;Pod A&amp;lt;br/&amp;gt;10.244.1.5&amp;#34;]
    end
    subgraph 节点B[&amp;#34;Node B&amp;#34;]
        PB[&amp;#34;Pod B&amp;lt;br/&amp;gt;10.244.2.8&amp;#34;]
    end
    PA &amp;lt;--&amp;gt;|&amp;#34;Pod 网络（CNI：Terway/Flannel/Calico）&amp;lt;br/&amp;gt;直连，无 NAT&amp;#34;| PB&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-service一组-pod-的稳定入口&#34;&gt;5.2 Service：一组 Pod 的稳定入口&lt;/h3&gt;
&lt;p&gt;Pod 会漂移（重建后 IP 就变了），所以不能在代码里写死 Pod IP。Service 提供&lt;strong&gt;一个固定虚拟 IP（ClusterIP）+ DNS 名字&lt;/strong&gt;，负载均衡到后端 Pod。&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
    Client[客户端 Pod] --&amp;gt;|&amp;#34;访问 my-svc:80&amp;lt;br/&amp;gt;DNS 解析为 10.96.0.10&amp;#34;| SVC[&amp;#34;Service: my-svc&amp;lt;br/&amp;gt;ClusterIP 10.96.0.10&amp;#34;]
    SVC --&amp;gt;|kube-proxy 的 iptables/IPVS 规则转发| P1[Pod 1]
    SVC --&amp;gt; P2[Pod 2]
    SVC --&amp;gt; P3[Pod 3]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三种 Service 类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ClusterIP&lt;/strong&gt;（默认）：仅集群内可达&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NodePort&lt;/strong&gt;：在每个节点开一个高端口（30000-32767），可从集群外访问&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LoadBalancer&lt;/strong&gt;：对接云厂商负载均衡器（阿里云 CLB/SLB、AWS NLB/ALB），对外暴露的标准姿势&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;53-为什么排查-k8s-网络要懂-unix-网络编程&#34;&gt;5.3 为什么排查 K8s 网络要懂 Unix 网络编程&lt;/h3&gt;
&lt;p&gt;下面这几组知识，是在 K8s 环境里排查连接问题的「元技能」：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（1）TCP 连接状态与 TIME_WAIT&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;TCP 挥手后主动关闭方会进入 TIME_WAIT 状态，默认保持 2MSL（Linux 上约 60s）。在高并发短连接场景（比如服务间大量 HTTP/1.0 式短调用），客户端端口会被 TIME_WAIT 大量占用，最终 &lt;code&gt;Cannot assign requested address&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;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    ESTABLISHED --&amp;gt; FIN_WAIT1: 主动方 close()
    FIN_WAIT1 --&amp;gt; FIN_WAIT2: 收到对端 ACK
    FIN_WAIT2 --&amp;gt; TIME_WAIT: 收到对端 FIN
    TIME_WAIT --&amp;gt; CLOSED: 2MSL 超时（约60s）
    ESTABLISHED --&amp;gt; CLOSE_WAIT: 对端先 close()
    CLOSE_WAIT --&amp;gt; LAST_ACK: 我方 close()
    LAST_ACK --&amp;gt; CLOSED&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;K8s 里的对应症状与解法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;症状：服务间调用偶发超时，&lt;code&gt;ss -s&lt;/code&gt; 看到 timewait 几万。&lt;/li&gt;
&lt;li&gt;正解：&lt;strong&gt;应用层用长连接 + 连接池&lt;/strong&gt;（HTTP keep-alive、gRPC），别依赖 &lt;code&gt;net.ipv4.tcp_tw_reuse&lt;/code&gt; 这类内核参数硬扛。&lt;/li&gt;
&lt;li&gt;另一个常见坑是 &lt;strong&gt;CLOSE_WAIT 堆积&lt;/strong&gt;：对端关了连接但你的代码没 close()（连接泄漏），&lt;code&gt;ss -ant | grep CLOSE_WAIT&lt;/code&gt; 一查便知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;（2）socket、backlog 与「连接被 Reset」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;服务端 &lt;code&gt;listen(fd, backlog)&lt;/code&gt; 的 backlog 是「已完成三次握手但应用还没 accept」的队列长度。K8s 场景下突发流量打到刚重启的 Pod，如果应用 backlog 太小（或迟迟不 accept），内核会直接 RST，客户端看到的是 &lt;code&gt;Connection reset by peer&lt;/code&gt;。这类问题在 Pod 扩容瞬间、探针刚通过时高发——&lt;strong&gt;readinessProbe 通过 ≠ 应用真的能扛流量&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（3）epoll 与 I/O 多路复用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一个进程通过 &lt;code&gt;epoll&lt;/code&gt; 可以管理数万连接（C10K 问题的解法）。Nginx Ingress、Envoy 都是 epoll 模型。要懂的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;进程能开的连接数受 &lt;strong&gt;文件描述符上限&lt;/strong&gt;限制：&lt;code&gt;ulimit -n&lt;/code&gt;。容器里同样生效，高并发服务记得确认 limit（&lt;code&gt;cat /proc/&amp;lt;pid&amp;gt;/limits&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;每个 TCP 连接 = 一个四元组（源IP:源端口 → 目标IP:目标端口）。客户端对单一目标的并发连接数理论上受源端口数（约 2.8 万个可用）限制——这又回到「必须用连接池复用连接」。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;（4）conntrack：K8s 特有的网络坑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;kube-proxy 的 iptables 模式依赖内核 conntrack（连接跟踪表）。节点上连接数巨大时 conntrack 表被打满（默认 &lt;code&gt;nf_conntrack_max&lt;/code&gt; 往往不够），表现为&lt;strong&gt;新建连接随机丢包、无日志&lt;/strong&gt;，极难排查。解法：调大 &lt;code&gt;net.netfilter.nf_conntrack_max&lt;/code&gt;，或干脆把 kube-proxy 切到 IPVS 模式。阿里云 ACK 的 Terway 模式在这方面压力小很多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;（5）DNS：K8s 里的「隐形依赖」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;集群内所有服务名解析都靠 CoreDNS。两个经典坑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;glibc 的 DNS 解析是串行的&lt;/strong&gt;，且默认 &lt;code&gt;ndots:5&lt;/code&gt; 导致集群内短域名先查 5 次后缀。症状：应用偶发解析慢 5s（一次超时重传）。解法：FQDN 写法 &lt;code&gt;my-svc.my-ns.svc.cluster.local.&lt;/code&gt;（结尾加点）或调整 Pod 的 &lt;code&gt;dnsConfig&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;Java 应用默认&lt;strong&gt;永久缓存 DNS 结果&lt;/strong&gt;（&lt;code&gt;networkaddress.cache.ttl=-1&lt;/code&gt;），Service 后端 IP 变了 JVM 不知道。容器里务必显式设置 DNS 缓存 TTL（如 30s）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;54-内外网访问隔离哪些服务能暴露哪些必须藏住&#34;&gt;5.4 内外网访问隔离：哪些服务能暴露，哪些必须藏住&lt;/h3&gt;
&lt;p&gt;生产集群的基本安全原则：&lt;strong&gt;默认所有东西都不出集群，只开必要的口子&lt;/strong&gt;。K8s 默认网络是「全通」的（任何 Pod 可以访问任何 Pod，LoadBalancer 一不小心就开到公网），隔离要靠自己显式配置。隔离分三个层面，从外到内：&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
    Internet[公网用户] --&amp;gt;|只放行 443| WAF[WAF / 安全组&amp;lt;br/&amp;gt;云平台层]
    WAF --&amp;gt; PubLB[公网 ALB/CLB&amp;lt;br/&amp;gt;仅 Web 入口服务]
    PubLB --&amp;gt; PubIng[Ingress Controller&amp;lt;br/&amp;gt;公网命名空间]
    PubIng --&amp;gt; WebSvc[Web Service&amp;lt;br/&amp;gt;ClusterIP]

    Office[办公网/运维] --&amp;gt;|VPN/跳板机| IntLB[内网 LoadBalancer&amp;lt;br/&amp;gt;intranet 类型]
    IntLB --&amp;gt; AdminSvc[管理后台 / 监控&amp;lt;br/&amp;gt;Grafana / Jenkins]

    subgraph 集群内[&amp;#34;集群内部（东西向流量）&amp;#34;]
        WebSvc --&amp;gt;|NetworkPolicy 允许| AppSvc[业务 Service]
        AppSvc --&amp;gt;|NetworkPolicy 允许| DBSvc[数据库 Proxy Service]
        AppSvc -.-&amp;gt;|NetworkPolicy 拒绝| Other[其他 namespace]
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;&lt;strong&gt;第一层：云网络边界（VPC / 安全组）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;API Server 用私网端点&lt;/strong&gt;：阿里云 ACK 创建集群时选择「专有网络（私网）」API Server 访问，或只绑定内网 EIP，运维通过 VPN/跳板机访问；公网端点即便有证书认证也是攻击面。AWS EKS 对应 endpoint private access。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点安全组收紧入方向&lt;/strong&gt;：只放行 NodePort 范围给内网、放行 LB 健康检查来源，其余一律拒绝。ACK 的节点安全组默认规则要审一遍。&lt;/li&gt;
&lt;li&gt;数据库、Redis 这类云产品走 VPC 内网地址，安全组只对集群节点网段开放。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第二层：Service 暴露方式——内外网分流的关键&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;LoadBalancer 类型 Service 默认创建的是&lt;strong&gt;公网 LB&lt;/strong&gt;，这是最常见的「误暴露」事故。面向内部的服务要用注解显式声明内网 LB：&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;# 阿里云：内网 CLB（VPC 内可达，公网不可达）&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;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;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;internal-admin&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;annotations&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;service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;intranet&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;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;type&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;LoadBalancer&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;port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;80&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;AWS EKS 对应注解：&lt;code&gt;service.beta.kubernetes.io/aws-load-balancer-internal: &amp;quot;true&amp;quot;&lt;/code&gt;（NLB）或 &lt;code&gt;aws-load-balancer-scheme: internal&lt;/code&gt;（ALB Controller）。&lt;/p&gt;
&lt;p&gt;经验法则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对外服务：公网 ALB/CLB + Ingress，前面最好再挂 WAF。&lt;/li&gt;
&lt;li&gt;对内服务（管理后台、监控、Jenkins）：内网 LB 或纯 ClusterIP，配合 VPN/跳板机访问。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;默认用 ClusterIP&lt;/strong&gt;，需要出集群时再显式升级类型——把「暴露」当成一个需要评审的动作。&lt;/li&gt;
&lt;li&gt;Ingress 上还可以用注解做来源 IP 白名单（如 &lt;code&gt;nginx.ingress.kubernetes.io/whitelist-source-range: 10.0.0.0/8&lt;/code&gt;），给内部接口再加一道闸。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第三层：NetworkPolicy——集群内部的东西向隔离&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;即使流量不出集群，「测试 namespace 的 Pod 误连生产数据库」这类事故也常见。NetworkPolicy 是 Pod 级的防火墙（需要 CNI 支持，ACK 的 Terway、Calico 都支持，Flannel 不支持）：&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;# 第一步：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;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;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;NetworkPolicy&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;default-deny&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;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;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;podSelector&lt;/span&gt;: {}          &lt;span style=&#34;color:#75715e&#34;&gt;# 选中 namespace 内所有 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;policyTypes&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ingress&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Egress&amp;#34;&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;# 第二步：只放行需要的流量&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;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;NetworkPolicy&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;allow-web-to-app&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;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;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;podSelector&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;matchLabels&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;order-svc}&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;policyTypes&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Ingress&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;ingress&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;from&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;podSelector&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;matchLabels&lt;/span&gt;: {&lt;span style=&#34;color:#f92672&#34;&gt;app&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;web-frontend}&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;port&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;8080&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;NetworkPolicy 是&lt;strong&gt;白名单累加&lt;/strong&gt;模型：任何一条 policy 选中某 Pod，该 Pod 未被放行的方向就被默认拒绝。写漏了 DNS（Egress 到 kube-dns 的 53 端口）会导致全 namespace 解析失败——默认拒绝后记得显式放行 DNS。&lt;/li&gt;
&lt;li&gt;策略&lt;strong&gt;只对选中后新建立的连接生效&lt;/strong&gt;，且 Flannel 下写了也不生效（静默失效，最坑的一种）。&lt;/li&gt;
&lt;li&gt;云安全组管的是「节点/LB 维度」，NetworkPolicy 管的是「Pod 维度」，两者互补不替代。&lt;/li&gt;
&lt;li&gt;上了 Istio 的话，还可以用 AuthorizationPolicy 做带身份（mTLS 服务身份）的七层鉴权，比纯 IP/端口的 NetworkPolicy 更细。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;六ingresshttp-流量的统一入口&#34;&gt;六、Ingress：HTTP 流量的统一入口&lt;/h2&gt;
&lt;p&gt;Service 解决的是四层（TCP）负载均衡；Ingress 解决的是七层（HTTP/HTTPS）路由：按域名、按路径把外部流量分发到不同 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;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TB
    User[外部用户] --&amp;gt;|HTTPS| LB[云负载均衡&amp;lt;br/&amp;gt;阿里云 ALB/CLB]
    LB --&amp;gt; IC[Ingress Controller&amp;lt;br/&amp;gt;Nginx / ALB Ingress]
    IC --&amp;gt;|&amp;#34;a.example.com/api/*&amp;#34;| S1[Service: api]
    IC --&amp;gt;|&amp;#34;a.example.com/web/*&amp;#34;| S2[Service: web]
    IC --&amp;gt;|&amp;#34;b.example.com/*&amp;#34;| S3[Service: admin]
    S1 --&amp;gt; P1[api Pod]
    S2 --&amp;gt; P2[web Pod]
    S3 --&amp;gt; P3[admin Pod]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;注意分清两层：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ingress 资源&lt;/strong&gt;（YAML 对象）：只是路由规则的声明，本身不做任何事。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress Controller&lt;/strong&gt;（真正干活的组件）：如 ingress-nginx、阿里云 ALB Ingress Controller。它 watch Ingress 资源，把规则翻译成 Nginx 配置并热加载。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;避坑点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pathType: Prefix&lt;/code&gt; 和 &lt;code&gt;Exact&lt;/code&gt; 的区别，&lt;code&gt;/api&lt;/code&gt; 配 Prefix 会把 &lt;code&gt;/apiserver&lt;/code&gt; 也匹配进去。&lt;/li&gt;
&lt;li&gt;后端重写（&lt;code&gt;rewrite-target&lt;/code&gt;）是最容易写错的注解，先想清楚「客户端路径 → 后端真实路径」的映射。&lt;/li&gt;
&lt;li&gt;HTTPS 证书放在 Secret 里，过期忘续是经典事故——建议上 cert-manager 自动签发续期。&lt;/li&gt;
&lt;li&gt;502 的第一排查动作：看 Ingress Controller 日志里 upstream 是「连接拒绝」（Pod 没 ready）还是「超时」（应用卡死）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;七configmap-与-secret配置和代码分离&#34;&gt;七、ConfigMap 与 Secret：配置和代码分离&lt;/h2&gt;
&lt;p&gt;十二要素应用的第一条就是「配置存环境」。K8s 里对应两个对象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ConfigMap&lt;/strong&gt;：普通配置（应用参数、配置文件、特性开关）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secret&lt;/strong&gt;：敏感配置（密码、密钥、证书），etcd 中 Base64 编码（注意：&lt;strong&gt;Base64 不是加密&lt;/strong&gt;，生产环境要开 etcd 静态加密或对接 KMS）&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;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;# 方式一：环境变量&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;env&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;DB_HOST&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;valueFrom&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;configMapKeyRef&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;app-config&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;key&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;db_host&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;# 方式二：挂载为文件（推荐，支持热更新）&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&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;config&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;mountPath&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/etc/app&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;config&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;configMap&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;app-config&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;/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;。这是用了 subPath 挂单个配置文件的同学必踩的坑——改完 ConfigMap 发现容器里还是旧的。解法：挂载整个目录，或接受重启 Pod 的事实。&lt;/li&gt;
&lt;li&gt;ConfigMap 有 1MB 大小限制，别把大文件塞进去。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;八共享存储pv--pvc--storageclass&#34;&gt;八、共享存储：PV / PVC / StorageClass&lt;/h2&gt;
&lt;p&gt;容器是临时的，数据要持久化就必须落到外部存储。K8s 的存储抽象分三层：&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
    subgraph 管理员/云平台侧
        SC[StorageClass&amp;lt;br/&amp;gt;定义「用什么盘」&amp;lt;br/&amp;gt;如 alicloud-disk-essd / nas]
        PV[PersistentVolume&amp;lt;br/&amp;gt;一块真实的存储&amp;lt;br/&amp;gt;云盘/NAS 文件系统]
    end
    subgraph 应用侧
        PVC[PersistentVolumeClaim&amp;lt;br/&amp;gt;「我要 100Gi 存储」的申请单]
        Pod[Pod 挂载 PVC]
    end
    PVC --&amp;gt;|绑定| PV
    SC --&amp;gt;|动态供给：PVC 一创建&amp;lt;br/&amp;gt;自动开出对应 PV| PV
    Pod --&amp;gt; PVC&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;现代用法几乎全是&lt;strong&gt;动态供给&lt;/strong&gt;：你只需要写 PVC 声明要多少空间，StorageClass 自动调用云厂商 API 创建云盘/NAS，不用手工建 PV。&lt;/p&gt;
&lt;p&gt;三种访问模式（Access Modes）是高频考点：&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;ReadWriteOnce (RWO)&lt;/td&gt;
					&lt;td&gt;只能挂到一个节点&lt;/td&gt;
					&lt;td&gt;云盘（阿里云 ESSD、AWS EBS）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ReadWriteMany (RWX)&lt;/td&gt;
					&lt;td&gt;多节点共享读写&lt;/td&gt;
					&lt;td&gt;文件存储（阿里云 NAS、AWS EFS）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ReadOnlyMany (ROX)&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;&lt;strong&gt;共享存储的避坑清单：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;云盘（RWO）不能跨可用区挂载&lt;/strong&gt;。PV 创建在可用区 A，Pod 被调度到可用区 B 就永远 Pending。解法：用 &lt;code&gt;WaitForFirstConsumer&lt;/code&gt; 模式的 StorageClass（等 Pod 定好节点再开盘），并用拓扑约束调度。&lt;/li&gt;
&lt;li&gt;要&lt;strong&gt;多副本共享文件&lt;/strong&gt;（如多个 Web Pod 共享上传目录），必须用 NAS/EFS 这类 RWX 存储，云盘做不到。&lt;/li&gt;
&lt;li&gt;NAS 挂载注意&lt;strong&gt;权限问题&lt;/strong&gt;：容器内进程 UID 和 NAS 目录属主不匹配会 Permission denied，用 &lt;code&gt;securityContext.fsGroup&lt;/code&gt; 或挂载参数解决。&lt;/li&gt;
&lt;li&gt;删除 PVC 会不会丢数据，取决于 PV 的 &lt;code&gt;persistentVolumeReclaimPolicy&lt;/code&gt;（Retain 保留 / Delete 连盘一起删）。&lt;strong&gt;生产数据盘务必确认是 Retain&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;StatefulSet 用 &lt;code&gt;volumeClaimTemplates&lt;/code&gt; 给每个副本独立开盘，扩缩容时数据卷跟着 Pod 名字走，这是有状态服务的标准姿势。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;九istio-服务网格把网络能力从业务代码里剥出来&#34;&gt;九、Istio 服务网格：把「网络能力」从业务代码里剥出来&lt;/h2&gt;
&lt;p&gt;微服务多了之后，每个服务都要自己实现超时重试、熔断、灰度、流量监控——这些和业务无关。Istio 的思路：给每个 Pod 注入一个 &lt;strong&gt;Envoy 边车代理&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;graph TB
    subgraph 控制平面[&amp;#34;Istio 控制平面（istiod）&amp;#34;]
        Pilot[配置下发&amp;lt;br/&amp;gt;流量规则]
        Citadel[证书管理&amp;lt;br/&amp;gt;mTLS]
    end
    subgraph 数据平面
        subgraph PodA[&amp;#34;Pod A&amp;#34;]
            App1[业务容器] --- E1[Envoy 边车]
        end
        subgraph PodB[&amp;#34;Pod B&amp;#34;]
            App2[业务容器] --- E2[Envoy 边车]
        end
    end
    Pilot -.-&amp;gt;|xDS 配置下发| E1
    Pilot -.-&amp;gt;|xDS 配置下发| E2
    E1 &amp;lt;--&amp;gt;|&amp;#34;mTLS 加密流量&amp;lt;br/&amp;gt;（应用无感知）&amp;#34;| E2&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;：VirtualService + DestinationRule 实现按权重灰度（v1 90% / v2 10%）、按 Header 路由、超时重试、熔断&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全&lt;/strong&gt;：服务间自动 mTLS 加密 + 身份认证（AuthorizationPolicy）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;可观测&lt;/strong&gt;：不用改代码就拿到全链路指标、调用拓扑、访问日志&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;入口&lt;/strong&gt;：Istio Gateway 替代/补充原生 Ingress，作为网格的南北向入口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;避坑点（Istio 坑不少，挑最重要的）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;边车有成本&lt;/strong&gt;：每个 Pod 多一份 Envoy 的 CPU/内存（默认 requests 约 100m/128Mi），上千 Pod 规模要算这笔账，必要时调小。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sidecar 启动顺序&lt;/strong&gt;：业务容器可能比 Envoy 先起来导致启动瞬间网络不通（现在 &lt;code&gt;holdApplicationUntilProxyStarts&lt;/code&gt; 可解）；Job 类任务跑完 Envoy 不退导致 Job 永不结束，需要显式退出或关掉注入。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;协议识别&lt;/strong&gt;：Service 端口名要带协议前缀（&lt;code&gt;name: http-xxx&lt;/code&gt;），否则 Istio 按 TCP 处理，七层能力全部失效。这是网格内路由不生效的第一嫌疑。&lt;/li&gt;
&lt;li&gt;排障三板斧：&lt;code&gt;istioctl analyze&lt;/code&gt;（静态检查配置）、&lt;code&gt;istioctl proxy-config&lt;/code&gt;（看 Envoy 实际生效配置）、Envoy 的 &lt;code&gt;:15000/stats&lt;/code&gt; 和 &lt;code&gt;:15000/clusters&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;新人别一上来就上全量 Istio。先在非核心 namespace 开注入练手，理解 VirtualService 再说。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十云上落地阿里云-ack-为主aws-eks-对照&#34;&gt;十、云上落地：阿里云 ACK 为主，AWS EKS 对照&lt;/h2&gt;
&lt;p&gt;自己从零搭一套生产级 K8s（etcd 高可用、证书、网络插件）成本极高，生产环境几乎都用云厂商托管服务。&lt;/p&gt;
&lt;h3 id=&#34;101-阿里云-ack-关键点&#34;&gt;10.1 阿里云 ACK 关键点&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;graph TB
    subgraph 阿里云托管[&amp;#34;ACK 托管层（免费/Pro）&amp;#34;]
        MA[控制平面：apiserver/etcd/scheduler&amp;lt;br/&amp;gt;阿里云托管，SLA 保障]
    end
    subgraph 你的账号[&amp;#34;你的 VPC 内&amp;#34;]
        N1[ECS 节点池 1&amp;lt;br/&amp;gt;按量/抢占式]
        N2[ECS 节点池 2&amp;lt;br/&amp;gt;弹性伸缩 ESS]
    end
    MA --&amp;gt; N1
    MA --&amp;gt; N2
    N1 -.对接.-&amp;gt; CLB[CLB/ALB&amp;lt;br/&amp;gt;负载均衡]
    N1 -.对接.-&amp;gt; NAS[云盘 ESSD / NAS&amp;lt;br/&amp;gt;持久存储]
    N1 -.对接.-&amp;gt; SLS[SLS 日志 / ARMS 监控&amp;lt;br/&amp;gt;可观测]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;实践经验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;网络插件选 Terway&lt;/strong&gt;：Pod 直接用 VPC 弹性网卡 IP，无 overlay 开销、conntrack 压力小，还能配 NetworkPolicy；Flannel 适合小规模简单场景。Terway 模式下&lt;strong&gt;规划 VPC 网段要留够&lt;/strong&gt;，每个 Pod 占一个真实 IP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;节点池 + 弹性伸缩&lt;/strong&gt;：按业务波峰波谷自动扩缩节点；抢占式实例（Spot）跑无状态任务能省 70% 成本，但要容忍随时被回收。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ingress 用 ALB Ingress Controller&lt;/strong&gt;：比自建 Nginx Ingress 省心，ALB 本身按量弹性，且和 ACK 深度集成。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;日志和监控开箱即用&lt;/strong&gt;：日志服务 SLS（采集容器 stdout 和文件日志）、ARMS Prometheus 监控，别再自己搭 ELK 了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;镜像仓库 ACR&lt;/strong&gt;：用 ACR EE（企业版）+ ACK 免密拉取插件，避免在 YAML 里配 imagePullSecret。&lt;/li&gt;
&lt;li&gt;存储选 &lt;strong&gt;CNFS/云盘 ESSD（有状态单副本）+ NAS（共享文件）+ OSS（静态资源、备份）&lt;/strong&gt; 的组合。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;102-aws-eks-速查对照&#34;&gt;10.2 AWS EKS 速查对照&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;需求&lt;/th&gt;
					&lt;th&gt;阿里云 ACK&lt;/th&gt;
					&lt;th&gt;AWS EKS&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;网络插件&lt;/td&gt;
					&lt;td&gt;Terway（VPC 网卡直连）&lt;/td&gt;
					&lt;td&gt;aws-vpc-cni（同理）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;七层入口&lt;/td&gt;
					&lt;td&gt;ALB Ingress&lt;/td&gt;
					&lt;td&gt;AWS Load Balancer Controller (ALB/NLB)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;块存储&lt;/td&gt;
					&lt;td&gt;云盘 ESSD&lt;/td&gt;
					&lt;td&gt;EBS (gp3)&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;共享文件存储&lt;/td&gt;
					&lt;td&gt;NAS&lt;/td&gt;
					&lt;td&gt;EFS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;日志&lt;/td&gt;
					&lt;td&gt;SLS&lt;/td&gt;
					&lt;td&gt;CloudWatch&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;监控&lt;/td&gt;
					&lt;td&gt;ARMS Prometheus&lt;/td&gt;
					&lt;td&gt;Managed Prometheus / Grafana&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;镜像仓库&lt;/td&gt;
					&lt;td&gt;ACR&lt;/td&gt;
					&lt;td&gt;ECR&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;无服务器节点&lt;/td&gt;
					&lt;td&gt;虚拟节点（ECI）&lt;/td&gt;
					&lt;td&gt;Fargate&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;概念完全互通，换云只是换 Controller 和注解。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十一典型问题排查手册&#34;&gt;十一、典型问题排查手册&lt;/h2&gt;
&lt;h3 id=&#34;111-通用排查流程&#34;&gt;11.1 通用排查流程&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;graph TD
    A[发现问题] --&amp;gt; B{Pod 状态？&amp;lt;br/&amp;gt;kubectl get pods}
    B --&amp;gt;|Pending| C[kubectl describe pod&amp;lt;br/&amp;gt;看 Events]
    C --&amp;gt; C1{原因}
    C1 --&amp;gt;|资源不足| C2[加节点/降 requests&amp;lt;br/&amp;gt;或开弹性伸缩]
    C1 --&amp;gt;|存储挂载失败| C3[检查 PVC 绑定/可用区]
    C1 --&amp;gt;|镜像拉取失败| C4[ImagePullBackOff：查镜像名/凭证/网络]
    B --&amp;gt;|CrashLoopBackOff| D[kubectl logs --previous&amp;lt;br/&amp;gt;看上一个容器的临终日志]
    D --&amp;gt; D1{原因}
    D1 --&amp;gt;|应用自身报错| D2[修代码/配置]
    D1 --&amp;gt;|Exit 137| D3[OOMKilled：调大 memory limit&amp;lt;br/&amp;gt;或查内存泄漏]
    D1 --&amp;gt;|Exit 143| D4[被优雅终止：查探针/滚动更新]
    B --&amp;gt;|Running 但访问异常| E[分层验证]
    E --&amp;gt; E1[1. 进 Pod 内 curl localhost 通吗？]
    E1 --&amp;gt; E2[2. curl Pod IP 通吗？]
    E2 --&amp;gt; E3[3. curl Service ClusterIP 通吗？&amp;lt;br/&amp;gt;查 Endpoints 是否有后端]
    E3 --&amp;gt; E4[4. Ingress 域名通吗？&amp;lt;br/&amp;gt;查 Controller 日志]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;112-高频问题速查表&#34;&gt;11.2 高频问题速查表&lt;/h3&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;ImagePullBackOff&lt;/td&gt;
					&lt;td&gt;镜像名/tag 错、私有仓库无凭证、节点无公网&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;describe pod&lt;/code&gt; 看 Events 报错原文&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;CrashLoopBackOff&lt;/td&gt;
					&lt;td&gt;配置错、依赖没起来、探针误杀&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;kubectl logs --previous&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;OOMKilled (137)&lt;/td&gt;
					&lt;td&gt;内存 limit 太小或泄漏&lt;/td&gt;
					&lt;td&gt;看监控曲线，调 limit；Java 服务注意堆外内存&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Pod 一直 Pending&lt;/td&gt;
					&lt;td&gt;资源不够、PVC 未绑定、污点/亲和性&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;describe pod&lt;/code&gt; 看调度失败原因&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;服务间偶发超时&lt;/td&gt;
					&lt;td&gt;conntrack 满、DNS 慢、TIME_WAIT 耗尽&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;ss -s&lt;/code&gt;、CoreDNS 日志、查连接池配置&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;滚动更新后 502&lt;/td&gt;
					&lt;td&gt;没配 readinessProbe / 没处理 SIGTERM 优雅退出&lt;/td&gt;
					&lt;td&gt;配探针 + preStop + 应用优雅关闭&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;ConfigMap 改了不生效&lt;/td&gt;
					&lt;td&gt;环境变量注入 / subPath 挂载&lt;/td&gt;
					&lt;td&gt;重启 Pod 或改为目录挂载&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;跨可用区挂载失败&lt;/td&gt;
					&lt;td&gt;云盘和节点不在同一 AZ&lt;/td&gt;
					&lt;td&gt;WaitForFirstConsumer + 拓扑约束&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;113-优雅退出滚动更新不断流的完整姿势&#34;&gt;11.3 优雅退出：滚动更新不断流的完整姿势&lt;/h3&gt;
&lt;p&gt;Pod 被删除时的标准流程（很多人只知道一半）：&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;sequenceDiagram
    participant K as kubelet
    participant P as Pod
    participant EP as Endpoints

    K-&amp;gt;&amp;gt;EP: 把 Pod 从 Service 摘除（不再接新流量）
    K-&amp;gt;&amp;gt;P: 执行 preStop 钩子（如 sleep 5，等摘流量生效）
    K-&amp;gt;&amp;gt;P: 发送 SIGTERM
    P-&amp;gt;&amp;gt;P: 应用应：停止接新请求，处理完存量请求后退出
    Note over K,P: 超过 terminationGracePeriodSeconds（默认30s）
    K-&amp;gt;&amp;gt;P: SIGKILL 强杀&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;三个缺一不可：readinessProbe 保证新 Pod ready 才切流量；preStop/优雅关闭保证老 Pod 处理完存量请求；&lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt; 给够时间。Java/Go 服务请确认框架注册了 SIGTERM 处理器（Spring Boot 的 &lt;code&gt;server.shutdown=graceful&lt;/code&gt;、Go 的 &lt;code&gt;signal.Notify&lt;/code&gt;）。&lt;/p&gt;
&lt;h3 id=&#34;114-kubectl-排障速查&#34;&gt;11.4 kubectl 排障速查&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-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl get pods -o wide -n &amp;lt;ns&amp;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;kubectl describe pod &amp;lt;pod&amp;gt; -n &amp;lt;ns&amp;gt;        &lt;span style=&#34;color:#75715e&#34;&gt;# 看 Events（排障第一步）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl logs &amp;lt;pod&amp;gt; -c &amp;lt;container&amp;gt; --previous   &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;kubectl get endpoints &amp;lt;svc&amp;gt; -n &amp;lt;ns&amp;gt;       &lt;span style=&#34;color:#75715e&#34;&gt;# Service 后面到底有没有 Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl exec -it &amp;lt;pod&amp;gt; -- sh              &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;kubectl run debug --rm -it --image&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;busybox:1.36 -- sh   &lt;span style=&#34;color:#75715e&#34;&gt;# 临时网络调试 Pod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;kubectl top pod / kubectl top node        &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;kubectl get events --sort-by&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;.lastTimestamp -n &amp;lt;ns&amp;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;hr&gt;
&lt;h2 id=&#34;十二cicdjenkins-与-k8s-打通实现自动化部署与验证&#34;&gt;十二、CI/CD：Jenkins 与 K8s 打通，实现自动化部署与验证&lt;/h2&gt;
&lt;p&gt;手工 &lt;code&gt;kubectl apply&lt;/code&gt; 只适合学习。生产环境的标准做法是把「构建 → 推镜像 → 部署 → 验证 → 失败回滚」串成流水线，代码合并后自动完成发布。&lt;/p&gt;
&lt;h3 id=&#34;121-整体流水线长什么样&#34;&gt;12.1 整体流水线长什么样&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;graph LR
    Dev[开发者 git push] --&amp;gt; Hook[Webhook 触发 Jenkins]
    Hook --&amp;gt; Build[阶段1: 构建&amp;lt;br/&amp;gt;单测 + Docker 镜像]
    Build --&amp;gt; Push[阶段2: 推送镜像&amp;lt;br/&amp;gt;到 ACR/ECR&amp;lt;br/&amp;gt;tag = git commit]
    Push --&amp;gt; Deploy[阶段3: 部署&amp;lt;br/&amp;gt;kubectl set image&amp;lt;br/&amp;gt;更新 K8s]
    Deploy --&amp;gt; Verify[阶段4: 验证&amp;lt;br/&amp;gt;rollout status + 冒烟测试]
    Verify --&amp;gt;|成功| Done[发布完成&amp;lt;br/&amp;gt;钉钉/邮件通知]
    Verify --&amp;gt;|失败| RB[自动回滚&amp;lt;br/&amp;gt;rollout undo] --&amp;gt; Alarm[告警通知]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;关键认知：&lt;strong&gt;K8s 侧不需要装任何 Jenkins 插件或 Agent 也能被部署&lt;/strong&gt;——Jenkins 只要有目标集群的 kubeconfig（访问凭证），用 &lt;code&gt;kubectl&lt;/code&gt; 就能驱动 apiserver 完成一切。打通的本质就两件事：凭证怎么管、流水线脚本怎么写。&lt;/p&gt;
&lt;h3 id=&#34;122-jenkins-的两种部署形态&#34;&gt;12.2 Jenkins 的两种部署形态&lt;/h3&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;Jenkins 在集群外（独立服务器/另一集群）&lt;/td&gt;
					&lt;td&gt;通过 kubeconfig 远程操作业务集群&lt;/td&gt;
					&lt;td&gt;一套 Jenkins 管多个集群，最常见&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Jenkins 跑在 K8s 集群内&lt;/td&gt;
					&lt;td&gt;配合 Kubernetes 插件，每次构建动态拉起一个 Pod 作为构建 agent，用完即销毁&lt;/td&gt;
					&lt;td&gt;构建任务多、想弹性利用集群资源&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&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
    subgraph Jenkins侧[&amp;#34;Jenkins（集群外或集群内）&amp;#34;]
        J[Jenkins Master]
        C1[凭据1: kubeconfig&amp;lt;br/&amp;gt;Secret file 类型]
        C2[凭据2: ACR 账号&amp;lt;br/&amp;gt;Username/Password 类型]
        J --&amp;gt; C1
        J --&amp;gt; C2
    end
    subgraph 阿里云[&amp;#34;阿里云&amp;#34;]
        J --&amp;gt;|docker push| ACR[ACR 镜像仓库]
        J --&amp;gt;|&amp;#34;kubectl（走内网 API Server 端点）&amp;#34;| API[ACK apiserver]
        API --&amp;gt; Pod[滚动更新业务 Pod]
        Pod --&amp;gt;|免密拉取| ACR
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;推荐做法：Jenkins 与 ACK 在&lt;strong&gt;同一 VPC 内&lt;/strong&gt;，kubectl 直接走 API Server 的&lt;strong&gt;内网端点&lt;/strong&gt;（安全且免公网流量）；镜像仓库用 ACR 企业版 + ACK 免密拉取，业务侧零凭证。&lt;/p&gt;
&lt;h3 id=&#34;123-最小权限的部署凭证别把-admin-kubeconfig-给-jenkins&#34;&gt;12.3 最小权限的部署凭证：别把 admin kubeconfig 给 Jenkins&lt;/h3&gt;
&lt;p&gt;这是 CI/CD 安全的第一课。给 Jenkins 一个&lt;strong&gt;只能操作特定 namespace、只能改 Deployment&lt;/strong&gt; 的 ServiceAccount：&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;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;ServiceAccount&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;jenkins-deployer&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;prod&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;# Role 只在 prod 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;apiVersion&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rbac.authorization.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;Role&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;deployer&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;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;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;apiGroups&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;apps&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;resources&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;deployments&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;verbs&lt;/span&gt;: [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;list&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;watch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;patch&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;update&amp;#34;&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;rbac.authorization.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;RoleBinding&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;jenkins-deployer&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;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;subjects&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;ServiceAccount&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;jenkins-deployer&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;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;roleRef&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;Role&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;deployer&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;apiGroup&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;rbac.authorization.k8s.io&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;然后为这个 SA 生成 token，做成只含该 token 的 kubeconfig，存进 Jenkins 的 &lt;strong&gt;Secret file 凭据&lt;/strong&gt;。好处：凭证泄露也只影响一个 namespace 的 Deployment，且可以随时 &lt;code&gt;kubectl delete rolebinding&lt;/code&gt; 吊销。&lt;/p&gt;
&lt;h3 id=&#34;124-完整-jenkinsfile-示例&#34;&gt;12.4 完整 Jenkinsfile 示例&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;pipeline &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;    agent any
&lt;/span&gt;&lt;/span&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/order-svc&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 做 tag，可回滚可追溯
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        KUBECONFIG &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; credentials&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;ack-prod-kubeconfig&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;)&lt;/span&gt;   &lt;span style=&#34;color:#75715e&#34;&gt;// Secret file 凭据
&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;    stages &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;mvn -q test&amp;#39;&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;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;docker build -t ${IMAGE}:${TAG} .&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;}&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;                withCredentials&lt;span style=&#34;color:#f92672&#34;&gt;([&lt;/span&gt;usernamePassword&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;                    credentialsId: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;acr-account&amp;#39;&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;                    usernameVariable: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;U&amp;#39;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;,&lt;/span&gt; passwordVariable: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;P&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;                    sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;echo $P | docker login registry.cn-hangzhou.aliyuncs.com -u $U --password-stdin&amp;#39;&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;docker push ${IMAGE}:${TAG}&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;}&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;部署到 ACK&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;#34;kubectl --kubeconfig=$KUBECONFIG -n 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:#e6db74&#34;&gt;                    set image deployment/order-svc order-svc=${IMAGE}:${TAG}&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;}&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;                &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;                sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n 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:#e6db74&#34;&gt;                    rollout status deployment/order-svc --timeout=300s&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;// 冒烟测试：走内网 SLB 打健康检查和关键接口
&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;curl -fsS --retry 5 --retry-delay 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;                    http://order-svc-internal.prod.svc.cluster.local/healthz || exit 1&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;}&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;    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;            &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;            sh &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;kubectl --kubeconfig=$KUBECONFIG -n 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:#e6db74&#34;&gt;                rollout undo deployment/order-svc || 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:#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;h3 id=&#34;125-验证环节的正确姿势&#34;&gt;12.5 「验证」环节的正确姿势&lt;/h3&gt;
&lt;p&gt;很多流水线只做到 &lt;code&gt;kubectl apply&lt;/code&gt; 就算「发布成功」，这是自欺欺人。&lt;code&gt;apply&lt;/code&gt; 返回成功只代表 apiserver 收下了 YAML，Pod 起不起得来是另一回事。可靠的验证链：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;rollout status 阻塞等待&lt;/strong&gt;：确认所有新副本 ready（背后靠的就是 readinessProbe——再次体现探针的重要性）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;冒烟测试&lt;/strong&gt;：对真实入口打 &lt;code&gt;/healthz&lt;/code&gt; 和一两个核心业务接口，确认「能起来」且「能干活」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;失败自动回滚&lt;/strong&gt;：&lt;code&gt;rollout undo&lt;/code&gt; 依赖旧 ReplicaSet 还在（&lt;code&gt;revisionHistoryLimit&lt;/code&gt; 别设 0）且镜像 tag 可追溯——这就是前面说「不要用 latest」的根本原因。&lt;/li&gt;
&lt;li&gt;更严谨的做法是&lt;strong&gt;灰度验证&lt;/strong&gt;：先用 Istio 把 5% 流量切到新版本观察指标，确认无误再全量（见第九章）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;126-cicd-避坑清单&#34;&gt;12.6 CI/CD 避坑清单&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;镜像 tag 必须唯一可追溯&lt;/strong&gt;：git commit 短哈希或构建号，杜绝 &lt;code&gt;latest&lt;/code&gt;，否则回滚时根本不知道该回滚到哪个镜像。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;凭据最小化 + 分环境&lt;/strong&gt;：dev/staging/prod 用不同的 SA 和 Jenkins 凭据，prod 的部署动作加人工审批（Jenkins 的 &lt;code&gt;input&lt;/code&gt; 步骤）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;声明式优先&lt;/strong&gt;：服务变多后，把 &lt;code&gt;kubectl set image&lt;/code&gt; 换成 Helm/Kustomize（YAML 也进 Git 仓库，可审计可 diff）；再进一步上 &lt;strong&gt;GitOps（ArgoCD/Flux）&lt;/strong&gt;——Jenkins 只负责构建推镜像，部署由集群内的 ArgoCD 从 Git 仓库自动同步，Jenkins 不再需要生产集群的任何凭证，安全性大幅提升。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jenkins 到集群的网络&lt;/strong&gt;：kubectl 走内网 API Server 端点；如果 Jenkins 在另一个 VPC，用云企业网/对等连接打通，不要把 API Server 暴露公网给 Jenkins 用。&lt;/li&gt;
&lt;li&gt;构建产物（镜像）与部署配置分离管理，避免「同一个 tag 被覆盖推送」导致回滚失效——ACR 里给 prod 仓库开&lt;strong&gt;镜像 tag 不可变&lt;/strong&gt;（immutable）开关。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;十三学习路线建议&#34;&gt;十三、学习路线建议&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;第一周&lt;/strong&gt;：本地装 minikube 或 kind，把 Deployment/Service/ConfigMap 各玩一遍，亲手制造一次 CrashLoopBackOff 并修好它。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第二周&lt;/strong&gt;：搞懂网络——Pod 网络、Service 转发（iptables）、Ingress、NetworkPolicy 内外网隔离，配合 &lt;code&gt;tcpdump&lt;/code&gt;/&lt;code&gt;ss&lt;/code&gt; 在容器里抓包看真实流量。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第三周&lt;/strong&gt;：上云。开一套阿里云 ACK（按量付费，玩完就删），跑通「ACR 推镜像 → ACK 部署 → ALB Ingress 暴露 → NAS 挂共享盘 → SLS 看日志」全流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;第四周&lt;/strong&gt;：打通 CI/CD。用 Jenkins + 最小权限 SA 实现「推代码 → 自动构建 → 自动部署 → rollout 验证 → 失败回滚」完整流水线。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;进阶&lt;/strong&gt;：Helm 包管理、HPA 自动扩缩容、Istio 灰度发布、GitOps（ArgoCD）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后送一句《Kubernetes 权威指南》反复强调的心法：&lt;strong&gt;K8s 的一切都是声明式的——你描述「我要什么」，控制器负责「做到」&lt;/strong&gt;。排查问题时永远先问：期望状态（YAML/Spec）是什么？实际状态（Status/Events）是什么？两者的差距就是答案所在。&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;参考资料：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《Kubernetes 权威指南：从 Docker 到 Kubernetes 实践全接触（第 4 版）》龚正 等&lt;/li&gt;
&lt;li&gt;《Kubernetes in Action》Marko Lukša&lt;/li&gt;
&lt;li&gt;《Unix 网络编程 卷1》W. Richard Stevens（网络排障内功）&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://kubernetes.io/zh-cn/docs/&#34;&gt;Kubernetes 官方文档&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://help.aliyun.com/product/85222.html&#34;&gt;阿里云 ACK 文档&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>
