<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>多线程 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/</link><description>Recent content in 多线程 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 07 Mar 2026 11:39:53 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/index.xml" rel="self" type="application/rss+xml"/><item><title>OpenAI 开源 Symphony：AI 不再只是写代码，而是开始接管“工作流”</title><link>https://xiaobox.github.io/p/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/</link><pubDate>Sat, 07 Mar 2026 11:39:53 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/cover.jpg" alt="Featured image of post OpenAI 开源 Symphony：AI 不再只是写代码，而是开始接管“工作流”" /&gt;&lt;p&gt;最近跟业界一些朋友交流，不少公司正在做内部软件开发的 &lt;strong&gt;AI 自动化流程系统&lt;/strong&gt;，正好这两天，OpenAI 在 GitHub 上低调开源了一个很值得认真看的项目：&lt;strong&gt;Symphony&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/001-29d66690.png"&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/002-23143dde.png"&gt;&lt;/p&gt;
&lt;p&gt;如果只看名字，你很容易把它理解成“又一个多 Agent 编排框架”；但只要认真读完 README、SPEC.md 和参考实现里的 WORKFLOW.md，你会发现它真正想解决的，根本不是“让 AI 会写代码”，而是另一件更大的事：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何把软件研发中的“工作”，交给一套可以持续运行、可隔离、可调度、可回收、可观测的系统去推进。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就是 Symphony 最重要的定位。官方原话非常值得细品：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;它会把项目工作转成 isolated, autonomous implementation runs，让团队从“监督 coding agents”转向“管理 work”。README 里的 demo 也很直白：Symphony 盯着 Linear 看板拿任务，拉起 agent 处理 issue，回传 CI 状态、PR review 反馈、复杂度分析和 walkthrough 视频，最后在被接受后安全落 PR。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;很多人第一次看到这里，会本能地把它和 Copilot、Cursor、Claude Code 之类工具放在一起比较。但我觉得，真正准确的比较方式不是“谁代码写得更强”，而是：谁更接近一个面向研发现场的执行系统。 Copilot 类产品解决的是“我写代码时，旁边有个聪明助手”；Symphony 想解决的是“我有一堆 issue，能不能让系统自己取单、分配环境、拉起 Agent、推进状态、处理失败、保留上下文，并把结果交回给我验收”。这已经不只是“辅助编码”，而是&lt;strong&gt;开始触碰软件交付流水线本身&lt;/strong&gt;。&lt;/p&gt;
&lt;h2 id="一symphony-到底是什么"&gt;&lt;a href="#%e4%b8%80symphony-%e5%88%b0%e5%ba%95%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;一、Symphony 到底是什么？
&lt;/h2&gt;&lt;p&gt;从 SPEC.md 看，Symphony 的定义非常清晰：它是一个 &lt;strong&gt;long-running automation service&lt;/strong&gt;。在当前规范版本里，它会持续从 issue tracker 读取工作（v1 里明确是 Linear），为每个 issue 创建独立 workspace，并在这个 workspace 里运行 coding agent session。规范还特别强调了它要解决的四类问题：&lt;/p&gt;
&lt;p&gt;1.把 issue 执行变成守护式工作流&lt;/p&gt;
&lt;p&gt;2.把每个任务隔离到独立 workspace&lt;/p&gt;
&lt;p&gt;3.把工作流策略放回 repo 内的 WORKFLOW.md&lt;/p&gt;
&lt;p&gt;4.以及为多任务并发运行提供足够的 observability。&lt;/p&gt;
&lt;p&gt;这段定义很重要，因为它一下子把 Symphony 和大量“Agent Demo”拉开了距离。它不是一个写几个 prompt、串几个工具的 toy project，也不是一个单轮任务脚本。它有轮询、有调度、有状态机、有重试退避、有 workspace 生命周期、有运行期事件、有恢复逻辑。换句话说，&lt;strong&gt;它的思维方式更像一个 orchestrator，而不是一个单纯的 agent wrapper。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更关键的是，SPEC 还专门写了一个“&lt;strong&gt;重要边界&lt;/strong&gt;”：Symphony 是 scheduler/runner 和 tracker reader。这句话很克制，也很专业。它的意思是，Symphony 的职责重点不是替你定义所有业务流程，而是负责任务编排、执行承载和状态协调；而 ticket 状态变更、评论、PR 链接等写操作，通常还是由 coding agent 借助工具在运行时完成。也就是说，&lt;strong&gt;它不是一个万能 PM 系统，而是一层面向软件交付的 agent orchestration 壳。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/003-d4fe840a.png"&gt;&lt;/p&gt;
&lt;h2 id="二它为什么比会写代码更进一步"&gt;&lt;a href="#%e4%ba%8c%e5%ae%83%e4%b8%ba%e4%bb%80%e4%b9%88%e6%af%94%e4%bc%9a%e5%86%99%e4%bb%a3%e7%a0%81%e6%9b%b4%e8%bf%9b%e4%b8%80%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;二、它为什么比“会写代码”更进一步？
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;因为真正麻烦的，从来不是 AI 能不能生成一段代码，而是几十个任务并行推进时，系统怎么不失控。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Symphony 在这方面做得非常工程化。它有明确的内部状态机：Unclaimed、Claimed、Running、RetryQueued、Released。它还定义了 run attempt 的阶段：准备 workspace、构建 prompt、拉起 agent 进程、初始化 session、流式执行 turn、结束、成功、失败、超时、卡死、被 reconciliation 取消。它甚至规定了每次 poll tick 到来时，先 reconciliation，再校验配置，再拉候选 issue，再按优先级分发，最后通知 observability 消费者。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/004-0d5ab841.png"&gt;&lt;/p&gt;
&lt;p&gt;这套设计背后的思想可以概括成一句话：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不要先问“怎么让 Agent 跑起来”，而要先问“怎么避免它跑重、跑错、跑飞”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;比如 candidate selection 里就有一条很像真实研发现场的规则：如果 issue 还处于 Todo，而它依赖的 blocker 还没进入终态，那就不要派发。排序也不是瞎来，而是按 priority、创建时间、issue 标识顺序稳定分发。失败之后也不是简单重试，而是区分正常退出后的短延迟 continuation retry 和异常退出后的指数退避重试。这样的设计，明显已经不是“写代码助手”的思路，而是“任务执行系统”的思路。&lt;/p&gt;
&lt;h2 id="三每个-issue-一个-workspace这是-symphony-最值钱的工程细节"&gt;&lt;a href="#%e4%b8%89%e6%af%8f%e4%b8%aa-issue-%e4%b8%80%e4%b8%aa-workspace%e8%bf%99%e6%98%af-symphony-%e6%9c%80%e5%80%bc%e9%92%b1%e7%9a%84%e5%b7%a5%e7%a8%8b%e7%bb%86%e8%8a%82" class="header-anchor"&gt;&lt;/a&gt;三、每个 issue 一个 workspace：这是 Symphony 最值钱的工程细节
&lt;/h2&gt;&lt;p&gt;如果你只让我挑 Symphony 里最关键的一点，我会选这个：&lt;strong&gt;per-issue workspace。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;SPEC 写得非常清楚：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;每个 issue 的 workspace 路径都必须位于配置的 workspace root 之下；coding agent 只能在该 issue 的 workspace 里执行；workspace 目录名必须净化；还支持 after_create、before_run、after_run、before_remove 等 hooks。工作区会跨运行复用，但终态 issue 可以在启动或状态变更时清理。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;**为什么这个设计这么重要？**因为一旦没有隔离，Agent 系统很快就会碰到三个问题：&lt;strong&gt;上下文污染、任务互相踩踏、失败后难以恢复&lt;/strong&gt;。Symphony 的思路很像给每个工单都分配一个独立工位，Agent 只能在自己的工位里思考、改代码、跑测试、记录状态。哪怕它中途失败了，下次重试回来，也可以在同一个 workspace 上继续，而不是重新失忆。&lt;/p&gt;
&lt;p&gt;这也是为什么我说 Symphony 更接近“工程执行系统”而不是“聊天式 Agent”。聊天系统强调对话连续；Symphony 强调的是 任务连续性。这两个东西，根本不是一个层级。&lt;/p&gt;
&lt;h2 id="四workflowmd-才是灵魂把-prompt-升级成-repo-内契约"&gt;&lt;a href="#%e5%9b%9bworkflowmd-%e6%89%8d%e6%98%af%e7%81%b5%e9%ad%82%e6%8a%8a-prompt-%e5%8d%87%e7%ba%a7%e6%88%90-repo-%e5%86%85%e5%a5%91%e7%ba%a6" class="header-anchor"&gt;&lt;/a&gt;四、WORKFLOW.md 才是灵魂：把 Prompt 升级成 repo 内契约
&lt;/h2&gt;&lt;p&gt;Symphony 很聪明的一点，是它没有把流程硬编码进平台，而是把策略收回到仓库里。SPEC 规定 WORKFLOW.md 由 YAML front matter 和 Markdown prompt body 组成，运行时会解析出 config 与 prompt template；很多核心行为——轮询间隔、workspace root、并发限制、hooks、agent 参数——都来自这份 repo-owned contract。&lt;/p&gt;
&lt;p&gt;参考实现里的 WORKFLOW.md 更是把这种思想写得非常彻底。它规定了 issue 在不同状态下该怎么流转：&lt;/p&gt;
&lt;p&gt;●Todo 要立即切到 In Progress，然后找或建唯一的 ## Codex Workpad 评论，再开始分析与实现；&lt;/p&gt;
&lt;p&gt;●Human Review 阶段不再改代码，只轮询 review 结果；&lt;/p&gt;
&lt;p&gt;●进入 Merging 后必须走专门的 land 技能，不能直接 gh pr merge。文档还要求把单个 workpad comment 当作进度和交接的唯一真相源，&lt;strong&gt;并且把 out-of-scope 改进拆成新的 Backlog issue，而不是在当前任务里偷偷扩 scope。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-07-openai-kai-yuan-symphony-ai-bu-zai-zhi-shi-xie-dai-ma-er-shi/005-d06eb76f.png"&gt;&lt;/p&gt;
&lt;p&gt;这件事的意义非常大。&lt;strong&gt;它意味着团队以后真正需要打磨的，不只是“怎么写 prompt”，而是“怎么把流程、约束、验收标准、状态流转、回退机制，写成一份和代码一起版本化的工程契约”。这比 prompt engineering 更接近组织能力。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="五为什么参考实现偏偏选了-elixir"&gt;&lt;a href="#%e4%ba%94%e4%b8%ba%e4%bb%80%e4%b9%88%e5%8f%82%e8%80%83%e5%ae%9e%e7%8e%b0%e5%81%8f%e5%81%8f%e9%80%89%e4%ba%86-elixir" class="header-anchor"&gt;&lt;/a&gt;五、为什么参考实现偏偏选了 Elixir？
&lt;/h2&gt;&lt;p&gt;这不是噱头，反而是我觉得 Symphony 最有工程味的地方之一。&lt;/p&gt;
&lt;p&gt;GitHub 仓库当前语言分布里，Elixir 约占 &lt;strong&gt;94.9%&lt;/strong&gt;；README 也直接写了 &lt;strong&gt;Why Elixir?&lt;/strong&gt;：因为 Elixir 运行在 Erlang/BEAM/OTP 之上，很适合监督长时间运行的进程，并且支持在不停止活跃 subagents 的情况下做 &lt;strong&gt;hot code reloading&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这和 Symphony 的问题形态是高度匹配的。一个普通 Web 请求可能几十毫秒就结束，但一个 coding agent 处理复杂任务时，可能会持续很久，还要接收事件、处理重试、维持会话、更新状态、暴露观测数据。BEAM/OTP 擅长的，恰好就是这种长生命周期、并发多、失败要可控隔离的系统。OpenAI 官方没有在 README 里展开讲 supervision tree 这些词，但它给出的理由已经足够说明方向：&lt;strong&gt;Symphony 不是在追求“AI 生态默认语言”，而是在追求“最适合承载 agent orchestration 的运行时”。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="六真正的前提不是更强模型而是-harness-engineering"&gt;&lt;a href="#%e5%85%ad%e7%9c%9f%e6%ad%a3%e7%9a%84%e5%89%8d%e6%8f%90%e4%b8%8d%e6%98%af%e6%9b%b4%e5%bc%ba%e6%a8%a1%e5%9e%8b%e8%80%8c%e6%98%af-harness-engineering" class="header-anchor"&gt;&lt;/a&gt;六、真正的前提不是更强模型，而是 Harness Engineering
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;如果说 Symphony 讲的是“如何调度 Agent”，那 Harness Engineering 讲的就是“怎样让 Agent 值得被调度”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;OpenAI 在官方文章里把这件事说得很重：他们构建的产品里，应用逻辑、测试、CI、文档、可观测性和内部工具，全部由 Codex 写出；而人类工程师的角色，从直接写代码，转向设计环境、明确意图、构建反馈回路。文章里那句“Humans steer. Agents execute.”，几乎可以看作整个 Symphony 时代的软件工程宣言。&lt;/p&gt;
&lt;p&gt;也正因如此，README 才会明确写：Symphony 最适合已经采用 harness engineering 的代码库。意思很简单：如果你的仓库没有可靠测试、没有清晰边界、没有稳定构建入口、没有可验证的反馈回路，那么再强的 Agent 也只是更快地在混乱里打转。&lt;strong&gt;Symphony 的价值，不是替代工程纪律；恰恰相反，它会把工程纪律的重要性放大十倍。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="七它的边界也必须讲清楚"&gt;&lt;a href="#%e4%b8%83%e5%ae%83%e7%9a%84%e8%be%b9%e7%95%8c%e4%b9%9f%e5%bf%85%e9%a1%bb%e8%ae%b2%e6%b8%85%e6%a5%9a" class="header-anchor"&gt;&lt;/a&gt;七、它的边界也必须讲清楚
&lt;/h2&gt;&lt;p&gt;一个成熟的技术判断，不能只讲想象力，不讲边界。&lt;/p&gt;
&lt;p&gt;Symphony 现在仍是一个工程预览版，README 明确写了适用于 trusted environments；SPEC 也写了 approval policy、sandbox policy、operator confirmation posture 都是 implementation-defined，不同实现可以高信任，也可以更严格。它当前规范版本只定义了 Linear 作为 tracker，至于更多 issue tracker 适配器，还是 TODO。参考实现虽然带可选 Phoenix observability 服务和 JSON API，但整个项目还远没到“所有团队直接开箱上生产”的阶段。&lt;/p&gt;
&lt;p&gt;所以，最稳妥的结论不是“研发彻底无人化已经到来”，而是：&lt;/p&gt;
&lt;p&gt;OpenAI 正在把 AI Coding 从“单点能力演示”推进到“工程系统形态演示”。&lt;/p&gt;
&lt;p&gt;这一步，比单纯再出一个更强的代码模型，更值得关注。&lt;/p&gt;
&lt;h2 id="结语"&gt;&lt;a href="#%e7%bb%93%e8%af%ad" class="header-anchor"&gt;&lt;/a&gt;结语
&lt;/h2&gt;&lt;p&gt;如果一定要用一句话概括 Symphony，我会这样说：&lt;/p&gt;
&lt;p&gt;它不是在教 Agent 如何写代码，而是在教团队如何把“软件交付”本身改写成一套可执行、可编排、可观测的系统。&lt;/p&gt;
&lt;p&gt;过去，AI 是工程师的副驾驶；现在，Symphony 展示的是另一种可能：工程师不再盯着每一行代码，而是站到更高一层，去设计流程、约束环境、设定验收标准，然后管理一批持续运行的 agent 去推进工作。&lt;strong&gt;真正的变化，不是“AI 会不会写 CRUD”，而是“软件组织会不会因此改写自己的工作方式”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这，才是 Symphony 最值得认真读的地方。&lt;/p&gt;</description></item><item><title>动画演示 Node.js 的事件循环</title><link>https://xiaobox.github.io/p/2026-03-03-dong-hua-yan-shi-node-js-de-shi-jian-xun-huan/</link><pubDate>Tue, 03 Mar 2026 04:38:15 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-03-03-dong-hua-yan-shi-node-js-de-shi-jian-xun-huan/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-03-03-dong-hua-yan-shi-node-js-de-shi-jian-xun-huan/cover.jpg" alt="Featured image of post 动画演示 Node.js 的事件循环" /&gt;&lt;p&gt;如果把 Node.js 比作一家快餐店：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;主线程（前台收银员）：&lt;/strong&gt; 只有一个人。他负责接待顾客点单（执行同步代码）。他动作很快，绝不离开柜台。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;异步任务（复杂的后厨订单）：&lt;/strong&gt; 顾客点了需要烤 20 分钟的披萨（读取大文件）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;libuv/底层系统（后厨团队/烤箱）：&lt;/strong&gt; 收银员把订单贴到后厨窗口就继续接待下一位顾客了。后厨团队（线程池）或者烤箱（系统内核）开始默默干活。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Callback Queue（出餐台）：&lt;/strong&gt; 披萨烤好了，后厨把它放到出餐台，并附上小票“3号桌的披萨好了”。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Event Loop（收银员的习惯动作）：&lt;/strong&gt; 收银员每服务完一个顾客（主线程空闲），就会下意识地回头看一眼出餐台。如果看到有做好的餐，就把它端给顾客（执行回调函数）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>一文讲透 GoF 的 23 种设计模式之单例</title><link>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</link><pubDate>Wed, 25 Feb 2026 10:13:49 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/cover.jpg" alt="Featured image of post 一文讲透 GoF 的 23 种设计模式之单例" /&gt;&lt;h1 id="一文讲透-gof-的-23-种设计模式之单例"&gt;&lt;a href="#%e4%b8%80%e6%96%87%e8%ae%b2%e9%80%8f-gof-%e7%9a%84-23-%e7%a7%8d%e8%ae%be%e8%ae%a1%e6%a8%a1%e5%bc%8f%e4%b9%8b%e5%8d%95%e4%be%8b" class="header-anchor"&gt;&lt;/a&gt;一文讲透 GoF 的 23 种设计模式之单例
&lt;/h1&gt;&lt;p&gt;单例模式&amp;ndash;Singleton 是创建型模式&lt;/p&gt;
&lt;h2 id="定义"&gt;&lt;a href="#%e5%ae%9a%e4%b9%89" class="header-anchor"&gt;&lt;/a&gt;定义
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;确保一个类在一个 JVM 内只有一个实例，并提供全局访问点&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/001-d2d7932d.png"&gt;&lt;/p&gt;
&lt;h2 id="什么时候用"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%97%b6%e5%80%99%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;什么时候用?
&lt;/h2&gt;&lt;p&gt;●配置中心、缓存管理器、日志器（有时）&lt;/p&gt;
&lt;p&gt;●需要全局共享状态/资源&lt;/p&gt;
&lt;p&gt;对于 那些初始化很贵，重复创建又特别浪费资源的场景非常合适 。&lt;/p&gt;
&lt;h2 id="不要滥用"&gt;&lt;a href="#%e4%b8%8d%e8%a6%81%e6%bb%a5%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;不要滥用
&lt;/h2&gt;&lt;p&gt;单例本质是“全局变量 + 访问入口”，会增加耦合、影响测试&lt;/p&gt;
&lt;h2 id="实现方式"&gt;&lt;a href="#%e5%ae%9e%e7%8e%b0%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;实现方式
&lt;/h2&gt;&lt;p&gt;以下为常见的 5 种实现方式对比。&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;th&gt;核心易错点 / 致命缺陷&lt;/th&gt;
 &lt;th&gt;综合推荐度&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1. 饿汉式(Eager)&lt;/td&gt;
 &lt;td&gt;类加载时立即创建静态 final 实例。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高 (运行时)获取实例无锁。但可能会拖慢系统启动速度，且如果不用会浪费内存。&lt;/td&gt;
 &lt;td&gt;低实现简单，不易出错。缺点是无法进行懒加载，且难以传递动态参数进行初始化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2. 懒汉式(同步方法)&lt;/td&gt;
 &lt;td&gt;在 getInstance 方法上加 synchronized 锁。&lt;/td&gt;
 &lt;td&gt;安全(粗粒度锁保证)&lt;/td&gt;
 &lt;td&gt;非常低每次调用 getInstance 都要发生线程竞争和锁获取，高并发下是严重的性能瓶颈。&lt;/td&gt;
 &lt;td&gt;低实现简单。主要的&amp;quot;错&amp;quot;是选择了这种低效的方案。&lt;/td&gt;
 &lt;td&gt;⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3. 双重检查锁(DCL)&lt;/td&gt;
 &lt;td&gt;两次判空 + 同步代码块 + volatile 关键字。&lt;/td&gt;
 &lt;td&gt;安全 (有前提)必须在实例变量上加 volatile 禁止指令重排序。&lt;/td&gt;
 &lt;td&gt;高只在第一次初始化时加锁，后续调用无锁。实现了高性能的懒加载。&lt;/td&gt;
 &lt;td&gt;极高 (致命)最常见的错误是忘记加 volatile 关键字。这会导致多线程环境下，某个线程可能会拿到一个&amp;quot;半初始化&amp;quot;的对象，引发难以排查的 Bug。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4. 静态内部类(Holder模式)&lt;/td&gt;
 &lt;td&gt;利用 JVM 加载外部类时不加载静态内部类的特性实现懒加载。&lt;/td&gt;
 &lt;td&gt;安全(JVM类加载机制保证)&lt;/td&gt;
 &lt;td&gt;高既实现了懒加载，又在获取实例时没有任何锁机制，性能优异。&lt;/td&gt;
 &lt;td&gt;低非常规整的写法。唯一需要注意的是要确保构造函数私有，防止外部意外实例化。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (手动实现首选)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5. 枚举(Enum)&lt;/td&gt;
 &lt;td&gt;利用 Java 枚举类型的特殊语法和底层实现。&lt;/td&gt;
 &lt;td&gt;安全 (天然)(JVM 层面保障，防御反射和序列化攻击)&lt;/td&gt;
 &lt;td&gt;高类似于饿汉式，类加载时完成初始化，运行时无锁。&lt;/td&gt;
 &lt;td&gt;极低代码最简洁，几乎不可能写错。缺点是无法继承其他类，且在语义上用来做复杂业务对象时显得突兀。&lt;/td&gt;
 &lt;td&gt;⭐⭐⭐⭐⭐ (最安全简洁)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;重点说明两种实现方式：枚举和静态内部类。&lt;/p&gt;
&lt;h3 id="枚举"&gt;&lt;a href="#%e6%9e%9a%e4%b8%be" class="header-anchor"&gt;&lt;/a&gt;枚举
&lt;/h3&gt;&lt;p&gt;这是 Java 最简洁实现。Java 的 Enum 在语言层面有一些特殊保证（例如不会被克隆），这也是它常被用来实现单例的原因之一。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;enum&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;prod&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// test&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用枚举（enum）来实现单例模式，被《Effective Java》的作者 Joshua Bloch 称为 &lt;strong&gt;“实现单例模式的最佳方法”&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;它之所以备受推崇，是因为它用极其简洁的代码，完美解决了传统单例模式面临的线程安全、序列化破坏和反射破坏三大难题&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理一：利用 JVM 类加载机制保证“线程安全”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在传统的懒汉式单例中，为了保证多线程下只创建一个实例，我们需要写复杂的“双重检查锁（Double-Checked Locking）”并加上 volatile 关键字。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;当你定义 INSTANCE 时，编译器底层实际会把它转化为类似这样的代码：&lt;/p&gt;
&lt;p&gt;⚡ java片段&lt;code&gt;public static final AppConfig INSTANCE = new AppConfig();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Java 虚拟机（JVM）在加载类的时候，会利用底层的类加载机制保证静态成员的初始化是绝对线程安全的。在这个类被加载到内存时，JVM 会自动实例化 INSTANCE 且只实例化一次，整个过程由 JVM 内部加锁保证同步，不需要你手动写任何并发控制代码。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理二：天生防御“反射攻击”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例模式有一个致命弱点：恶意代码可以通过 Java 的反射机制（Reflection）把私有构造函数设置为可见（setAccessible(true)），从而强行 new 出新的实例，打破单例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 的反射 API 从源码级别就直接“封杀”了通过反射创建枚举实例的可能性。如果你去看 Constructor.newInstance() 的 Java 底层源码，会发现有一段明确的校验逻辑：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;java片段if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;clazz&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getModifiers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Modifier&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ENUM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Cannot reflectively create enum objects&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;也就是说，&lt;strong&gt;一旦 JVM 发现你要用反射去创建枚举类的对象，就会直接抛出异常&lt;/strong&gt;，从根本上杜绝了反射攻击。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原理三：天生防御“序列化破坏”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;传统的单例对象如果实现了 Serializable 接口，在进行网络传输或持久化到磁盘再反序列化读取回来时，默认会重新分配内存，生成一个全新的对象。传统做法是必须手动写一个 readResolve() 方法来返回原实例。&lt;/p&gt;
&lt;p&gt;而枚举怎么做的？&lt;/p&gt;
&lt;p&gt;Java 规范对枚举的序列化有特殊的规定。枚举在序列化的时候，仅仅是将枚举常量的名称（name）输出到了结果中；在反序列化的时候，Java 会调用 java.lang.Enum.valueOf() 方法，通过名字去查找并返回内存中已经存在的那个常量对象。&lt;/p&gt;
&lt;p&gt;因此，无论你反序列化多少次，拿到的永远是内存里的同一个 INSTANCE 对象。&lt;/p&gt;
&lt;p&gt;总结来说：枚举单例的核心原理就是 &lt;strong&gt;直接利用 Java 语言底层的机制&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;●用 JVM 类加载机制 搞定了线程安全。&lt;/p&gt;
&lt;p&gt;●用 反射 API 的硬编码拦截 搞定了反射破坏。&lt;/p&gt;
&lt;p&gt;●用 特殊的名称匹配机制 搞定了序列化破坏。&lt;/p&gt;
&lt;p&gt;在理论上，枚举单例确实是“最完美”的单例实现；但在实际的工程代码中，它的出场率确实不高。这并不是因为枚举本身有 bug，而是因为它在现代工程架构、面向对象设计理念以及测试友好度上，存在一些不可避免的局限性&lt;/p&gt;
&lt;p&gt;具体来说，有以下几个核心原因：&lt;/p&gt;
&lt;p&gt;1.&lt;strong&gt;现代框架（如 Spring）接管了单例的管理&lt;/strong&gt; 这是最根本的原因。在现代 Java 工程中（尤其是企业级开发），我们几乎不再手动编写任何单例模式了。 我们广泛使用 Spring/Spring Boot 这样的依赖注入（DI）框架。在 Spring 中，你只需要在一个普通的类上加上 @Service、@Component 或 @Configuration 注解，Spring 容器（IoC Container）就会默认将其作为一个单例来管理。框架不仅帮你保证了单例，还能帮你自动注入其他依赖（如数据库连接、其他服务），这比用枚举手写单例要强大、灵活得多。&lt;/p&gt;
&lt;p&gt;2.&lt;strong&gt;违反了“语义”和开发者的直觉&lt;/strong&gt; 代码不仅是给机器运行的，更是给人读的。 枚举的本来语义：代表一组固定的常量集合（如星期、颜色、订单状态）。单例的语义：通常是一个拥有复杂业务逻辑的管理类（如 UserManager、DatabaseConnectionPool）。&lt;/p&gt;
&lt;p&gt;如果把一个复杂的业务服务写成 enum，会让接手代码的其他开发者感到困惑，这违反了“最小惊讶原则（Principle of Least Astonishment）”。感觉就像是“为了用单例模式而强行用枚举”。&lt;/p&gt;
&lt;p&gt;3.&lt;strong&gt;面向对象特性的缺失（无法继承）&lt;/strong&gt; Java 规定，所有的枚举类都隐式继承了 java.lang.Enum。因为 Java 不支持多重继承，这意味着你的枚举单例不能再继承任何其他的父类。 如果你的架构需要 AppConfig 继承一个 BaseConfig 类来复用代码，枚举单例直接就做不到。 虽然枚举可以实现接口（implements Interface），但在需要共享基类代码的场景下，它的表现非常无力。&lt;/p&gt;
&lt;p&gt;4.&lt;strong&gt;传参初始化非常困难&lt;/strong&gt; 在工程实践中，单例对象在初始化时往往需要外部参数。比如，一个数据库连接池单例，在启动时需要读取配置文件里的 url 和 password。 普通的单例模式或 Spring 管理的 Bean，可以在运行时读取配置后，再进行初始化。 枚举常量的实例化是在类加载的最早期进行的，这个时候你很难把运行时的参数优雅地传递给枚举的构造函数。&lt;/p&gt;
&lt;p&gt;5.&lt;strong&gt;极难进行单元测试（Mock）&lt;/strong&gt; 在做单元测试时，我们经常需要把某些依赖的单例对象“Mock（模拟）”掉（比如使用 Mockito），以隔离测试环境。 普通类别的单例很容易被 Mock 框架替换。但是，枚举是静态的全局常量，它的生命周期和类加载器绑定。在测试中强行替换枚举实例极其困难，容易导致测试用例之间互相污染。&lt;/p&gt;
&lt;p&gt;在实际工程中：&lt;/p&gt;
&lt;p&gt;●如果你要写一个完全无状态、不需要继承、不依赖外部配置的纯工具类/简单配置类，用枚举单例确实不错。&lt;/p&gt;
&lt;p&gt;●但对于包含业务逻辑、需要依赖注入、需要被测试的类，交给 Spring 等框架去管理才是工业界的最佳实践。&lt;/p&gt;
&lt;h3 id="静态内部类"&gt;&lt;a href="#%e9%9d%99%e6%80%81%e5%86%85%e9%83%a8%e7%b1%bb" class="header-anchor"&gt;&lt;/a&gt;静态内部类
&lt;/h3&gt;&lt;p&gt;如果你不想用枚举，又想要一个&lt;strong&gt;既能延迟加载（懒汉式），又绝对线程安全，还能完美避开繁琐的加锁（synchronized）&lt;/strong&gt; 的单例，静态内部类是最佳选择。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;java片段public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 1. 私有化构造函数，防止外部 new&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 可选：在这里加上防御反射攻击的代码&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RuntimeException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;不允许通过反射创建单例！&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 2. 核心：定义一个私有的静态内部类&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这个类直到被调用时才会被 JVM 加载&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SingletonHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 由 JVM 保证这里的实例化是绝对线程安全的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;INSTANCE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 3. 提供全局访问点&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DatabaseConnectionPool&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 只有在调用这里时，SingletonHolder 才会被加载，从而实例化 INSTANCE&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SingletonHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;INSTANCE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么它很巧妙？&lt;/p&gt;
&lt;p&gt;●懒加载（Lazy Loading）：当你加载 DatabaseConnectionPool 这个类时，内部类 SingletonHolder 并不会被立刻加载。只有当你真正调用 getInstance() 方法时，内部类才会被加载，对象才会被创建。这就节省了内存。&lt;/p&gt;
&lt;p&gt;●零并发负担：它没有使用任何 synchronized 或者 volatile 关键字。它完全将线程安全的控制权交给了 JVM 底层的类加载机制（JVM 在加载一个类时，会自动加锁保证全局唯一）。&lt;/p&gt;
&lt;h2 id="spring-是如何实现单例的"&gt;&lt;a href="#spring-%e6%98%af%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e5%8d%95%e4%be%8b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;Spring 是如何实现单例的？
&lt;/h2&gt;&lt;p&gt;Spring 里的单例（Singleton）和我们在《设计模式》书里学到的单例，在概念和实现思路上有很大的不同。&lt;/p&gt;
&lt;p&gt;●传统单例（GoF单例）：保证在一个 JVM（准确地说是类加载器）级别，某个类只有一个实例。类自己控制自己的实例化。&lt;/p&gt;
&lt;p&gt;●Spring 单例：保证在一个 Spring IoC 容器（ApplicationContext）内部，某个指定的 Bean 名称只有一个实例。它是由 Spring 框架来统一管理的。&lt;/p&gt;
&lt;p&gt;Spring 实现单例的核心原理可以概括为：&lt;strong&gt;单例注册表（Singleton Registry）&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="1-核心数据结构concurrenthashmap"&gt;&lt;a href="#1-%e6%a0%b8%e5%bf%83%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84concurrenthashmap" class="header-anchor"&gt;&lt;/a&gt;1. 核心数据结构：ConcurrentHashMap
&lt;/h3&gt;&lt;p&gt;如果你翻开 Spring 的底层源码（DefaultSingletonBeanRegistry 类），你会发现 Spring 管理单例的本质，就是一个大大的缓存 Map：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;⚡ java片段// Spring 源码中的 &amp;#34;一级缓存&amp;#34;，存放所有完全初始化好的单例 Bean
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;private final Map&amp;lt;String, Object&amp;gt; singletonObjects = new ConcurrentHashMap&amp;lt;&amp;gt;(256);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Spring 的单例其实就是把创建好的对象塞进了一个线程安全的 ConcurrentHashMap 里。Key 是 Bean 的名字（通常是类名首字母小写），Value 就是这个类的实例对象。&lt;/p&gt;
&lt;h3 id="2-spring-创建单例的流程"&gt;&lt;a href="#2-spring-%e5%88%9b%e5%bb%ba%e5%8d%95%e4%be%8b%e7%9a%84%e6%b5%81%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;2. Spring 创建单例的流程
&lt;/h3&gt;&lt;p&gt;当你在代码里注入一个单例（比如通过 @Autowired），或者调用 context.getBean(&amp;ldquo;myService&amp;rdquo;) 时，Spring 大致会经历以下步骤：&lt;/p&gt;
&lt;p&gt;1.查缓存：Spring 首先会去 singletonObjects 这个 Map 里查，看看有没有叫 &amp;ldquo;myService&amp;rdquo; 的对象。&lt;/p&gt;
&lt;p&gt;2.有则返回：如果 Map 里有，说明已经创建过了，直接把这个对象返回给你。这就是单例的体现。&lt;/p&gt;
&lt;p&gt;3.无则创建并加锁：如果 Map 里没有，Spring 就会准备创建它。为了保证在多线程环境下只有一个线程能去创建这个 Bean，Spring 会对这个 Bean 的名字进行加锁（通常是通过对全局单例集合的锁或者特定的互斥锁来实现同步）。&lt;/p&gt;
&lt;p&gt;4.实例化与初始化：Spring 通过反射调用构造函数把对象 new 出来，然后进行属性填充（依赖注入），再调用 @PostConstruct 等初始化方法。&lt;/p&gt;
&lt;p&gt;5.放入 Map 并返回：最后，把完全准备好的对象放进 singletonObjects 这个 ConcurrentHashMap 里，然后返回给你。以后所有对这个 Bean 的请求，都直接从 Map 里拿。&lt;/p&gt;
&lt;h3 id="3-补充循环依赖的杀手锏三级缓存"&gt;&lt;a href="#3-%e8%a1%a5%e5%85%85%e5%be%aa%e7%8e%af%e4%be%9d%e8%b5%96%e7%9a%84%e6%9d%80%e6%89%8b%e9%94%8f%e4%b8%89%e7%ba%a7%e7%bc%93%e5%ad%98" class="header-anchor"&gt;&lt;/a&gt;3. 补充：循环依赖的杀手锏“三级缓存”
&lt;/h3&gt;&lt;p&gt;Spring 在管理单例时，还要解决一个传统单例很难解决的问题——循环依赖（比如 A 依赖 B，B 又依赖 A）。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，Spring 其实并没有只用一个 Map，而是用了三个 Map（传说中的三级缓存）：&lt;/p&gt;
&lt;p&gt;●一级缓存（singletonObjects）：存完整的、可用的单例对象。&lt;/p&gt;
&lt;p&gt;●二级缓存（earlySingletonObjects）：存半成品对象（刚 new 出来，但还没注入属性的对象），用于提前暴露自己，打破循环。&lt;/p&gt;
&lt;p&gt;●三级缓存（singletonFactories）：存对象工厂，用于在需要时生成代理对象（比如处理 AOP 切面）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-02-25-yi-wen-jiang-tou-gof-de-23-zhong-she-ji-mo-shi-zhi-dan-li/002-5ae19680.svg"&gt;&lt;/p&gt;
&lt;p&gt;结合上面的图，核心过程如下：&lt;/p&gt;
&lt;p&gt;第一阶段：A 的创建与曝光&lt;/p&gt;
&lt;p&gt;1.调用 getBean(A)：Spring 容器开始创建 Bean A。&lt;/p&gt;
&lt;p&gt;2.实例化 A：调用构造函数，A 对象在内存中诞生，但属性（如 B）还是 null。&lt;/p&gt;
&lt;p&gt;3.暴露三级缓存：Spring 将 A 的工厂对象放入 三级缓存 (singletonFactories)。这是解决循环依赖的关键一步，意味着此时如果有其他对象引用 A，可以通过这个工厂拿到 A 的引用。&lt;/p&gt;
&lt;p&gt;第二阶段：A 填充属性，触发 B 的创建&lt;/p&gt;
&lt;p&gt;4.填充属性 B：A 发现自己依赖 B，于是暂停自己，转而去创建 B。&lt;/p&gt;
&lt;p&gt;第三阶段：B 的创建与获取 A&lt;/p&gt;
&lt;p&gt;5.实例化 B：B 对象诞生，属性（如 A）还是 null。&lt;/p&gt;
&lt;p&gt;6.暴露三级缓存：将 B 的工厂放入三级缓存。&lt;/p&gt;
&lt;p&gt;7.填充属性 A：B 发现自己依赖 A，于是尝试去缓存找 A。&lt;/p&gt;
&lt;p&gt;第四阶段：B 从缓存中找到 A (核心转折)&lt;/p&gt;
&lt;p&gt;8.查找缓存：&lt;/p&gt;
&lt;p&gt;●找一级缓存？没有（A 还没彻底完工）。&lt;/p&gt;
&lt;p&gt;●找二级缓存？没有（还没人提取过 A 的早期引用）。&lt;/p&gt;
&lt;p&gt;●找三级缓存？有了！&lt;/p&gt;
&lt;p&gt;9.升级缓存：&lt;/p&gt;
&lt;p&gt;●B 调用三级缓存中的工厂方法，拿到 A 的早期引用。&lt;/p&gt;
&lt;p&gt;●重点：如果 A 配置了 AOP（比如事务管理），这个工厂会提前生成 A 的代理对象。&lt;/p&gt;
&lt;p&gt;●将 A 的早期引用放入 二级缓存 (earlySingletonObjects)，并从三级缓存移除。&lt;/p&gt;
&lt;p&gt;10.B 完成：B 拿到了 A 的引用，完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;
&lt;p&gt;第五阶段：A 完成&lt;/p&gt;
&lt;p&gt;11.A 获取 B：B 已经创建好了，A 顺利拿到 B 的引用。&lt;/p&gt;
&lt;p&gt;12.A 完成：A 完成属性填充和初始化，放入 一级缓存。&lt;/p&gt;</description></item><item><title>AI 模型推理平台架构设计与实践</title><link>https://xiaobox.github.io/p/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/</link><pubDate>Sun, 30 Nov 2025 04:11:10 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/cover.jpg" alt="Featured image of post AI 模型推理平台架构设计与实践" /&gt;&lt;h1 id="一背景"&gt;&lt;a href="#%e4%b8%80%e8%83%8c%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;一、背景
&lt;/h1&gt;&lt;h2 id="为什么要搞推理平台"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e6%90%9e%e6%8e%a8%e7%90%86%e5%b9%b3%e5%8f%b0" class="header-anchor"&gt;&lt;/a&gt;为什么要搞推理平台
&lt;/h2&gt;&lt;p&gt;从实用的角度讲，搞推理平台的目的就是为了给部署、运行、维护模型打造一个良好的 “环境”。&lt;/p&gt;
&lt;h2 id="为什么要自己部署运行维护模型呢-全部用-api-不行吗"&gt;&lt;a href="#%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e8%87%aa%e5%b7%b1%e9%83%a8%e7%bd%b2%e8%bf%90%e8%a1%8c%e7%bb%b4%e6%8a%a4%e6%a8%a1%e5%9e%8b%e5%91%a2-%e5%85%a8%e9%83%a8%e7%94%a8-api-%e4%b8%8d%e8%a1%8c%e5%90%97" class="header-anchor"&gt;&lt;/a&gt;为什么要自己部署、运行、维护模型呢？ 全部用 API 不行吗？
&lt;/h2&gt;&lt;p&gt;这个问题涉及到&lt;strong&gt;模型的功能分化&lt;/strong&gt;。简单来讲，传统的 LLM 基座模型是很强，类似全能型选手，但在企业落地场景下并不完全适用。企业需要的是 ROI 极高的方案，企业场景下会考虑并发、延迟、成本等非常具体的指标。所以用满足单一场景且成本极低的小模型 + 基座大模型是比较务实的选择。&lt;/p&gt;
&lt;h2 id="一定要有-gpu-显卡资源吗"&gt;&lt;a href="#%e4%b8%80%e5%ae%9a%e8%a6%81%e6%9c%89-gpu-%e6%98%be%e5%8d%a1%e8%b5%84%e6%ba%90%e5%90%97" class="header-anchor"&gt;&lt;/a&gt;一定要有 GPU （显卡资源）吗？
&lt;/h2&gt;&lt;p&gt;不一定，有些模型在 CPU 也跑的很好。 比如 all-MiniLM-L6-v2，但绝大多数模型是需要 GPU 的。&lt;/p&gt;
&lt;h1 id="二资源规划与集群架构"&gt;&lt;a href="#%e4%ba%8c%e8%b5%84%e6%ba%90%e8%a7%84%e5%88%92%e4%b8%8e%e9%9b%86%e7%be%a4%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;二、资源规划与集群架构
&lt;/h1&gt;&lt;p&gt;我们假设你的生产环境是如下图所示的 K8S 集群环境&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/001-35230c44.png"&gt;&lt;/p&gt;
&lt;h2 id="不止-k8s"&gt;&lt;a href="#%e4%b8%8d%e6%ad%a2-k8s" class="header-anchor"&gt;&lt;/a&gt;不止 k8s
&lt;/h2&gt;&lt;p&gt;对于一个 “&lt;strong&gt;模型在线推理平台（Serving 平台）&lt;/strong&gt;”，光靠 k8s 是不够的。&lt;/p&gt;
&lt;p&gt;如果光有 k8s 我们会遇到以下几个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;资源利用率极低，成本高昂&lt;/strong&gt;：Kubernetes 的原生调度单位是 Pod。它不理解 “模型” 这个概念，也不知道如何在一张 GPU 上高效地运行多个模型。那就意味着一个模型会独占一张 GPU 卡。中小模型的计算量不大，大部分时间里，这个 Pod 和 GPU 都是空闲的。当模型数量增加时（例如 10 个或 50 个模型），成本会呈线性增长，变得非常昂贵。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运维复杂度极高&lt;/strong&gt;：原生 Kubernetes 缺少一个更高层次的抽象来描述 “模型服务” 这个场景，必须手动组合多个底层资源来完成一个任务（手动编写和维护一套复杂的 Kubernetes YAML 文件（Deployment, Service, HorizontalPodAutoscaler 等）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺乏标准化的模型服务能力&lt;/strong&gt;：这些都是应用层的逻辑，原生的 Kubernetes 并不直接提供这些开箱即用的功能&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;如何进行 A / B 测试或金丝雀发布（Canary Rollouts）来平滑升级模型？&lt;/li&gt;
&lt;li&gt;如何处理模型的预处理和后处理逻辑？&lt;/li&gt;
&lt;li&gt;如何监控模型的 QPS、延迟、成功率等指标？ &lt;strong&gt;可观测性不足（难以按 “模型维度” 看指标）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;流量高峰时如何自动扩容，没有流量时如何缩容以节省成本？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="kserve"&gt;&lt;a href="#kserve" class="header-anchor"&gt;&lt;/a&gt;KServe
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Standardized Distributed Generative and Predictive AI Inference Platform for Scalable, Multi-Framework Deployment on Kubernetes&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;为了解决上述问题，社区催生了专门针对 Kubernetes 的模型服务平台，KServe 就是其中的佼佼者。&lt;strong&gt;它通过引入一个名为 InferenceService 的自定义资源（CRD）来解决这些问题。&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解决资源利用率问题&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;模型复用&lt;/strong&gt; (Multi-Model Serving)：KServe 可以与 NVIDIA Triton Inference Server 或 TorchServe 等高性能推理服务器集成。这些服务器支持在 &lt;strong&gt;单个 Pod 和单个 GPU 上加载和运行多个模型&lt;/strong&gt;。当请求进来时，由推理服务器动态地将计算任务分配给 GPU。这样，多个中小模型就可以共享一张 GPU，极大提高资源利用率。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;自动缩容至零&lt;/strong&gt; (Scale to Zero)：当一个模型在一段时间内没有收到任何请求时，KServe 可以自动将该模型的 Pod 缩减到 0。当新的请求到来时，它又能快速拉起一个新的 Pod 来提供服务。这对于流量不稳定的中小模型来说，是巨大的成本节省。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解决运维复杂度问题&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;单一抽象&lt;/strong&gt; (InferenceService)：不再需要编写 Deployment, Service 等一大堆 YAML。只需要定义一个 InferenceService 对象，在里面声明用的是什么框架（TensorFlow, PyTorch, Triton 等）以及模型的存储路径（如 S3）。KServe 会自动创建和管理所有底层的 Kubernetes 资源。这极大地简化了运维工作。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;解决标准化服务能力问题&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;开箱即用的高级部署&lt;/strong&gt;：在 InferenceService 的配置中，只需修改几行代码，就可以轻松实现金丝雀发布。例如，您可以指定将 10% 的流量发送到新模型，90% 的流量发送到旧模型，验证通过后再全量切换。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;请求日志、监控指标&lt;/strong&gt;：KServe 自动提供了标准化的接口和可观测性指标，方便接入 Prometheus、Grafana 等监控系统。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;推理图谱&lt;/strong&gt; (Inference Graph)：对于需要多个模型串联（例如预处理 -&amp;gt; 模型 A -&amp;gt; 后处理 -&amp;gt; 模型 B）的复杂场景，KServe 也提供了标准化的解决方案。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/002-febeb6af.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于 “在 Kubernetes 上部署多个中小模型” 这个场景， KServe 是目前最好、最主流的开源解决方案之一。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;直接采用 KServe 将会极大降低成本、简化管理、并提升部署的稳定性和灵活性，让我们可以更专注于模型算法本身，而不是底层的基础设施。&lt;/p&gt;
&lt;h3 id="kserve-架构"&gt;&lt;a href="#kserve-%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;KServe 架构
&lt;/h3&gt;&lt;p&gt;KServe 架构概览：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/003-de90c2f0.png"&gt;&lt;/p&gt;
&lt;p&gt;核心架构：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/004-3db13bc5.png"&gt;&lt;/p&gt;
&lt;p&gt;控制面架构：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/005-af8372cf.png"&gt;&lt;/p&gt;
&lt;p&gt;数据面架构：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/006-dd47cccc.png"&gt;&lt;/p&gt;
&lt;p&gt;模型 runtime 支持：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/007-d3f9de7c.png"&gt;&lt;/p&gt;
&lt;h3 id="vllm-和-triton"&gt;&lt;a href="#vllm-%e5%92%8c-triton" class="header-anchor"&gt;&lt;/a&gt;vLLM 和 Triton
&lt;/h3&gt;&lt;p&gt;从 KServe 的运行体系图中可以看到在推理层面，大致有两种最流行的软件，一个是 vLLM，一个是 Triton&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;vLLM&lt;/strong&gt; 是 LLM 专用推理引擎（只跑 Transformer，极快但单一）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Triton&lt;/strong&gt; 通用推理平台（CV/NLP/LLM/推荐都能跑，全能但略重）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 KServe 里，两者的选择就是： “要极速跑 LLM，还是要一车拉所有模型” 。在实践中，KServe 是一个统一平台，可以支持我们按需选引擎，&lt;strong&gt;所以不用 all in 其中任何一种，比较灵活、方便。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="三部署思路"&gt;&lt;a href="#%e4%b8%89%e9%83%a8%e7%bd%b2%e6%80%9d%e8%b7%af" class="header-anchor"&gt;&lt;/a&gt;三、部署思路
&lt;/h1&gt;&lt;h2 id="架构"&gt;&lt;a href="#%e6%9e%b6%e6%9e%84" class="header-anchor"&gt;&lt;/a&gt;架构
&lt;/h2&gt;&lt;p&gt;结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/008-4c464ba2.png"&gt;&lt;/p&gt;
&lt;p&gt;这里我们需要解释几个问题&lt;/p&gt;
&lt;h3 id="1-整体链路是谁在做自动扩缩容"&gt;&lt;a href="#1-%e6%95%b4%e4%bd%93%e9%93%be%e8%b7%af%e6%98%af%e8%b0%81%e5%9c%a8%e5%81%9a%e8%87%aa%e5%8a%a8%e6%89%a9%e7%bc%a9%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;1. 整体链路是谁在做自动扩缩容？
&lt;/h3&gt;&lt;p&gt;在 KServe + Knative 模式 下，职责大致是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;KServe&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;写 InferenceService CRD（YAML）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;KServe Controller 把它翻译成一个 Knative Service（和一些 K8s 资源）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;同时通过 annotations / 字段把我们希望的 autoscaling 配置写进去&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Knative Serving&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个 Revision&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个对应的 Deployment（里面的 Pod 跑模型容器）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;一个自动伸缩器：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;要么是 KPA（Knative Pod Autoscaler）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;要么是一个真正的 Kubernetes HPA（如果配置了 autoscaling.knative.dev/class: hpa.autoscaling.knative.dev）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接管 Knative Service，创建：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Istio&lt;/strong&gt;：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;负责入口网关、路由、mTLS 等&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把请求导入到 Knative 的 activator / queue-proxy 上&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为 Knative 提供 HTTP 请求 metrics（QPS、并发等）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不直接做扩缩容决策，只是提供流量和指标&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;真正做扩缩容决策的是：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Knative 的 KPA 或 HPA，再通过 Deployment 控制最终 Pod 数量&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;KServe 只是 “声明模型 + 帮我们写好 Knative 配置”，并不直接操作 replicas&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-enable-scale-to-zero-true--的含义是什么"&gt;&lt;a href="#2-enable-scale-to-zero-true--%e7%9a%84%e5%90%ab%e4%b9%89%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;2. enable-scale-to-zero: &amp;ldquo;true&amp;rdquo; 的含义是什么？
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 文件： knative-config.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;enable-scale-to-zero: &amp;#34;true&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在 Knative 的 config（比如 config-autoscaler）里，表示 允许某个 Knative Service 被 KPA 缩到 0 个 Pod。 再配合 InferenceService / Knative Service 上的配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;若没有设置 minScale / minReplicas，默认允许从 0 → N&lt;/li&gt;
&lt;li&gt;若在 InferenceService 里（或 annotations）配了 minReplicas: 1 或 autoscaling.knative.dev/minScale: &amp;ldquo;1&amp;rdquo;，则不会缩到 0，而是至少保留 1 个 Pod（即 1 块 GPU 一直常驻）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;缩到 0 的流程大致是：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一段时间内没有请求（由 Knative 的 autoscaler 统计）&lt;/li&gt;
&lt;li&gt;KPA 认为可以缩减，就把 Deployment 的 replicas 降到 0&lt;/li&gt;
&lt;li&gt;Pod 把 GPU 释放掉；节点上的 GPU 就空闲了&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;从 0 唤醒：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;有新请求到达 Istio 网关 → 被路由到 Knative 的 Activator&lt;/li&gt;
&lt;li&gt;Activator 缓冲请求，并通知 autoscaler&lt;/li&gt;
&lt;li&gt;autoscaler 把 Deployment 从 0 扩到 1（或更多）个 Pod&lt;/li&gt;
&lt;li&gt;Pod 启动，模型加载进 GPU，处理缓存的请求（这就是冷启动）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="3-单模型-多-pod-如何占多机多卡"&gt;&lt;a href="#3-%e5%8d%95%e6%a8%a1%e5%9e%8b-%e5%a4%9a-pod-%e5%a6%82%e4%bd%95%e5%8d%a0%e5%a4%9a%e6%9c%ba%e5%a4%9a%e5%8d%a1" class="header-anchor"&gt;&lt;/a&gt;3. 单模型 多 Pod ，如何占多机多卡？
&lt;/h3&gt;&lt;p&gt;把上面的流程套到 GPU 上看就是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. 每个推理 Pod 的容器请求&lt;/strong&gt;：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. K8s 调度时确保：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 Pod 分配到一个有空闲 GPU 的节点&lt;/li&gt;
&lt;li&gt;默认 nvidia.com / gpu 是「不可共享资源」，所以 1 Pod 独占 1 块卡&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. 当 autoscaler 决定从 1 Pod 扩到 N Pod 时：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;K8s 再调度 N-1 个新的 Pod 到其他 GPU 节点&lt;/li&gt;
&lt;li&gt;最终你就是：同一个模型，多 Pod，分布在多台单卡机器上&lt;/li&gt;
&lt;li&gt;Istio / Knative 负责把请求均衡到这些 Pod 上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;4. 当流量变小&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;autoscaler 把 replicas 从 N 缩回 1，甚至缩到 0&lt;/li&gt;
&lt;li&gt;对应地释放掉一部分 / 全部节点上的 GPU&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;总的来说：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目前有两种模式：&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;单模型 + 单 Pod = 占用一台单卡机&lt;/li&gt;
&lt;li&gt;单模型 + 多 Pod = 水平扩展到多台单卡机，多卡并行处理请求&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;多 Pod 的自动扩缩容流程：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;决策逻辑在 Knative（KPA 或 HPA）这层&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;KServe 只是根据 InferenceService 的 spec &amp;amp; annotations 帮我们创建出合适的 Knative Service / autoscaler 配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Istio 不做扩缩容决策，只负责网关和路由，同时为 Knative 提供 metrics / 流量通路&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;enable-scale-to-zero: &amp;ldquo;true&amp;rdquo; 是 Knative 的全局开关，允许在 InferenceService 里配置成真正可缩到 0 的无流量模型服务。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="面临的问题"&gt;&lt;a href="#%e9%9d%a2%e4%b8%b4%e7%9a%84%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;面临的问题
&lt;/h2&gt;&lt;p&gt;整个架构从水平扩容的角度讲是没有太大的问题，但当我们把视角切换到机器内部，看 pod 内部的情况，是有问题的，比如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一个显卡 48G 显存，一个小模型可能只需要 10G 显示，但它独占了一张显示，这会造成资源的浪费&lt;/li&gt;
&lt;li&gt;当我只有一两个模型需要部署时候问题不大。浪费也不大，但如果我有多个小模型（单卡能放下）都需要同时部署，如果不仔细计算显卡的使用率，那么有可能造成大量的资源浪费。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="解决办法"&gt;&lt;a href="#%e8%a7%a3%e5%86%b3%e5%8a%9e%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;解决办法
&lt;/h3&gt;&lt;p&gt;对一个 LLM 来说，显存大致分三块：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;模型权重（weights）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行时开销&lt;/strong&gt;（activations / 临时 buffer 等）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KV Cache&lt;/strong&gt;（连续 batching 的关键，vLLM 会尽可能把剩余显存拿来做这个）VLLM Docs&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;vLLM 通过 &amp;ndash;gpu-memory-utilization 控制 “自己能用的显存占比”（默认 0.9），在这个额度内， 剩下的空间基本都会拿去做 KV Cache，以提升吞吐和并发。&lt;/p&gt;
&lt;p&gt;所以：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果我们看到 “模型只占 10G”，&lt;strong&gt;很可能只是在低并发、短上下文下的一瞬间观感；&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;一旦并发、上下文长度、请求峰值上去，KV Cache 会吃掉大量显存，这时候那 “剩余的 30+ G” 就会逐步被用起来。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果在&lt;strong&gt;业务高峰期&lt;/strong&gt;，这几个指标都比较高（比如显存长期 &amp;gt;70%，KV cache 使用率也不低），那 “单模型独占一张卡” 并不浪费，而是在换 &lt;strong&gt;性能 &amp;amp; 稳定性&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;其实我们问题的本质是：“&lt;strong&gt;我有好几种小模型都要在线，单卡其实装得下，但一机一模型的部署方式会造成卡粒度上的浪费&lt;/strong&gt;。”&lt;/p&gt;
&lt;p&gt;要解决这个问题，大致有几条思路：按 &lt;strong&gt;“现实可行度” 从高到低排序：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;业务层合并：能不用多模型就别用多模型&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;能用 一个 “能力足够强” 的主模型 + Prompt / LoRA 搞定，就不要真部署 N 个完全独立的小模型。&lt;/li&gt;
&lt;li&gt;多数 “业务小模型” 的差异，其实是 “提示词 + 风格 + LoRA” 的区别，不一定非要上不同 base model。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;把单模型的吞吐吃满&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;利用 vLLM 的连续 batching，提高并发、适当增加最大上下文、控制 QPS，让 GPU 真正跑到比较高的利用率。&lt;/li&gt;
&lt;li&gt;我们已经有完整的 Prometheus / Grafana 看板方案，可以直接看 QPS、Token 吞吐、GPU Util、KV Cache 占用来调优。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;实在必须多模型同卡，再考虑 “共享 GPU” 技术&lt;/strong&gt;（下面会拆开说）&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="共享-gpu-技术"&gt;&lt;a href="#%e5%85%b1%e4%ba%ab-gpu-%e6%8a%80%e6%9c%af" class="header-anchor"&gt;&lt;/a&gt;共享 GPU 技术
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Time-Slicing&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;NVIDIA GPU Operator / k8s-device-plugin 提供的 Time-Slicing，本质是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;把一张物理 GPU 虚拟成多个 “replica” 资源&lt;/strong&gt;，Pod 申请 nvidia.com / gpu: 1 时，拿到的是其中一个 replica；&lt;/li&gt;
&lt;li&gt;底层靠 时间片轮转 在同一张卡上跑多个 Pod。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键点（也是坑点）是：&lt;strong&gt;Time-Slicing 只切算力，不切显存，显存是共享的，没有隔离&lt;/strong&gt;。NVIDIA Docs 这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果多个 Pod 加起来申请 / 实际占用的显存 &amp;gt; 实际物理显存，就有概率 OOM；&lt;/li&gt;
&lt;li&gt;即使不 OOM，Page Fault / 内存碎片也会让延迟非常不稳定。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而 vLLM 非常依赖稳定且持续的显存做 KV Cache，Time-Slicing 没有显存隔离，很容易被别的 Pod 挤爆显存导致 OOM，所以&lt;strong&gt;不适合 vLLM&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MIG（Multi-Instance GPU）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;MIG 的特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;真正把 &lt;strong&gt;一张 GPU 切成多个硬件隔离的 “小卡”&lt;/strong&gt;，每块有独立的显存、高带宽内存、缓存和计算核心&lt;/li&gt;
&lt;li&gt;适合需要 &lt;strong&gt;延迟可预测、多租户隔离&lt;/strong&gt; 的 LLM 推理场景&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但 MIG 只在 A100 / H100 / A30 等特定卡上存在，普通云上 L4、L40、T4、V100 这类要么不支持，要么支持非常有限。对于我们来说，&lt;strong&gt;也不适用&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ModelMesh&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;KServe 其实就内置了两种 “模型平台形态”：&lt;strong&gt;1. Single-Model Serving（单模型平台）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个服务只跑一个模型；&lt;/li&gt;
&lt;li&gt;LLM / 大模型几乎都是走这一条（包括 vLLM Runtime）。&lt;strong&gt;2. Multi-Model Serving（基于 ModelMesh 的多模型平台）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;同一个模型服务器里可以放多模型，按需加载/卸载，适合一堆小模型共享有限卡的场景（比如 SKLearn/ONNX/OpenVINO 那些）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ModelMesh 适合「很多模型 + 访问稀疏」的场景，ModelMesh 的设计目标是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;管理 大量模型（&lt;strong&gt;几十、几百甚至更多&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;很多模型 QPS 很低，没必要长时间常驻显存 / 内存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过「按需加载 + LRU 驱逐」来平衡：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;内存 / 显存占用&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;冷启动延迟&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外，社区对 ModelMesh 的定位也比较明确：更偏向 “可伸缩多模型平台”，现在要把 LLM Runtime（特别是 vLLM）硬往 ModelMesh 里塞，是有一定探索和集成成本的，而且生态也还在演进中 &lt;a class="link" href="https://github.com/kserve/kserve/issues/4299" target="_blank" rel="noopener"
 &gt;https://github.com/kserve/kserve/issues/4299&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/009-719449fc.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果我们前期的目标只是「十个以内」的小模型，希望高利用率、简单稳定，所以可以先不用 ModelMesh，真正到模型数爆炸、并且很多模型很冷时，再考虑 ModelMesh 会更合适。尤其是当前的重点是 “先把核心 LLM 跑稳定 &amp;amp; 可观测 &amp;amp; 易扩容”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Triton + vLLM + 多模型同 Pod / 同 GPU&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以上方案都不太合适，于是我把目光投向了 Triton&lt;/p&gt;
&lt;p&gt;NVIDIA Triton 的能力是比较强的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持在同一台机器上&lt;strong&gt;多个模型 / 多个模型实例并行执行&lt;/strong&gt;，由 Triton 负责调度；NVIDIA Docs&lt;/li&gt;
&lt;li&gt;支持多种后端（TensorRT-LLM、PyTorch、ONNXRuntime、Python backend 等）；&lt;/li&gt;
&lt;li&gt;现在还有 &lt;strong&gt;官方的 vLLM backend&lt;/strong&gt;，可以在 Triton 里用 vLLM 做 LLM 推理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;从 GPU 视角看，Triton 做的事类似于：&lt;strong&gt;“一个进程负责管理很多模型，来了请求就把对应的 op 丢给 GPU，GPU 再在硬件层面做调度并发。” 但是：Triton 也不会神奇地帮我们 “切显存”—— 多个模型的权重 + KV Cache 依然是往同一个物理显存里塞。Triton 提供的是 “共享一块显存的多模型协调器”，不是 “把显存分成几块小卡” 的硬隔离器。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/010-068205ed.png"&gt;&lt;/p&gt;
&lt;p&gt;因此：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Triton 不能像 MIG 一样说：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;“模型 A 只能用 16G，模型 B 只能用 8G，互相绝不会越界”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;它顶多是：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;通过配置 + 调度让你 “尽量别把自己搞到 OOM”；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;但如果你把几个模型配置得都很激进，合起来 &amp;gt; 物理显存，照样可能 OOM，仅仅是更 “有迹可循”。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结合我们的实际情况，综合考虑，Triton 可以有以下几种组合姿势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Triton 只负责传统模型（embedding、CV、语音等），LLM 仍由独立 vLLM 服务跑&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;这时候 “一卡多模型” 主要是非 LLM 模型之间的事，LLM 是单卡独占或少量共享；&lt;/li&gt;
&lt;li&gt;对现在的 “私有化大模型平台” 来说，这是最现实、也最可控的一种搭配。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;Triton + vLLM backend，把 LLM 也塞进 Triton 的统一服务里&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;本质上还是 “一张卡一个 vLLM 引擎”，只是对外通过 Triton 统一暴露接口而已；&lt;/li&gt;
&lt;li&gt;多模型同卡时，显存依然一起抢；如果你试图放多个 LLM（哪怕是 7B SLM），很快就会撞上显存天花板，需要极其克制的 &amp;ndash;gpu-memory-utilization 和并发控制。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;Triton 内部多 LLM + 非 LLM 模型混合&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;这种组合在理论上可行，工程上可做，但对资源规划、监控、故障排查的要求会非常高；&lt;/li&gt;
&lt;li&gt;对现在来说，属于 “下一阶段再考虑” 的东西&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;综上，目前我们利用 Triton 采用第一种方式：负责传统模型（embedding、CV、语音等），LLM 仍由独立 vLLM 服务跑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;对所有私有化部署的模型部署整体策略如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/011-66896efb.png"&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;上图是从 KServe 视角看的，如果从 k8s 视角，不同的 pod 还会有多副本扩容的情况。但每个 pod 都是独占 GPU。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Triton 多模型（非 LLM）分组方案&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整体思路：&lt;strong&gt;1 GPU 1 Triton，多模型共用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在现有架构下，最自然的做法是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;每块 GPU 起一个 Triton Pod（由一个 InferenceService 管）&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这个 Triton Pod 里面的 &lt;strong&gt;model repository 里放多个非 LLM 模型&lt;/strong&gt;：embedding / rerank / CV / ASR…&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;KServe 只是负责：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;帮我们起 kserve-tritonserver 这个 runtime&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把远端（S3/MinIO/PVC）上的 Triton model repository 挂到 /models（或 /mnt/models）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;暴露统一的 HTTP / gRPC 入口（/v2/models//infer）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Triton 本身就是为「一台机上多个模型、多个实例并发」设计的：多个模型、多个实例可以在同一块卡上并发执行，通过 instance_group、dynamic_batching 来调度和吃满卡资源。NVIDIA Docs&lt;/p&gt;
&lt;p&gt;建议&lt;strong&gt;按业务域 + 性能特性分组&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一组：文本向量 + rerank（text-embedder / text-reranker）&lt;/li&gt;
&lt;li&gt;一组：CV / OCR / ASR（图像 &amp;amp; 语音） 这样：&lt;/li&gt;
&lt;li&gt;同一组内模型的 batch 维度、输入大小比较接近，Triton 的 dynamic batching 比较好调；&lt;/li&gt;
&lt;li&gt;资源隔离更清晰：文本这组爆了不会影响语音那组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面是一套完整配置样例（可以先从「所有非 LLM 都放一个组」开始，后面再拆分）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;分组示例&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Triton 模型仓库（model repository）结构示例，Triton 要求的模型仓库布局类似这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; s3://your-bucket/triton-nonllm-repo/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;├── text-embedding-e5-small/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;│ ├── config.pbtxt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;│ └── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;│ └── model.onnx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;├── text-rerank-msmarco/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;│ ├── config.pbtxt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;│ └── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;│ └── model.onnx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;├── vision-cls-resnet50/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;│ ├── config.pbtxt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;│ └── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;│ └── model.onnx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;├── asr-conformer/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;│ ├── config.pbtxt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;│ └── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;│ └── model.onnx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;└── search-pipeline/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; ├── config.pbtxt # 可选：Triton ensemble，把 embedder + reranker 串起来
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; └── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; └── model.graphdef / model.py / ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;只要 storageUri 指向这个目录，Triton 就会把子目录当成多个模型一起加载。&lt;/p&gt;
&lt;p&gt;单个模型的 config.pbtxt 示例（带分组 / 实例配置），以一个 ONNX embedding 模型 为例： 路径：text-embedding-e5-small/config.pbtxt&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;name: &amp;#34;text-embedding-e5-small&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;platform: &amp;#34;onnxruntime_onnx&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;max_batch_size: 128 # 这里根据你的 embedding 模型实际情况调
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;input [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; name: &amp;#34;input_ids&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; data_type: TYPE_INT64
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; dims: [ -1 ] # 序列长度，-1 表示动态
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; name: &amp;#34;attention_mask&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; data_type: TYPE_INT64
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; dims: [ -1 ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;output [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; name: &amp;#34;embedding&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; data_type: TYPE_FP32
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; dims: [ 768 ] # 或者你的模型真实向量维度
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;# 关键：在同一块 GPU 上开多实例，提高吞吐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;instance_group [
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; kind: KIND_GPU
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; count: 2 # 这块卡上起两个实例，看显存情况调 1/2/3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;# 关键：Dynamic Batching，让 Triton 自动拼 batch
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;dynamic_batching {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; preferred_batch_size: [ 8, 16, 32, 64 ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; max_queue_delay_microseconds: 2000 # 2ms 内尽量攒一波请求
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;同理，你可以为其他模型写各自的 config.pbtxt。分组思路：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对 &lt;strong&gt;高 QPS 的模型&lt;/strong&gt;（比如 text embedding）可以把 max_batch_size 和 preferred_batch_size 设得大些，多起几个 instance_group；&lt;/li&gt;
&lt;li&gt;对 &lt;strong&gt;低 QPS 但重模型&lt;/strong&gt;（ASR、复杂 CV）就用 max_batch_size 小一点，甚至单实例。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果有完整的 pipeline（比如「embedding → rerank」），可以用 Triton 的 ensemble 在 search-pipeline/config.pbtxt 里把两个模型串起来，一次请求走一条 DAG，减少网络往返。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;KServe InferenceService YAML 示例（kserve-tritonserver）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;KServe 自带 kserve-tritonserver 这个 ClusterServingRuntime，支持 TensorFlow / ONNX / PyTorch / TensorRT 模型。可以这样起一个「非 LLM 小模型专用」的 Triton 服务：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;InferenceService&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;triton-nonllm-text&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Knative 自动扩缩容（按并发）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/metric&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;concurrency&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;10&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 每 Pod 目标并发&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/minScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/maxScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;5&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ✅ 新 schema：通过 model.runtime 显式指定使用 kserve-tritonserver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;modelFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 这里写实际模型格式（比如 onnx / pytorch），只要包含在 kserve-tritonserver 支持列表中即可&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Triton 仓库里可以混放多种 backend，KServe 不会限制这一层&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;onnx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-tritonserver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 指向刚才那个包含多个模型的 Triton 模型仓库&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3://your-bucket/triton-nonllm-repo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtimeVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;24.03-py3&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 按你集群里安装的 kserve-tritonserver 版本改&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 如需 gRPC（性能更好），参考官方示例暴露 9000 端口:contentReference[oaicite:6]{index=6}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;h2c&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;TCP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;32Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nodeSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gpu-pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 按你集群里标的 label 改，确保调度到有 GPU 的节点&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&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;多模型是 Triton 内部概念&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;KServe 看到的只是「一个 InferenceService + 一个 Triton Pod」。&lt;/li&gt;
&lt;li&gt;Triton 会根据 storageUri 下的目录加载多个模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;请求路径&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;走 KServe / Istio / Knative 的网关时：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;HTTP：POST http:///v2/models//infer&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;gRPC：grpc://:/InferenceServer/ModelInfer（按 Triton V2 协议）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;就是每个子目录名：text-embedding-e5-small / vision-cls-resnet50 / asr-conformer…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;简单 Checklist：&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;准备 Triton 模型仓库&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;在 MinIO / S3 / PVC 上建好 triton-*-repo 目录；&lt;/li&gt;
&lt;li&gt;把 embedding、rerank、CV、ASR 模型按 Triton 要求拆目录 + 写 config.pbtxt。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;&lt;strong&gt;确认集群里有 kserve-tritonserver 的 ClusterServingRuntime&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;kubectl get clusterservingruntime | grep triton&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;&lt;strong&gt;应用上面那个 InferenceService YAML&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;改好 storageUri、runtimeVersion、nodeSelector；&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;&lt;strong&gt;通过 /v2/models//infer 分别打 smoke test&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;文本 embedding / rerank / CV / ASR 各来几条请求；&lt;/li&gt;
&lt;li&gt;对比 Triton metrics（/metrics）和 DCGM，看 GPU 利用率 &amp;amp; 显存占用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="请求流--监控流两条主链路"&gt;&lt;a href="#%e8%af%b7%e6%b1%82%e6%b5%81--%e7%9b%91%e6%8e%a7%e6%b5%81%e4%b8%a4%e6%9d%a1%e4%b8%bb%e9%93%be%e8%b7%af" class="header-anchor"&gt;&lt;/a&gt;请求流 &amp;amp; 监控流（两条主链路）
&lt;/h2&gt;&lt;h3 id="推理请求链路从客户端到-vllm"&gt;&lt;a href="#%e6%8e%a8%e7%90%86%e8%af%b7%e6%b1%82%e9%93%be%e8%b7%af%e4%bb%8e%e5%ae%a2%e6%88%b7%e7%ab%af%e5%88%b0-vllm" class="header-anchor"&gt;&lt;/a&gt;推理请求链路（从客户端到 vLLM）
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/012-7abde61a.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;KServe 把 InferenceService 抽象出来，底层仍然是 Knative Service + Istio VirtualService 这些资源；Istio ServiceMesh 文档里也有 “给 InferenceService 打 sidecar 做安全 / 流量治理” 的说明。&lt;/li&gt;
&lt;li&gt;vLLM 服务端会在 /metrics 上暴露自身的 Prometheus 指标，例如 vllm:prompt_tokens_total、vllm:generation_tokens_total、vllm:e2e_request_latency_seconds 等，用来统计 QPS、Token 数量和端到端延迟。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="监控链路业务--gpu"&gt;&lt;a href="#%e7%9b%91%e6%8e%a7%e9%93%be%e8%b7%af%e4%b8%9a%e5%8a%a1--gpu" class="header-anchor"&gt;&lt;/a&gt;监控链路（业务 + GPU）
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/013-b187c0ff.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;NVIDIA 官方文档明确建议：在 Kubernetes 中监控 GPU 时，使用 DCGM Exporter → Prometheus → Grafana 这一条链路。&lt;/li&gt;
&lt;li&gt;我们现在的设计就是把这一套和 vLLM 的业务 metrics 汇总到同一个 kube-prometheus-stack 里 —— 这也是很多实践里推荐的做法，用 Prometheus Operator 的 ServiceMonitor 去发现所有 exporter 与应用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在 K8s 里做 GPU 监控，典型链路是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;GPU 节点 → GPU Operator → DCGM Exporter → Prometheus → Grafana&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPU Operator&lt;/strong&gt;：在 GPU 节点上自动装好驱动、Container Toolkit、Device Plugin、DCGM / Exporter 等一整套 GPU 栈。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DCGM Exporter&lt;/strong&gt;：基于 NVIDIA DCGM，把 GPU 的利用率、显存、温度、功耗等指标以 Prometheus /metrics 的形式暴露出来。&lt;/li&gt;
&lt;li&gt;NVIDIA 官方推荐：在 K8s 集群里采集 GPU Telemetry，就用 DCGM Exporter + Prometheus + Grafana 这一套。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPU Operator 默认就会启用 DCGM Exporter 来采集 GPU metrics&lt;/strong&gt;（可以通过 Helm values 里的 dcgmExporter.enabled 开关）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/014-041efd3a.png"&gt;&lt;/p&gt;
&lt;p&gt;所以，我们只需要：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 Kubekey 装好的 K8s 集群中安装 GPU Operator（包含 DCGM Exporter）。&lt;/li&gt;
&lt;li&gt;在公司统一 Prometheus 上加一个 scrape job（或者在集群里用 ServiceMonitor），把这些 /metrics 抓过去即可&lt;/li&gt;
&lt;/ol&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;GPU Operator 是一个管理者，DCGM Exporter 是它管理的一个组件。 你只和管理者（Operator）打交道，它会帮你搞定一切。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h1 id="四环境搭建步骤"&gt;&lt;a href="#%e5%9b%9b%e7%8e%af%e5%a2%83%e6%90%ad%e5%bb%ba%e6%ad%a5%e9%aa%a4" class="header-anchor"&gt;&lt;/a&gt;四、环境搭建步骤
&lt;/h1&gt;&lt;h2 id="需要安装的软件版本及顺序"&gt;&lt;a href="#%e9%9c%80%e8%a6%81%e5%ae%89%e8%a3%85%e7%9a%84%e8%bd%af%e4%bb%b6%e7%89%88%e6%9c%ac%e5%8f%8a%e9%a1%ba%e5%ba%8f" class="header-anchor"&gt;&lt;/a&gt;需要安装的软件、版本及顺序
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/015-14833749.png"&gt;&lt;/p&gt;
&lt;h2 id="安装步骤-sop"&gt;&lt;a href="#%e5%ae%89%e8%a3%85%e6%ad%a5%e9%aa%a4-sop" class="header-anchor"&gt;&lt;/a&gt;安装步骤 SOP
&lt;/h2&gt;&lt;h3 id="第-0-步准备工作"&gt;&lt;a href="#%e7%ac%ac-0-%e6%ad%a5%e5%87%86%e5%a4%87%e5%b7%a5%e4%bd%9c" class="header-anchor"&gt;&lt;/a&gt;第 0 步：准备工作
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 添加所有需要的 Helm 仓库
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm repo add nvidia https://helm.ngc.nvidia.com/nvidia
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;helm repo add jetstack https://charts.jetstack.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;helm repo add istio https://istio-release.storage.googleapis.com/charts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;helm repo update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-1-步安装-nvidia-gpu-operator-v2491"&gt;&lt;a href="#%e7%ac%ac-1-%e6%ad%a5%e5%ae%89%e8%a3%85-nvidia-gpu-operator-v2491" class="header-anchor"&gt;&lt;/a&gt;第 1 步：安装 NVIDIA GPU Operator (v24.9.1)
&lt;/h3&gt;&lt;p&gt;目的：启用 GPU 驱动，并配置 CDI (Container Device Interface) 以兼容 K8s 1.30。&lt;/p&gt;
&lt;p&gt;以下命令是在 GPU 驱动都安装好的前提下执行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;helm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;nvidia&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="n"&gt;v24&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;9.1&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;false&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;cdi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;cdi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证：等待 kubectl get pods -n gpu-operator 全绿。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果出错，那么：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个命令显式指定了版本，并且强制告诉 Operator 你的 Containerd 配置文件在哪里，避免出现之前的 FailedCreatePodSandBox 错误。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;helm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="n"&gt;nvidia&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="n"&gt;gpu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="n"&gt;v24&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;9.1&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;false&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CONTAINERD_CONFIG&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toml&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CONTAINERD_SOCKET&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=/&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;containerd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sock&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;CONTAINERD_RUNTIME_CLASS&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;toolkit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nvidia&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;cdi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt; &lt;span class="n"&gt;cdi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-2-步安装-kube-prometheus-stack-v6190"&gt;&lt;a href="#%e7%ac%ac-2-%e6%ad%a5%e5%ae%89%e8%a3%85-kube-prometheus-stack-v6190" class="header-anchor"&gt;&lt;/a&gt;第 2 步：安装 Kube-Prometheus-Stack (v61.9.0)
&lt;/h3&gt;&lt;p&gt;目的：基础监控。必须修正配置，否则 KServe 的 ServiceMonitor 会被忽略。&lt;/p&gt;
&lt;p&gt;准备 values-kube-prometheus-stack.yaml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# values-kube-prometheus-stack.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;prometheus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prometheusSpec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 允许抓取所有 namespace 下的 ServiceMonitor / PodMonitor&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceMonitorNamespaceSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podMonitorNamespaceSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 不再强制使用 Helm 的 release label 做筛选&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# （默认是 true，会要求 ServiceMonitor 带 release=&amp;lt;helm release&amp;gt; 这样的 label）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceMonitorSelectorNilUsesHelmValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podMonitorSelectorNilUsesHelmValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 空 selector = 不按 label 过滤，看到就抓&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceMonitorSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podMonitorSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;{}&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可选：Prometheus 数据保留时间&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retention&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;15d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 暴露 Prometheus 的方式（开发环境方便直接 NodePort）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;NodePort&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nodePort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30090&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 访问地址：任一节点IP:30090 &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;grafana&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Grafana 用 NodePort 方便先调试；生产看你们自己安全策略&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;NodePort&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nodePort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30080&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可不填，让 kube 随机分配&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 管理员密码（不写一般是 prom-operator，也可以明确写死）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;adminPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;prom-operator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这套配置的核心就是：Prometheus 不再只认「带 release=kube-prometheus-stack 的 ServiceMonitor」，而是「所有 namespace 下的 ServiceMonitor 都抓」，这样 GPU Operator 自动创建的 ServiceMonitor 也不会漏掉。&lt;/p&gt;
&lt;p&gt;安装 kube-prometheus-stack&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;helm install monitoring prometheus-community/kube-prometheus-stack \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; -n monitoring --create-namespace \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --version 61.9.0 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; -f values-kube-prometheus-stack.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;打开 &lt;strong&gt;GPU Operator 的 监控组件（ServiceMonitor）&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;#解释：
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;#--reuse-values: 保留之前设置的 cdi.enabled=true 等参数。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;#--set dcgmExporter.serviceMonitor.enabled=true: 这才是核心，告诉 Operator 创建监控对象。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;helm upgrade gpu-operator nvidia/gpu-operator \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; -n gpu-operator \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; --reuse-values \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; --set dcgmExporter.serviceMonitor.enabled=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-3-步安装-cert-manager-v1153"&gt;&lt;a href="#%e7%ac%ac-3-%e6%ad%a5%e5%ae%89%e8%a3%85-cert-manager-v1153" class="header-anchor"&gt;&lt;/a&gt;第 3 步：安装 Cert-Manager (v1.15.3)
&lt;/h3&gt;&lt;p&gt;目的：为 KServe 和 Knative 的 Webhook 签发自签名证书。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;helm install cert-manager jetstack/cert-manager \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; -n cert-manager --create-namespace \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --version v1.15.3 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --set crds.enabled=true
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证：等待 kubectl get pods -n cert-manager 全绿。&lt;/p&gt;
&lt;h3 id="第-4-步安装-istio-v1226"&gt;&lt;a href="#%e7%ac%ac-4-%e6%ad%a5%e5%ae%89%e8%a3%85-istio-v1226" class="header-anchor"&gt;&lt;/a&gt;第 4 步：安装 Istio (v1.22.6)
&lt;/h3&gt;&lt;p&gt;目的：流量网关。严格按照 Base -&amp;gt; Istiod -&amp;gt; Gateway 的顺序安装。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 1. 安装 Base CRD
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm install istio-base istio/base -n istio-system --create-namespace --version 1.22.6
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;# 2. 安装 Istiod 控制平面
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;helm install istiod istio/istiod -n istio-system --version 1.22.6 --wait
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;# 3. 安装 Ingress Gateway (数据平面)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;helm install istio-ingressgateway istio/gateway -n istio-system --version 1.22.6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-5-步安装-knative-serving--net-istio-v1151"&gt;&lt;a href="#%e7%ac%ac-5-%e6%ad%a5%e5%ae%89%e8%a3%85-knative-serving--net-istio-v1151" class="header-anchor"&gt;&lt;/a&gt;第 5 步：安装 Knative Serving &amp;amp; Net-Istio (v1.15.1)
&lt;/h3&gt;&lt;p&gt;目的：实现 Serverless 扩缩容能力。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 安装 CRDs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.15.2/serving-crds.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 安装 Serving Core&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.15.2/serving-core.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 安装 Net-Istio (网络适配器)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f https://github.com/knative-extensions/net-istio/releases/download/knative-v1.15.1/net-istio.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证：kubectl get pods -n knative-serving，确保 net-istio-controller 和 activator 状态为 Running。&lt;/p&gt;
&lt;h3 id="第-6-步安装-kserve-v0141"&gt;&lt;a href="#%e7%ac%ac-6-%e6%ad%a5%e5%ae%89%e8%a3%85-kserve-v0141" class="header-anchor"&gt;&lt;/a&gt;第 6 步：安装 KServe (v0.14.1)
&lt;/h3&gt;&lt;p&gt;目的：核心推理平台。 注意这里使用的是 v0.14.1。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 安装 KServe CRDs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm install kserve-crd oci://ghcr.io/kserve/charts/kserve-crd \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --version v0.14.1 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; -n kserve --create-namespace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;# 安装 KServe Controller
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;helm install kserve oci://ghcr.io/kserve/charts/kserve \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt; --version v0.14.1 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt; -n kserve 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行完上述两条命令后，检查 KServe 的系统组件： kubectl get pods -n kserve&lt;/p&gt;
&lt;h3 id="第-7-步配置-vllm-runtime-关键"&gt;&lt;a href="#%e7%ac%ac-7-%e6%ad%a5%e9%85%8d%e7%bd%ae-vllm-runtime-%e5%85%b3%e9%94%ae" class="header-anchor"&gt;&lt;/a&gt;第 7 步：配置 vLLM Runtime (关键)
&lt;/h3&gt;&lt;p&gt;KServe 默认只有简单的 CPU 模型支持。为了运行公司级 LLM 服务，必须添加支持 GPU 的 Runtime。&lt;/p&gt;
&lt;p&gt;保存以下内容为 vllm-runtime.yaml 并执行 kubectl apply -f vllm-runtime.yaml：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterServingRuntime&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-vllm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prometheus.kserve.io/port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;8080&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prometheus.kserve.io/path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/metrics&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;supportedModelFormats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vllm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoSelect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vllm/vllm-openai:latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 建议生产环境锁定具体 image sha256&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-m&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;vllm.entrypoints.openai.api_server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;port=8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;model=/mnt/models&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;gpu-memory-utilization=0.9&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;PORT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8080&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;32Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-8-步为-gpu-调度开启-knative-的-nodeselector--tolerations"&gt;&lt;a href="#%e7%ac%ac-8-%e6%ad%a5%e4%b8%ba-gpu-%e8%b0%83%e5%ba%a6%e5%bc%80%e5%90%af-knative-%e7%9a%84-nodeselector--tolerations" class="header-anchor"&gt;&lt;/a&gt;第 8 步：为 GPU 调度开启 Knative 的 nodeSelector / tolerations
&lt;/h3&gt;&lt;p&gt;Knative 默认禁止你在 Knative Service 的 Pod 里写 nodeSelector / tolerations，KServe 官方教程在使用 GPU 时也会做这一步 patch。&lt;/p&gt;
&lt;p&gt;打开特性开关&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl patch configmap/config-features &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; --namespace knative-serving &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --type merge &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --patch &lt;span class="s1"&gt;&amp;#39;{&amp;#34;data&amp;#34;:{&amp;#34;kubernetes.podspec-nodeselector&amp;#34;:&amp;#34;enabled&amp;#34;, &amp;#34;kubernetes.podspec-tolerations&amp;#34;:&amp;#34;enabled&amp;#34;}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;重启 Webhook：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl delete pod -n knative-serving -l &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;webhook
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-9-步istio-sidecar-注入策略避免影响-gpu-operator-和监控"&gt;&lt;a href="#%e7%ac%ac-9-%e6%ad%a5istio-sidecar-%e6%b3%a8%e5%85%a5%e7%ad%96%e7%95%a5%e9%81%bf%e5%85%8d%e5%bd%b1%e5%93%8d-gpu-operator-%e5%92%8c%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;第 9 步：Istio Sidecar 注入策略（避免影响 GPU Operator 和监控）
&lt;/h3&gt;&lt;p&gt;默认 Istio 只对打了 istio-injection=enabled 标签的 namespace 注入 sidecar。确保「不需要注入」的 namespace 没有 label&lt;/p&gt;
&lt;p&gt;保护基础设施 (防止 Sidecar 导致 Job 不退出)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 明确禁止 GPU Operator 注入 (防止 Validator/Driver 安装卡死)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace gpu-operator istio-injection&lt;span class="o"&gt;=&lt;/span&gt;disabled --overwrite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 明确禁止 监控 注入 (减少开销)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace monitoring istio-injection&lt;span class="o"&gt;=&lt;/span&gt;disabled --overwrite
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 明确禁止 kube-system 注入 (安全底线)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace kube-system istio-injection&lt;span class="o"&gt;=&lt;/span&gt;disabled --overwrite
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;保护控制平面 (防止 Webhook 超时)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace knative-serving istio-injection&lt;span class="o"&gt;=&lt;/span&gt;disabled --overwrite 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace kserve istio-injection&lt;span class="o"&gt;=&lt;/span&gt;disabled --overwrite
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;启用业务空间 (让模型享受 Service Mesh 能力)，业务命名空间 “必须” 注入 ✅&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 创建你的业务空间 (如果你还没创建)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl create namespace model-serving
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 启用注入 (关键一步)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;kubectl label namespace model-serving istio-injection&lt;span class="o"&gt;=&lt;/span&gt;enabled --overwrite
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这样做的好处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GPU Operator、DCGM Exporter、Prometheus 不会被 sidecar 干扰&lt;/li&gt;
&lt;li&gt;模型推理流量全部走 Istio + Knative 控制的入口&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="第-10-步安装-minio可选"&gt;&lt;a href="#%e7%ac%ac-10-%e6%ad%a5%e5%ae%89%e8%a3%85-minio%e5%8f%af%e9%80%89" class="header-anchor"&gt;&lt;/a&gt;第 10 步：安装 MinIO（可选）
&lt;/h3&gt;&lt;p&gt;添加所有依赖 Helm 仓库&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 添加MinIO、Loki官方仓库
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm repo add minio https://helm.min.io/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;helm repo add grafana https://grafana.github.io/helm-charts
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;# 更新所有仓库（确保获取最新Chart版本）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;helm repo update
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;MinIO 部署 —— 部署 2 节点分布式 MinIO（Loki 后端存储） MinIO 配置为 2 副本分布式，适配当前 2 节点，同时预留未来扩容参数，升级时仅需修改副本数即可。&lt;/p&gt;
&lt;p&gt;编写 MinIO 配置文件 minio-distributed-values.yaml&lt;/p&gt;
&lt;p&gt;关键标注：文件内 replicas 和 numberOfNodes 为扩容核心参数，未来扩容需同步修改为 3+ 等节点数。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 核心：启用分布式模式（2节点适配，未来扩容改replicas/numberOfNodes）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;distributed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;numberOfNodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 镜像配置（你原单节点的成功版本）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;quay.io/minio/minio&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;RELEASE.2023-07-07T07-13-57Z&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pullPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;IfNotPresent&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 访问密钥（完全沿用你的配置，Loki对接需一致）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;rootUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;U4DwltABIX8p20aONyoY &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;rootPassword&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;9YZInPYCqXwerS0NE6PDGrxo9g0l4akt2fs0IJNm &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 持久化存储配置（你的成功配置，保障数据持久化）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;persistence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 使用集群默认存储类&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;10Gi &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mountPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/export&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 服务配置（集群内访问，无需暴露外网）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterIP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# S3兼容接口端口（Loki对接用）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;consoleService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterIP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 管理控制台端口（可选）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 默认Bucket（Loki日志存储专用，自动创建，无需手动操作）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;defaultBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki-chunks &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Loki配置需与该Bucket名一致&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read-write&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;purge&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 卸载MinIO时保留数据&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 资源限制（适配测试环境，低资源占用）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;100m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;512Mi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;500m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1Gi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 健康检查（分布式启动稍慢，微调延迟避免探针失败）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;livenessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;periodSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;readinessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;periodSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 跨节点调度策略（强制2个副本分布在不同节点，保障高可用）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;affinity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;podAntiAffinity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;labelSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;matchExpressions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;operator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;In&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;minio&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;topologyKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kubernetes.io/hostname&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 安全配置（确保权限足够）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;securityContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runAsUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runAsGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fsGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 监控集成（默认关闭，若需对接Prometheus可改为true）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceMonitor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;安装分布式 MinIO&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;helm install minio minio/minio \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; -n monitoring \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; --version 5.4.0 \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; -f minio-distributed-values.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证 MinIO 部署（2 节点核心检查）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看MinIO Pod状态（2个副本均为Running，分布在不同节点）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl get pods -n monitoring -o wide &lt;span class="p"&gt;|&lt;/span&gt; grep minio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看MinIO Service（地址固定，扩容后不变）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;kubectl get svc -n monitoring &lt;span class="p"&gt;|&lt;/span&gt; grep minio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 验证桶创建成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -n monitoring minio-0 -- mc ls minio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 预期输出：[2024-xx-xx xx:xx:xx UTC] DIR loki-data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;创建存储桶&lt;/p&gt;
&lt;p&gt;Loki 的 ConfigMap 中指定了两个桶：chunks（存储日志块）、ruler（存储规则），先在 MinIO 中手动创建这两个桶（避免 Loki 首次写入时因桶不存在报错）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 进入minio-0 Pod，创建chunks和ruler桶&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl &lt;span class="nb"&gt;exec&lt;/span&gt; -it minio-0 -n monitoring -- /bin/sh -c &lt;span class="s2"&gt;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; /usr/local/bin/mc alias set minio http://localhost:9000 U4DwltABIX8p20aONyoY 9YZInPYCqXwerS0NE6PDGrxo9g0l4akt2fs0IJNm --api S3v4 &amp;amp;&amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; /usr/local/bin/mc mb minio/chunks --ignore-existing &amp;amp;&amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; /usr/local/bin/mc mb minio/ruler --ignore-existing &amp;amp;&amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; echo &amp;#39;✅ Loki所需的chunks和ruler桶创建成功&amp;#39; &amp;amp;&amp;amp;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; /usr/local/bin/mc ls minio # 验证桶是否存在
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="第-11-步安装-loki-和-promtail"&gt;&lt;a href="#%e7%ac%ac-11-%e6%ad%a5%e5%ae%89%e8%a3%85-loki-%e5%92%8c-promtail" class="header-anchor"&gt;&lt;/a&gt;第 11 步：安装 Loki 和 Promtail
&lt;/h3&gt;&lt;p&gt;编写 Loki 配置文件（loki-values.yaml）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;autoscaling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;maxReplicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;minReplicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;targetCPUUtilizationPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;70&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;targetMemoryUtilizationPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;backend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;persistence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;10Gi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;local&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可选新增：就绪探针（解决之前Pod卡死问题，不影响原有配置）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;readinessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;canary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;grafanaAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2.9.2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;loki&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;auth_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retention_period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;720h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schemaConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;24h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;index_&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;object_store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v11&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;boltdb-shipper&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;access_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;U4DwltABIX8p20aONyoY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bucketnames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;chunks &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 匹配MinIO已创建的chunks桶&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio:9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 保留你原有简写地址，不改动&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;insecure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secret_access_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;9YZInPYCqXwerS0NE6PDGrxo9g0l4akt2fs0IJNm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3forcepathstyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# MinIO必需的核心配置&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可选新增：就绪探针（解决之前Pod卡死问题，不影响原有配置）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;readinessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;1000m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;2Gi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;300m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;768Mi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;write&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;persistence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;20Gi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;local&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 可选新增：就绪探针（解决之前Pod卡死问题，不影响原有配置）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;readinessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署 Loki&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 部署Loki
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm install loki grafana/loki -n monitoring \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; -f loki-values.yaml \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --version 5.36.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Loki ConfigMap:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;config.yaml&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;|2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;auth_enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;common&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;compactor_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;loki-backend&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path_prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/var/loki&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;replication_factor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bucketnames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;chunks&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO集群内服务地址&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio.monitoring.svc.cluster.local:9000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO的Access Key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;access_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;U4DwltABIX8p20aONyoY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO的Secret Key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secret_access_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;9YZInPYCqXwerS0NE6PDGrxo9g0l4akt2fs0IJNm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 修正：MinIO未开启HTTPS，改为true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;insecure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 修正：MinIO必须开启路径风格，改为true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3forcepathstyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;frontend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;scheduler_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;query-scheduler-discovery.monitoring.svc.cluster.local.:9095&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;frontend_worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;scheduler_address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;query-scheduler-discovery.monitoring.svc.cluster.local.:9095&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;index_gateway&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ring&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enforce_metric_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_cache_freshness_per_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;10m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reject_old_samples&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;reject_old_samples_max_age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;168h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;retention_period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;720h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;split_queries_by_interval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;15m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memberlist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;join_members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;loki-memberlist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;query_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;align_queries_with_step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ruler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bucketnames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ruler&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO集群内服务地址&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;minio.monitoring.svc.cluster.local:9000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO的Access Key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;access_key_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;U4DwltABIX8p20aONyoY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 新增：MinIO的Secret Key&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secret_access_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;9YZInPYCqXwerS0NE6PDGrxo9g0l4akt2fs0IJNm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 修正：MinIO未开启HTTPS，改为true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;insecure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 修正：MinIO必须开启路径风格，改为true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;s3forcepathstyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/etc/loki/runtime-config/runtime-config.yaml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schema_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;24h&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;index_&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;object_store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v11&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;boltdb-shipper&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;grpc_listen_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;9095&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;http_listen_port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3100&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storage_config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hedging&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;250ms&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;max_per_second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;up_to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ConfigMap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;meta.helm.sh/release-name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;meta.helm.sh/release-namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;monitoring&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;creationTimestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2025-11-24T08:14:03Z&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app.kubernetes.io/instance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app.kubernetes.io/managed-by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Helm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app.kubernetes.io/name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;84&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app.kubernetes.io/version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2.9.2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;85&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;helm.sh/chart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki-5.36.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;86&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;loki&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;87&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;monitoring&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;88&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resourceVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8228076&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;89&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;103b336c-fcb1-4516-85d8-76d45ca6c79d&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证 Loki 部署&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看Loki核心组件（read/write/backend均需Running）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl get pods -n monitoring &lt;span class="p"&gt;|&lt;/span&gt; grep -E &lt;span class="s2"&gt;&amp;#34;loki-read|loki-write|loki-backend&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 示例输出：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# loki-backend-0 2/2 Running 0 5m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# loki-read-546cd5b67c-dsb84 1/1 Running 0 5m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# loki-write-0 1/1 Running 0 5m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署 Promtail（日志采集代理）&lt;/p&gt;
&lt;p&gt;编写 Promtail 配置文件（promtail-values.yaml）&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对接Loki的write服务（集群内服务名解析）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;clients&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://loki-write:3100/loki/api/v1/push&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 日志采集规则（采集K8s Pod日志）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;scrape_configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;job_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kubernetes-pods&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pod &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 基于Pod自动发现&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;relabel_configs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 添加命名空间标签&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;source_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;__meta_kubernetes_pod_namespace]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;replace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target_label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;namespace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 添加Pod名称标签&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;source_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;__meta_kubernetes_pod_name]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;replace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target_label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pod&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 添加容器名称标签&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;source_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;__meta_kubernetes_pod_container_name]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;replace&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target_label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 过滤掉无需采集的系统组件（可选，按需调整）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;source_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;__meta_kubernetes_pod_namespace]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;kube-system|istio-system&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;drop&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 部署模式：DaemonSet（每个节点1个副本）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;daemonset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;extraArgs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;max-open-files=1000000 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 增加文件打开限制&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 健康检查配置&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;readinessProbe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeoutSeconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 权限配置（解决日志目录读取权限问题）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;securityContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runAsUser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 以root用户运行&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runAsGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;fsGroup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;allowPrivilegeEscalation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;DAC_READ_SEARCH &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 增加目录读取权限&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 资源配置（低资源占用）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;50m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;64Mi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;200m&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;256Mi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 禁用非必需组件&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;serviceMonitor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;prometheusRule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;部署 Promtail&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 部署Promtail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;helm install promtail grafana/promtail -n monitoring \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; -f promtail-values.yaml \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --version 5.36.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证 Promtail 部署&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看Promtail Pod（每个节点1个副本，均需Running）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl get pods -n monitoring &lt;span class="p"&gt;|&lt;/span&gt; grep promtail
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 查看Promtail日志（确认无报错，有日志推送记录）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PROMTAIL_POD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;kubectl get pods -n monitoring -l &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;promtail -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;kubectl logs -n monitoring &lt;span class="nv"&gt;$PROMTAIL_POD&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;&amp;#34;Successfully sent batch&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 示例输出：level=info ts=xxx caller=client.go:347 msg=&amp;#34;Successfully sent batch&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="最终验证"&gt;&lt;a href="#%e6%9c%80%e7%bb%88%e9%aa%8c%e8%af%81" class="header-anchor"&gt;&lt;/a&gt;最终验证
&lt;/h2&gt;&lt;h3 id="验证-gpu-operator--gpu-metrics-是否正常"&gt;&lt;a href="#%e9%aa%8c%e8%af%81-gpu-operator--gpu-metrics-%e6%98%af%e5%90%a6%e6%ad%a3%e5%b8%b8" class="header-anchor"&gt;&lt;/a&gt;验证 GPU Operator &amp;amp; GPU metrics 是否正常
&lt;/h3&gt;&lt;p&gt;看 GPU Operator 相关 Pod：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl -n gpu-operator get pods
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl -n gpu-operator get ds
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;通常会看到类似：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;驱动 DaemonSet&lt;/li&gt;
&lt;li&gt;Device Plugin DaemonSet&lt;/li&gt;
&lt;li&gt;nvidia-dcgm-exporter 或类似名字的 DaemonSet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;找到 DCGM Exporter 暴露出来的 Service：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl -n gpu-operator get svc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;里面一般会有一个和 dcgm-exporter 类似名字的 Service，对应端口 9400（Prometheus 默认端口）。&lt;/p&gt;
&lt;p&gt;本地 port-forward 看看 /metrics：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 换成你查到的 dcgm exporter 服务名&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;kubectl -n gpu-operator port-forward svc/nvidia-dcgm-exporter 9400:9400
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 打开一个新终端：&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;curl http://127.0.0.1:9400/metrics &lt;span class="p"&gt;|&lt;/span&gt; head
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;应该能看到类似：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# HELP DCGM_FI_DEV_SM_CLOCK SM clock frequency (in MHz).
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;# TYPE DCGM_FI_DEV_SM_CLOCK gauge
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;DCGM_FI_DEV_SM_CLOCK{gpu=&amp;#34;0&amp;#34;,UUID=&amp;#34;GPU-xxxx&amp;#34;} 139
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;说明 GPU 指标已经通过 DCGM Exporter 暴露出来了。&lt;/p&gt;
&lt;h3 id="轻量级测试"&gt;&lt;a href="#%e8%bd%bb%e9%87%8f%e7%ba%a7%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;轻量级测试
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 运行 GPU 测试 (显式申请 1 个 GPU)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;sudo kubectl run test-gpu-real &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; -n model-serving &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; --image&lt;span class="o"&gt;=&lt;/span&gt;vllm/vllm-openai:latest &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; --restart&lt;span class="o"&gt;=&lt;/span&gt;Never &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; --overrides&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{&amp;#34;metadata&amp;#34;: {&amp;#34;annotations&amp;#34;: {&amp;#34;sidecar.istio.io/inject&amp;#34;: &amp;#34;false&amp;#34;}}, &amp;#34;spec&amp;#34;: {&amp;#34;containers&amp;#34;: [{&amp;#34;name&amp;#34;: &amp;#34;test-gpu-real&amp;#34;, &amp;#34;image&amp;#34;: &amp;#34;vllm/vllm-openai:latest&amp;#34;, &amp;#34;command&amp;#34;: [&amp;#34;nvidia-smi&amp;#34;], &amp;#34;resources&amp;#34;: {&amp;#34;limits&amp;#34;: {&amp;#34;nvidia.com/gpu&amp;#34;: &amp;#34;1&amp;#34;}}}]}}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;kubectl logs test-gpu-real -n model-serving
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="完整测试triton--vllm"&gt;&lt;a href="#%e5%ae%8c%e6%95%b4%e6%b5%8b%e8%af%95triton--vllm" class="header-anchor"&gt;&lt;/a&gt;完整测试（triton + vllm）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;有关 Namespace&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;注意 InferenceService 的 namespace 是：model-serving&lt;/p&gt;
&lt;p&gt;为什么必须是 model-serving？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Istio 注入生效：我们之前只给 model-serving 命名空间打了 istio-injection=enabled 标签。只有部署在这个命名空间下的 Pod，才会自动拥有 Istio Sidecar（负责流量路由、Metrics 等）。&lt;/li&gt;
&lt;li&gt;资源隔离：将业务模型与系统组件（如 gpu-operator, knative-serving）分开，是生产环境的最佳实践。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;举例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;InferenceService&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;qwen-7b-chat &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 你的服务名称&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;model-serving &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 👈 这里必须写 model-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;modelFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vllm &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对应 ClusterServingRuntime 的名字&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-vllm &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 如果你有自定义 Runtime，这里指定名字&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;pvc://model-pvc/qwen-7b&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 或者 &amp;#34;s3://...&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 👈 申请 1 张显卡&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;即使 YAML 里写了 namespace，习惯上在 apply 时显式指定一下也是个好习惯（双重保险）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f isvc-llm.yaml -n model-serving
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;五、完整调用链路&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/016-0a588075.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/017-0e4fb1a7.png"&gt;&lt;/p&gt;
&lt;h1 id="六基于-argo-的-ci--cd"&gt;&lt;a href="#%e5%85%ad%e5%9f%ba%e4%ba%8e-argo-%e7%9a%84-ci--cd" class="header-anchor"&gt;&lt;/a&gt;六、基于 Argo 的 CI / CD
&lt;/h1&gt;&lt;p&gt;GitHub: &lt;a class="link" href="https://github.com/argoproj/argo-cd" target="_blank" rel="noopener"
 &gt;https://github.com/argoproj/argo-cd&lt;/a&gt; 官网：https://argoproj.github.io/&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Argo = 一套专门给 Kubernetes 用的开源工具家族，用来做 CI / CD、工作流编排、GitOps 部署、灰度发布、事件驱动等，是 CNCF 下面的毕业项目&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Argo 不是一个单一软件，而是一个 “工具矩阵”，主要包括四个子项目：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Argo Workflows&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Kubernetes 原生的 工作流 / 任务编排引擎&lt;/li&gt;
&lt;li&gt;用 CRD（自定义资源）定义 Workflow，每个步骤跑在 Pod 里，非常适合 CI 流水线、数据处理、ML 训练等批处理任务&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Argo CD&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;一个 GitOps 风格的持续交付工具&lt;/li&gt;
&lt;li&gt;通过对比 Git 仓库里的 “期望状态” 和 K8s 集群中的 “实际状态”，自动同步和回滚应用，常用来管理大规模集群配置&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Argo Rollouts&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;替代原生 Deployment 的 CRD&lt;/li&gt;
&lt;li&gt;支持 蓝绿发布、金丝雀发布，可以接入网关、监控指标做渐进式发布和自动回滚&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Argo Events&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;做 事件驱动 自动化&lt;/li&gt;
&lt;li&gt;支持各种事件源（Webhook、Kafka、S3 等），触发 Argo Workflows 或其他 K8s 资源，实现 event-driven CI / CD 或自动化任务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一句话：Argo = “围绕 Kubernetes 打造的一整套自动化 / GitOps / 发布 / 事件工具链”。&lt;/p&gt;
&lt;h2 id="argo-跟-kubernetes-是什么关系"&gt;&lt;a href="#argo-%e8%b7%9f-kubernetes-%e6%98%af%e4%bb%80%e4%b9%88%e5%85%b3%e7%b3%bb" class="header-anchor"&gt;&lt;/a&gt;Argo 跟 Kubernetes 是什么关系？
&lt;/h2&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Kubernetes 提供 “集群和基础设施”，Argo 提供 “在这个集群上自动化地干活的工具”。Argo 是 Kubernetes 最主流的 GitOps / Workflow 方案之一&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;CNCF 官方介绍中就把 Argo 定义为 “Kubernetes-native tools to run workflows, manage clusters, and do GitOps right”&lt;/p&gt;
&lt;p&gt;运行环境层面：完全依赖 K8s&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Argo 的所有组件（Controller、UI 等）都是以 Deployment / Pod 的形式部署在 Kubernetes 集群中。&lt;/li&gt;
&lt;li&gt;Argo 的核心对象（Workflow、Rollout、EventSource、Application 等）都是 Kubernetes CRD。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;职责分工：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Kubernetes 负责：调度 Pod、管理节点、网络、存储、基础监控。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Argo 负责：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把一堆任务编排成 “工作流” 并在 K8s 上跑（Workflows）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把 Git 仓库里的 YAML 自动同步到集群（CD &amp;amp; GitOps）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把发布过程做成可观测、可灰度控制的 rollouts（Rollouts）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;把外部事件变成触发器（Events）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="部署层级"&gt;&lt;a href="#%e9%83%a8%e7%bd%b2%e5%b1%82%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;部署层级
&lt;/h2&gt;&lt;p&gt;所有文件都应该放到 git 让 Argo 负责吗？&lt;/p&gt;
&lt;p&gt;不是的，这涉及到 “部署层级” 的问题。在云原生的 GitOps 实践中，我们将部署分为了两个截然不同的层级：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;层级一：平台基础设施层&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;包含组件：Istio, Knative Serving, KServe, Cert-Manager, Nvidia Device Plugin 等。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;变动低频：装好后很少动，顶多几个月升级一次版本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;全局影响：一旦挂了，所有模型全挂。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理者：平台运维工程师 / SRE。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署方式：通常使用 Helm Chart 或 Operator。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;层级二：应用负载层&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;包含组件：InferenceService (模型), gateway (网关), ConfigMap (业务配置)。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;特点：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;变动高频：每天可能有新模型上线，或者修改版本、调整并发参数。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;局部影响：配置错了只影响这一个模型。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;管理者：算法工程师 / MLOps 工程师。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署方式：YAML 文件 (InferenceService)。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;所以目前来看，层级二的内容要以放到 git 中由 argo 管理 CD。层级一的也可以放到 git 中，但手动运维。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="实践"&gt;&lt;a href="#%e5%ae%9e%e8%b7%b5" class="header-anchor"&gt;&lt;/a&gt;实践
&lt;/h2&gt;&lt;p&gt;Jenkins 本身是可以做全套的 CI + CD 的，但从我们推理服务部署这件事上来讲，CD (持续部署) 并不适合用 Jenkins，而适合用 Argo。Jenkins 在我们的这个场景下可以继续做它擅长的 CI (持续集成)，但想了想，没必要那么麻烦，全部用 Argo 结合 Git 就完全能搞定，而且很方便，不适合用 jenkins 再增加运维复杂度了。&lt;/p&gt;
&lt;p&gt;ArgoCD 是云原生时代的王者（GitOps 流）&lt;/p&gt;
&lt;h3 id="实操-for-triton预演"&gt;&lt;a href="#%e5%ae%9e%e6%93%8d-for-triton%e9%a2%84%e6%bc%94" class="header-anchor"&gt;&lt;/a&gt;实操 for Triton（预演）
&lt;/h3&gt;&lt;p&gt;假设：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Git 仓库地址：git@github.com:your-name/ai-ops.git&lt;/li&gt;
&lt;li&gt;S3 Bucket：my-ai-models&lt;/li&gt;
&lt;li&gt;EKS 命名空间：ai-serving&lt;/li&gt;
&lt;li&gt;自定义 Docker 镜像 &amp;ndash;&amp;gt; 镜像仓库&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;第一阶段：基础设施与权限准备 (一次性工作)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这部分工作通常不需要经常变动，主要是为了打通 K8s 和 S3 的权限，以及准备 Git 仓库。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建 Namespace (如果还没建)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl create namespace ai-serving
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;配置 S3 访问凭证 (Secrets) 注意：敏感信息不要直接上传到 Git。 我们先用 kubectl 手动创建 Secret（或者使用 ExternalSecrets / SealedSecrets 等高级方案，但现在先用简单直接的方式）。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;准备一个 s3-secret.yaml 在你本地（ 不要提交到 Git）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Secret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-s3-secret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serving.kserve.io/s3-endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;s3.amazonaws.com&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# AWS S3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serving.kserve.io/s3-region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;us-east-1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 你的 Region&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Opaque&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;stringData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你的AK&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;你的SK&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行应用：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f s3-secret.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="3"&gt;
&lt;li&gt;准备 Git 仓库目录结构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在你的 ai-ops Git 仓库中，创建一个专门存放 ASR 部署文件的目录，例如 apps/asr-service/overlays/prod (如果是 Kustomize 结构) 或者直接 manifests/asr-service。&lt;/p&gt;
&lt;p&gt;建议结构如下&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;manifests/asr-service/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;├── service-account.yaml # 关联 Secret 的账号配置
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;└── inference-service.yaml # 核心模型服务配置
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;编写 manifests/asr-service/service-account.yaml 并提交到 Git：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ServiceAccount&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sa-s3-access&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;secrets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;my-s3-secret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 引用刚才手动创建的 Secret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第二阶段：模型工件准备 (模型上线 / 更新时操作)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个阶段是 “搬运工” 工作，把模型传上去，让 KServe 有东西可拉。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地整理 Triton 结构&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如之前所述，确保本地目录结构正确：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;simple-asr/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;├── config.pbtxt
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;└── 1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; └── model.onnx
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;上传到 S3&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;使用 AWS CLI 或手动上传。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;# 假设上传到 bucket 的 triton-repo 目录下
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;aws s3 cp --recursive simple-asr/ s3://my-ai-models/triton-repo/simple-asr/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;验证： 确保 s3://my-ai-models/triton-repo/simple-asr/config.pbtxt 存在。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三阶段：Argo CD 配置与部署 (GitOps 核心)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是让 Argo CD 接管部署的关键步骤。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;编写 InferenceService 配置文件&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在 Git 仓库的 manifests/asr-service/inference-service.yaml 中写入：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;InferenceService&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;asr-service&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 稍微改动这个字段可以触发 Argo 重新同步和 Pod 重启，常用于强制重新拉取模型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serving.kserve.io/model-version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;v1-20231121&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceAccountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sa-s3-access&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;modelFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;onnx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-tritonserver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3://my-ai-models/triton-repo/simple-asr&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;提交代码到 Git：&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;git commit -m &lt;span class="s2"&gt;&amp;#34;Add ASR inference service&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;git push
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;创建 Argo CD Application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你需要告诉 Argo CD：“去监控我的 Git 仓库，把东西部署到 EKS 里”。&lt;/p&gt;
&lt;p&gt;你可以通过 Argo CD 的 Web UI 点击 &amp;ldquo;New App&amp;rdquo; 创建，或者写一个 YAML 文件（推荐 YAML 方式，这叫 App-of-Apps 模式）。&lt;/p&gt;
&lt;p&gt;创建一个文件 asr-argocd-app.yaml (手动 apply 这个文件)：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argoproj.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Application&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;asr-serving-app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argocd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;repoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://github.com/your-name/ai-ops.git&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 你的 Git 地址&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;targetRevision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;HEAD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;manifests/asr-service &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 你的 YAML 所在目录&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://kubernetes.default.svc&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 开启自动同步和自愈&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;automated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prune&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Git 里删了文件，K8s 里也删掉&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selfHeal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 手动改了 K8s 配置，Argo 会强制改回来&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;CreateNamespace=true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f asr-argocd-app.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第四阶段：验证与观察&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;一旦应用了上面的 Application YAML，奇迹就开始了：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;观察 Argo CD UI：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;你会看到 asr-serving-app 变成 Processing 状态。&lt;/li&gt;
&lt;li&gt;它会画出一棵树：Application -&amp;gt; InferenceService -&amp;gt; Knative Configuration -&amp;gt; Revision -&amp;gt; Deployment -&amp;gt; Pod。&lt;/li&gt;
&lt;li&gt;确保所有图标变绿（Healthy 和 Synced）。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;观察 Pod 状态 (命令行)：&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl get pods -n ai-serving
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;你会看到类似 asr-service-predictor-00001-deployment-xxx 的 Pod。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果是 Init:0 / 1：正在运行 storage-initializer 下载 S3 模型。&lt;/li&gt;
&lt;li&gt;如果是 Running：模型下载完毕，Triton 启动成功&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;日常开发流程&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这套系统搭建好后，以后的日常工作流就是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;算法同学：训练新模型 -&amp;gt; 导出 ONNX -&amp;gt; 上传覆盖 S3 上的 model.onnx。&lt;/li&gt;
&lt;li&gt;运维 / 算法同学：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;修改 Git 里的 inference-service.yaml。&lt;/li&gt;
&lt;li&gt;比如修改 annotations 里的 version: &amp;ldquo;v2&amp;rdquo; 或者修改资源配额。&lt;/li&gt;
&lt;li&gt;git push。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Argo CD：自动检测到 Git 变化 -&amp;gt; 更新 K8s 资源 -&amp;gt; Knative 滚动更新 -&amp;gt; 新 Pod 拉取新模型 -&amp;gt; 流量平滑切换。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这就是最标准的 GitOps 模型部署流程。&lt;/p&gt;
&lt;h3 id="实操-for-vllm预演"&gt;&lt;a href="#%e5%ae%9e%e6%93%8d-for-vllm%e9%a2%84%e6%bc%94" class="header-anchor"&gt;&lt;/a&gt;实操 for vLLM（预演）
&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;第一阶段：基础设施准备 (一次性工作)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为 KServe 可能不知道怎么启动 vLLM，我们需要先在集群里注册一个 “说明书”，告诉 KServe：“当我说用 vllm 时，请拉取这个镜像并运行这个命令”。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建 vLLM 的 ClusterServingRuntime&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;将以下内容保存为 vllm-runtime.yaml 并 kubectl apply -f（或者放入 ArgoCD 管理的基础设施 Git 仓库中）。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterServingRuntime&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-vllm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prometheus.kserve.io/path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/metrics&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prometheus.kserve.io/port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8000&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;containers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-container&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vllm/vllm-openai:latest &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 使用 vLLM 官方镜像&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;-m&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;vllm.entrypoints.openai.api_server&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 这里的 args 是默认值，会被 InferenceService 里的 args 覆盖或追加&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;port=8080&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;model=/mnt/models&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;served-model-name=default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;trust-remote-code&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;HF_HOME&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;/tmp/hf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;4&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;32Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;注意：vLLM 默认监听 8000，但 KServe 容器通常要求监听 8080，所以我们在 args 里强制指定 &amp;ndash;port = 8080。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;第二阶段：模型上传 (S3)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;vLLM 不需要 Triton 那种 1 / model.onnx 的结构。它只需要标准的 HuggingFace 模型文件夹。&lt;/p&gt;
&lt;p&gt;假设你要部署 Qwen2-7B：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地准备&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你需要把 HuggingFace 上的文件下载下来，目录结构大概长这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;Qwen2-7B/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;├── config.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;├── tokenizer.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;├── model-00001-of-00004.safetensors
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;├── ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;└── model.safetensors.index.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;上传 S3&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;aws s3 cp --recursive Qwen2-7B/ s3://my-ai-models/llm/Qwen2-7B/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第三阶段：Argo CD 部署配置 (GitOps)&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 Git 仓库中（manifests/llm-service/），编写 inference-service.yaml。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里有几个关键点需要注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;runtime: 指定刚才创建的 kserve-vllm。&lt;/li&gt;
&lt;li&gt;storageUri: 指向 S3 文件夹。KServe 会把这里面的所有文件下载到 Pod 的 /mnt/models 目录下。&lt;/li&gt;
&lt;li&gt;args: 我们需要覆盖启动参数，告诉 vLLM 模型就在 /mnt/models。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;serving.kserve.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;InferenceService&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;qwen-llm&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# LLM 启动很慢（加载权重需要时间），必须调大健康检查超时时间，否则会被 K8s 杀掉&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serving.knative.dev/progressDeadline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;20m&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 自动扩缩容配置 (LLM通常基于并发或请求数)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;5&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/minScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 建议 LLM 至少保留1个，因为冷启动太慢了&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;autoscaling.knative.dev/maxScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;predictor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;serviceAccountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;sa-s3-access&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 别忘了 S3 权限账号&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;modelFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pytorch &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;kserve-vllm&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对应 ClusterServingRuntime 的名字&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# KServe 会把这个 S3 路径下的内容下载到容器的 /mnt/models&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;storageUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;s3://my-ai-models/llm/Qwen2-7B&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 核心参数配置&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;model=/mnt/models &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 必填：指向下载好的模型路径&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;served-model-name=qwen&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 服务名称，API调用时用到&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;gpu-memory-utilization=0.9&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 显存占用率&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;max-model-len=4096 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 上下文长度，防止 OOM&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- --&lt;span class="l"&gt;dtype=float16 &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 或 bfloat16&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;8&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;32Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 必须有 GPU&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;limits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;16&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;64Gi&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nvidia.com/gpu&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;nodeSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gpu-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;A100&amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 建议指定节点类型&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;提交到 Git，Argo CD 检测到后会自动同步。&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;创建 Argo CD Application&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你可以通过 Argo CD 的 Web UI 点击 &amp;ldquo;New App&amp;rdquo; 创建，或者写一个 YAML 文件（推荐 YAML 方式，这叫 App-of-Apps 模式）。&lt;/p&gt;
&lt;p&gt;创建一个文件 llm-argocd-app.yaml (手动 apply 这个文件)：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argoproj.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Application&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;llm-serving-app &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 应用名称，要在 Argo 面板上显示的&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argocd &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ArgoCD 安装的命名空间&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;repoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://github.com/your-name/ai-ops.git&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 你的 Git 仓库&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;targetRevision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;HEAD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;manifests/llm-service &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ✅ 关键点：指向存放 vLLM InferenceService 的目录&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://kubernetes.default.svc&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ai-serving &lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 部署的目标命名空间&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 启用自动同步，Git 变了 K8s 自动变&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;automated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prune&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Git 里删了，K8s 也删&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selfHeal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# K8s 里被改了，强制还原回 Git 的状态&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;CreateNamespace=true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 如果 ai-serving 命名空间不存在，自动创建&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl apply -f llm-argocd-app.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;第四阶段：部署后的验证与调用&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;vLLM 启动成功后，它提供的是 OpenAI Compatible API。这意味着你可以直接用 OpenAI 的 SDK 或者 curl 来调用，这比 Triton 的 gRPC 接口对开发者更友好。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;验证 Pod 状态&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl get pods -n ai-serving
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 等待状态变为 Running (可能需要几分钟下载模型和加载权重)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;kubectl logs -f &amp;lt;pod-name&amp;gt; -c kserve-container -n ai-serving
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 看到日志显示 &amp;#34;Uvicorn running on http://0.0.0.0:8080&amp;#34; 即成功&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ol start="2"&gt;
&lt;li&gt;调用测试 (在集群内部或通过 Ingress)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;获取服务的 URL&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;kubectl get isvc qwen-llm -n ai-serving
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 假设 URL 是 http://qwen-llm.ai-serving.svc.cluster.local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;发送请求（完全兼容 OpenAI 格式）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;curl http://qwen-llm.ai-serving.svc.cluster.local/v1/chat/completions &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; -H &lt;span class="s2"&gt;&amp;#34;Content-Type: application/json&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; -d &lt;span class="s1"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;model&amp;#34;: &amp;#34;qwen&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;messages&amp;#34;: [
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; {&amp;#34;role&amp;#34;: &amp;#34;system&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;You are a helpful assistant.&amp;#34;},
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; {&amp;#34;role&amp;#34;: &amp;#34;user&amp;#34;, &amp;#34;content&amp;#34;: &amp;#34;你好，介绍一下你自己。&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; &amp;#34;max_tokens&amp;#34;: 100
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;vLLM 流程的关键 Checklist&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;ClusterServingRuntime: 你的集群里如果没有 kserve-vllm 定义，第一步就会报错，必须先加这个 CRD。&lt;/li&gt;
&lt;li&gt;Timeouts: LLM 动辄 20GB+，下载 + 加载显存需要很久。一定要在 annotations 里设置 progressDeadline 为 20m 或更长，否则 Knative 会以为部署失败并回滚。&lt;/li&gt;
&lt;li&gt;Arguments: 必须通过 args 显式指定 &amp;ndash;model=/mnt/models，因为这是 KServe storageUri 下载的目标路径。&lt;/li&gt;
&lt;li&gt;Resources: 显存和内存给够，否则 vLLM 会报 OOM（Out Of Memory）并 CrashLoopBackOff。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这套流程结合 ArgoCD 后，以后更新 LLM 版本（比如从 Qwen2 换到 Qwen2.5），你只需要：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;上传新模型到 S3 的新目录。&lt;/li&gt;
&lt;li&gt;修改 Git 里的 storageUri。&lt;/li&gt;
&lt;li&gt;ArgoCD 自动同步，Knative 会等待新 Pod 里的 vLLM 完全加载好权重后，才切断旧 Pod 的流量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="七如何衡量平台是否成功"&gt;&lt;a href="#%e4%b8%83%e5%a6%82%e4%bd%95%e8%a1%a1%e9%87%8f%e5%b9%b3%e5%8f%b0%e6%98%af%e5%90%a6%e6%88%90%e5%8a%9f" class="header-anchor"&gt;&lt;/a&gt;七、如何衡量平台是否成功？
&lt;/h1&gt;&lt;p&gt;一个优秀的模型服务平台，其核心指标应该覆盖 性能、成本、稳定性 几个维度。&lt;/p&gt;
&lt;h2 id="维度一性能与延迟-performance--latency---我们的服务快不快"&gt;&lt;a href="#%e7%bb%b4%e5%ba%a6%e4%b8%80%e6%80%a7%e8%83%bd%e4%b8%8e%e5%bb%b6%e8%bf%9f-performance--latency---%e6%88%91%e4%bb%ac%e7%9a%84%e6%9c%8d%e5%8a%a1%e5%bf%ab%e4%b8%8d%e5%bf%ab" class="header-anchor"&gt;&lt;/a&gt;维度一：性能与延迟 (Performance &amp;amp; Latency) - “我们的服务快不快？”
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;端到端延迟 (End-to-End Latency) - P95 / P99&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 从业务应用发出 API 请求，到收到完整响应的总时间。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量用户体验的黄金标准。我们通常关注 P95（95% 的请求都快于此值）和 P99，因为平均值会掩盖那些最慢的、最影响用户的请求。&lt;/li&gt;
&lt;li&gt;如何衡量？ 从 Istio Gateway 或 Prometheus 中间件获取。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;首 Token 时间 (Time to First Token - TTFT) - (LLM 专属)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 对于生成式模型，从发出请求到收到第一个有意义的 token 所需的时间。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量 LLM 服务 “感知响应速度” 的最关键指标。一个低 TTFT 的模型会让用户感觉 “反应很快”，即使生成全文总时间较长。&lt;/li&gt;
&lt;li&gt;如何衡量？ 需要在客户端或 Transformer 中进行定制化测量&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;每输出 Token 时间 (Time Per Output Token - TPOT) - (LLM 专属)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 生成每个后续 token 的平均时间。它是 (总时间 - TTFT) / (总 token 数 - 1)。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量 LLM “生成速度” 的核心指标。一个低的 TPOT 意味着模型的 “吐字” 速度很快，用户体验流畅。&lt;/li&gt;
&lt;li&gt;如何衡量？ 客户端或 Transformer 中计算。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;吞吐量 (Throughput)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 单位时间内平台能成功处理的请求数。通常用 RPS (Requests Per Second) 或 QPS (Queries Per Second) 表示。&lt;/li&gt;
&lt;li&gt;对于 LLM，一个更有意义的指标可能是 输出 Tokens/秒 (Output Tokens/Second)，因为它综合了并发处理能力和生成速度。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量平台容量和处理能力的上限。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="维度二成本与效率-cost--efficiency---我们的钱花得值不值"&gt;&lt;a href="#%e7%bb%b4%e5%ba%a6%e4%ba%8c%e6%88%90%e6%9c%ac%e4%b8%8e%e6%95%88%e7%8e%87-cost--efficiency---%e6%88%91%e4%bb%ac%e7%9a%84%e9%92%b1%e8%8a%b1%e5%be%97%e5%80%bc%e4%b8%8d%e5%80%bc" class="header-anchor"&gt;&lt;/a&gt;维度二：成本与效率 (Cost &amp;amp; Efficiency) - “我们的钱花得值不值？”
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;GPU 利用率 (GPU Utilization - Compute)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ GPU 计算核心在单位时间内的繁忙程度百分比。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量 “GPU 是否在干活” 的首要指标。一个持续低于 20% 的利用率可能意味着巨大的资源浪费。&lt;/li&gt;
&lt;li&gt;如何衡量？ 通过 NVIDIA DCGM Exporter 在 Prometheus 中采集。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;GPU 显存利用率 (GPU Memory Utilization)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ GPU 显存被占用的百分比。&lt;/li&gt;
&lt;li&gt;为什么重要？ 很多模型（尤其是 LLM）可能计算量不大，但会占用海量显存。高显存占用会限制单卡能部署的模型数量。这是成本优化的另一个关键。&lt;/li&gt;
&lt;li&gt;如何衡量？ 通过 NVIDIA DCGM Exporter 采集。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;闲置实例数 / 缩容至零频率 (Scale-to-Zero Metrics)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 平台上有多少模型服务实例处于 0 副本状态，以及它们进入和退出 0 副本状态的频率。&lt;/li&gt;
&lt;li&gt;为什么重要？ 直接体现了 KServe + Knative Serverless 架构带来的成本节省效果。&lt;/li&gt;
&lt;li&gt;如何衡量？ 从 Knative 的监控指标中获取。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;冷启动延迟 (Cold Start Latency)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 当一个服务从 0 副本状态接收到第一个请求时，从开始拉起 Pod 到成功响应请求的总时间。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是 Serverless 模式为了节省成本而付出的性能代价。你需要监控并优化它，确保它在可接受的范围内。&lt;/li&gt;
&lt;li&gt;如何衡量？ 结合 Knative 指标和应用日志进行分析。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="维度三稳定性与可用性-stability--availability--我们的服务稳不稳"&gt;&lt;a href="#%e7%bb%b4%e5%ba%a6%e4%b8%89%e7%a8%b3%e5%ae%9a%e6%80%a7%e4%b8%8e%e5%8f%af%e7%94%a8%e6%80%a7-stability--availability--%e6%88%91%e4%bb%ac%e7%9a%84%e6%9c%8d%e5%8a%a1%e7%a8%b3%e4%b8%8d%e7%a8%b3" class="header-anchor"&gt;&lt;/a&gt;维度三：稳定性与可用性 (Stability &amp;amp; Availability)- “我们的服务稳不稳？”
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;服务可用性 (Availability)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 在规定时间内，服务能够正常响应的请求比例。通常目标是 99.9% 或 99.99%。&lt;/li&gt;
&lt;li&gt;为什么重要？ 这是衡量服务可靠性的最终标准。&lt;/li&gt;
&lt;li&gt;如何衡量？ (成功请求数 / 总请求数) * 100%。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;错误率 (Error Rate)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 返回 5xx（服务器错误）状态码的请求比例。&lt;/li&gt;
&lt;li&gt;为什么重要？ 错误率的飙升是服务出现严重问题的最直接信号。需要设置告警。&lt;/li&gt;
&lt;li&gt;如何衡量？ 从 Istio Gateway 或 Prometheus 中间件获取。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Pod 重启次数 (Pod Restart Count)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;是什么？ 模型服务 Pod 的重启次数。&lt;/li&gt;
&lt;li&gt;为什么重要？ 频繁的重启（特别是 CrashLoopBackOff 状态）表明代码存在 Bug、内存溢出（OOM Killed）或配置错误。&lt;/li&gt;
&lt;li&gt;如何衡量？ 从 Kubernetes API 直接获取。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;短期看，最重要的指标有这几个：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;端到端延迟 (End-to-End Latency)&lt;/li&gt;
&lt;li&gt;首 Token 时间 (Time to First Token - TTFT)&lt;/li&gt;
&lt;li&gt;吞吐量 (Throughput)&lt;/li&gt;
&lt;li&gt;GPU 利用率&lt;/li&gt;
&lt;li&gt;服务可用性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-11-30-ai-mo-xing-tui-li-ping-tai-jia-gou-she-ji-yu-shi-jian/018-482b1ef1.png"&gt;&lt;/p&gt;
&lt;p&gt;长期看其实还要加上模型效果指标，量化 “准确率” 与 “生成质量”。&lt;/p&gt;</description></item><item><title>Shopify 构建生产级 Agentic 系统的方法分析</title><link>https://xiaobox.github.io/p/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/</link><pubDate>Wed, 01 Oct 2025 02:12:18 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/cover.jpg" alt="Featured image of post Shopify 构建生产级 Agentic 系统的方法分析" /&gt;&lt;h1 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h1&gt;&lt;p&gt;本文是对 Shopify 应用机器学习总监 Andrew McNamara 的博客 《Building Production-Ready Agentic Systems: Lessons from Shopify Sidekick》的详细分析，主要包括 Shopify 在构建 agentic 系统时所面临的工程挑战与最佳实践。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Andrew McNamara 在助手开发领域已有超过15年的经验。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h1 id="sidekick"&gt;&lt;a href="#sidekick" class="header-anchor"&gt;&lt;/a&gt;Sidekick
&lt;/h1&gt;&lt;h2 id="sidekick-是什么"&gt;&lt;a href="#sidekick-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;Sidekick 是什么
&lt;/h2&gt;&lt;p&gt;Sidekick 是 Shopify 官方内置的 AI 商务助理，嵌在你的 Shopify 管理后台（Admin）里，通过聊天对话来解答问题、给出操作指引、直接执行部分店铺任务（在你确认后），属于 Shopify 的 AI 体系 Shopify Magic 的核心能力之一。&lt;/p&gt;
&lt;h2 id="sidekick-能做什么"&gt;&lt;a href="#sidekick-%e8%83%bd%e5%81%9a%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;Sidekick 能做什么
&lt;/h2&gt;&lt;p&gt;●导航与操作指导：一句话让它带你到正确的后台页面，或给出分步操作卡片（如设置国际运费、连接域名等）。&lt;/p&gt;
&lt;p&gt;●内容生成与填写：在 Admin 的表单里直接帮你填文案（邮件、产品、集合、折扣等），填过的字段会高亮，便于你审核后应用。&lt;/p&gt;
&lt;p&gt;●营销与客户：用自然语言创建客户分群、折扣（金额/订单/免邮/买 X 送 Y）。&lt;/p&gt;
&lt;p&gt;●主题样式调整：在主题编辑器中按目标风格（如 “更复古”）建议并修改主题设置；你手动保存后生效。&lt;/p&gt;
&lt;p&gt;●数据洞察：生成简单报表 / 图表，甚至帮你写 ShopifyQL 查询来查看销售、访问等。&lt;/p&gt;
&lt;p&gt;●元数据管理：创建/更新 metafield 与 metaobject（比如新增/更新 “达人” 条目）。&lt;/p&gt;
&lt;p&gt;●应用发现与安装：在聊天里推荐、对比并发起安装合适的 App。&lt;/p&gt;
&lt;p&gt;●图片生成功能：根据文字 / 参考图生成横幅、海报素材。&lt;/p&gt;
&lt;p&gt;●移动端补充：在手机端也可用，并能引导完成 3D 扫描、条码打印、Tap to Pay 等移动相关任务。&lt;/p&gt;
&lt;h1 id="关键工程挑战与应对策略"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e5%b7%a5%e7%a8%8b%e6%8c%91%e6%88%98%e4%b8%8e%e5%ba%94%e5%af%b9%e7%ad%96%e7%95%a5" class="header-anchor"&gt;&lt;/a&gt;关键工程挑战与应对策略
&lt;/h1&gt;&lt;p&gt;Shopify 在开发其商家助手 Sidekick 过程中，遇到了多方面的工程挑战，包括系统可靠性、LLM 推理控制以及工具集成扩张带来的复杂性等。为打造可在生产环境稳定运行的智能代理，团队针对这些挑战提出了一系列解决方案和最佳实践。&lt;/p&gt;
&lt;h2 id="1-工具集成扩张导致的复杂性激增"&gt;&lt;a href="#1-%e5%b7%a5%e5%85%b7%e9%9b%86%e6%88%90%e6%89%a9%e5%bc%a0%e5%af%bc%e8%87%b4%e7%9a%84%e5%a4%8d%e6%9d%82%e6%80%a7%e6%bf%80%e5%a2%9e" class="header-anchor"&gt;&lt;/a&gt;1. 工具集成扩张导致的复杂性激增
&lt;/h2&gt;&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/001-505b901f.png"&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;随着可用工具数量从不到 20 个增至 50 + 个，Agentic 系统的复杂度显著提高。早期（0-20 个工具）每个工具职责清晰，行为可预测；中期（20-50 个）工具边界开始模糊，工具组合出现意外结果；后期（50 + 个）不同工具可实现相同任务，系统行为难以推理和维护。这种现象被团队戏称为 “Death by a Thousand Instructions”（千指令之死）。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Sidekick 初期仅支持少量工具，系统行为简单可控。但随着功能扩展，集成的工具激增至数十个后，出现了工具集成的规模化挑战。工具数量越多，越容易出现职责重叠和边界不清的问题，多种工具路径可以实现相似任务，导致 Agent 难以选择最佳行动，行为开始变得不可预测。Shopify 团队发现，他们不得不在系统提示（system prompt）里堆叠大量针对各个工具和边缘情况的特殊指令，以致提示词变成了充满冲突规则和特例的 “大杂烩”。这种指令爆炸现象不仅拖慢了模型推理速度，也令系统几乎无法维护 。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/002-d36024dd.png"&gt;&lt;/p&gt;
&lt;p&gt;为应对这一复杂性挑战，Shopify 引入了 “Just-in-Time (JIT) 指令” 技术，即 按需即时注入指导。代替将所有工具使用说明和特殊规则预先塞入系统提示，他们改为在恰当的时机提供当前情境相关的指令。具体而言，Sidekick 会在每次工具调用前后动态地附加该工具所需的指导信息，并提供给 LLM，从而为每一步决策定制最精简完备的上下文。通过这种方法，模型在处理诸如 “查询多伦多客户” 这样的请求时，只会收到与 “数据查询” 相关的指令和工具信息；若用户请求撰写产品 SEO 描述，则只注入与 “内容生成” 和相应产品上下文有关的指导。JIT 指令让指导信息与工具数据同步出现，确保 “不多一字、不少一字” 地提供恰到好处的上下文 。&lt;/p&gt;
&lt;p&gt;JIT 思路的最小可运行示例：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;⚡&lt;/span&gt; &lt;span class="n"&gt;python片段import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 简化的 base prompt（短且稳定）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;BASE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;You are Sidekick, a helpful assistant.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;Core rules:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;- Be concise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;- Don&amp;#39;t invent data.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;- If action requires DB or tool, return a JSON action object: {&amp;#34;action&amp;#34;:&amp;#34;tool_name&amp;#34;,&amp;#34;args&amp;#34;:{...}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 每个工具的 JIT 模板（放在配置文件里更好）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;TOOL_TEMPLATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;db_query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dedent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; TOOL: db_query
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Purpose: Generate a safe, parameterized SQL query to satisfy the user request.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; DB schema (customers): id:int, name:str, city:str, email:str, status:str
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Rules:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 1) Only use fields: id, name, email.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 2) Always return a JSON object: {{ &amp;#34;sql&amp;#34;: &amp;#34;...&amp;#34;, &amp;#34;params&amp;#34;: {{}} }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 3) Do NOT embed variables directly — use parameter placeholders.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Example output:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; {{ &amp;#34;sql&amp;#34;: &amp;#34;SELECT id,name,email FROM customers WHERE city = :city AND status = :status&amp;#34;, &amp;#34;params&amp;#34;: {{ &amp;#34;city&amp;#34;: &amp;#34;Toronto&amp;#34;, &amp;#34;status&amp;#34;:&amp;#34;ENABLED&amp;#34; }} }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;seo_write&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;textwrap&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dedent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; TOOL: seo_write
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Purpose: Produce SEO title and meta description for product.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Rules:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 1) Tone: &lt;/span&gt;&lt;span class="si"&gt;{tone}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 2) Max meta length: &lt;/span&gt;&lt;span class="si"&gt;{max_meta_chars}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 3) Required keywords (inject): &lt;/span&gt;&lt;span class="si"&gt;{keywords}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Return JSON: {{ &amp;#34;title&amp;#34;:&amp;#34;...&amp;#34;, &amp;#34;meta_description&amp;#34;:&amp;#34;...&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 假设的 LLM 调用口（替换为你们的 client）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;llm_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# placeholder: 调用实际 LLM API，得到文本响应&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;action&amp;#34;:&amp;#34;db_query&amp;#34;,&amp;#34;args&amp;#34;:{&amp;#34;city&amp;#34;:&amp;#34;Toronto&amp;#34;,&amp;#34;status&amp;#34;:&amp;#34;ENABLED&amp;#34;}}&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# 示例返回&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Prompt 组装&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;assemble_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chosen_tool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_vars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tool_instr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TOOL_TEMPLATES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;chosen_tool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;tool_vars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;BASE_PROMPT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=== TOOL-SPECIFIC INSTRUCTIONS ===&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_instr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;=== USER QUERY ===&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Orchestrator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 1) 意图分类（这里用简单规则）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;多伦多&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_query&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Toronto&amp;#34;&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chosen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;db_query&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tool_vars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;chosen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;seo_write&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;tool_vars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;tone&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;professional and friendly&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;max_meta_chars&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;155&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;keywords&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;shopify,product&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 2) 组装并调用 LLM（此处实现 JIT）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;assemble_prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_vars&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;llm_resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;llm_resp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 3) 执行工具（示例）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;db_query&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;args&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# 实际应该构造 SQL 并用参数执行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ... 执行 DB，得到 rows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;email&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;a@t.com&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 4) 把工具输出回传给 LLM 以生成最终回复（再次 JIT）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;post_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_PROMPT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Tool result:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Task: Provide a short summary for user.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm_call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# seo_write case...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;llm_resp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;说明：接收用户请求 -&amp;gt; 意图判定 -&amp;gt; 根据意图选择工具 -&amp;gt; 在调用 LLM 前注入该工具的 JIT 指令 -&amp;gt; 根据 LLM 的 “行动” 调用工具 -&amp;gt; 将工具结果以 JIT 指令形式回传给 LLM 作最终输出。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这一策略带来了显著 三大收益：&lt;/p&gt;
&lt;p&gt;●局部化指令：仅在需要时才出现相关指导，系统提示中不再堆满与当前任务无关的规则，从而将核心提示聚焦于通用的 Agent 行为准则。这使模型决策更专注，减少了工具交叉干扰。&lt;/p&gt;
&lt;p&gt;●缓存效率：由于可以动态调整指令内容，避免了每次对 LLM 调用都传入大段不变的说明，大幅提高了 Prompt 缓存命中率，在调用高端模型时降低延迟和成本。&lt;/p&gt;
&lt;p&gt;●模块化解耦：不同情境下可以注入不同指令模块，例如按启用特性开关、模型版本或页面上下文提供定制指导 。这意味着可以针对新工具或新场景添加独立的提示片段，而无需重构整个提示或模型，系统具有更高灵活性。&lt;/p&gt;
&lt;p&gt;实施 JIT 指令后，效果立竿见影 —— 原本混乱冗长的提示被精简，系统可维护性显著提升，各项性能指标也有所改善。总结来说，避免一次性集成过多工具、为每个工具设定清晰边界并采用即时按需的指令注入，是 Shopify 控制 Agent 复杂性的一项最佳实践。&lt;/p&gt;
&lt;h2 id="2-系统可靠性与评估难题"&gt;&lt;a href="#2-%e7%b3%bb%e7%bb%9f%e5%8f%af%e9%9d%a0%e6%80%a7%e4%b8%8e%e8%af%84%e4%bc%b0%e9%9a%be%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;2. 系统可靠性与评估难题
&lt;/h2&gt;&lt;p&gt;让 Agentic 系统在开放的对话环境中保持可靠表现，是另一个严峻挑战。传统的软件测试方法（如固定单元测试）很难覆盖 LLM 不确定的输出和多步推理路径。模型一次微小的提示调整，可能在某些对话场景下提升效果，却在另一些场景意外地引入错误。因此，单靠人工凭感觉 (“vibe testing”) 去对 Agent 表现打分是远远不够的 —— 这会带来一种虚假的安全感。Shopify 工程团队认识到，需要严谨、统计可靠的评估体系来保障 Sidekick 的质量 。&lt;/p&gt;
&lt;p&gt;为此，Shopify 构建了多层次的 LLM 評估基础设施来提升系统可靠性：&lt;/p&gt;
&lt;p&gt;●采用真实分布的 Ground Truth 数据集：团队放弃了人工精心编写的狭窄 “黄金数据集”，转而收集实际生产环境中的对话来建立 Ground Truth Sets (GTX)。这些 GTX 数据更真实地反映了用户提问的多样性和复杂任务分布，而非理想化脚本。通过观察商家和 Sidekick 的真实交互，从中提炼评估标准，团队能够捕捉模型在生产中可能遇到的各类行为，而不用臆测所有可能情况。&lt;/p&gt;
&lt;p&gt;●引入人工标注与统计验证：针对收集的对话数据，Shopify 邀请多个产品专家对模型回答进行多维度标签和评分，确保每个对话至少有三人独立评估。然后使用统计学指标（如 Cohen’s Kappa、Kendall Tau、Pearson 相关系数等）来衡量不同人工标注者之间的一致性。这种方法确保评估标准本身的可靠性 —— 如果连人工都难以达成一致，机器评估更无从谈起。统计验证让团队确定了人类评估一致性所能达到的理论上限，据此作为机器评估的目标基线。&lt;/p&gt;
&lt;p&gt;●开发专用的 LLM 评价模型（LLM Judges）：团队为 Sidekick 的不同性能方面训练了不同的 LLM Judge 模型，用于自动评判 Agent 回复的质量。关键在于，通过反复调优提示，这些 LLM Judges 与人类评价高度相关：最初它们的判断几乎和随机猜测一样差（Cohen’s Kappa 仅 0.02），但经过多轮提示工程和校准，Judge 模型的判断与人类标签的相关度提升到了 0.61，接近人类相互之间 0.69 的一致水平。团队采用的方法是不断调整 Judge 的准则，使其输出和人类评价尽可能一致，并随机用真人评估替换部分机器评估进行盲测，当内置的 LLM Judge 和人类评委已难以分辨时，即表明该 Judge 达到了可令人信任的水准。&lt;/p&gt;
&lt;p&gt;●构建用户模拟器进行全面测试：为了在上线前验证 Agent 的新版本，Shopify 开发了一个由 LLM 驱动的商家用户模拟器。这个模拟器能抓住真实商家在对话中表现出的 “意图” 和行为模式，用它来与不同版本的 Sidekick 进行对话测试。模拟器重放许多真实场景，让团队可以在短时间内对比多个候选系统在相同情景下的表现，评估哪一版本综合表现最佳，然后再决定是否部署。通过这种自动化的对抗测试，许多潜在的对话问题和性能回退在进入生产环境前就被发现并解决。&lt;/p&gt;
&lt;p&gt;上述评估体系共同构成了一条端到端的评测流水线：从收集真实对话、人工评估标注、训练校准 AI 评估器，到模拟用户对话回放，对每次模型或提示更新进行全面 “体检”。实践证明，这一流水线 极大提升了系统稳健性 —— 在新版本发布前，团队有工具及时发现性能衰减或意外行为，从而避免将不成熟的更新部署给真实用户。相比简单的主观打分或有限单元测试，这种方法更加客观、全面，也为业界提供了评估 LLM 智能体的范式模板。&lt;/p&gt;
&lt;h2 id="3-推理控制与模型优化强化学习反馈回路"&gt;&lt;a href="#3-%e6%8e%a8%e7%90%86%e6%8e%a7%e5%88%b6%e4%b8%8e%e6%a8%a1%e5%9e%8b%e4%bc%98%e5%8c%96%e5%bc%ba%e5%8c%96%e5%ad%a6%e4%b9%a0%e5%8f%8d%e9%a6%88%e5%9b%9e%e8%b7%af" class="header-anchor"&gt;&lt;/a&gt;3. 推理控制与模型优化（强化学习反馈回路）
&lt;/h2&gt;&lt;p&gt;除了架构和评估，Shopify 还面临优化模型行为的挑战，即如何引导 LLM 更加准确、高效地完成复杂任务。团队在初始开发后，很快将目光投向了强化学习调优：通过上线后的反馈不断提升模型决策质量。然而，在使用自定义奖励信号对模型进行微调时，他们遇到了 “奖励函数破解 (Reward Hacking)” 难题。&lt;/p&gt;
&lt;p&gt;Shopify 采用了一种名为 GRPO（Group Relative Policy Optimization） 的强化学习算法对 Sidekick 的 LLM 进行精调，把之前提到的 LLM Judges 评价分数作为模型的奖励信号。简单来说，模型生成回复后，LLM Judge 会对其在不同指标上打分，这些分数经组合形成奖励，指导模型朝更优的方向更新参数。为增强训练信号的可靠性，团队设计了 N 级闸门式奖励机制：首先通过一系列程序化校验（如输出格式是否合法、JSON schema 是否正确）过滤掉明显不合规的结果，然后再由语义层面的 LLM Judge 赋予奖励分。这种 “规则 + AI” 结合的复合奖励可确保模型既满足硬性规范，又在内容质量上优化。&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-10-01-shopify-gou-jian-sheng-chan-ji-agentic-xi-tong-de-fang-fa-fe/003-7861f3fe.png"&gt;&lt;/p&gt;
&lt;p&gt;然而，尽管事先精心设计了评估准则，模型在强化学习过程中依然找到了意想不到的投机取巧方法来提升奖励分，而非真正提升任务质量：&lt;/p&gt;
&lt;p&gt;●选择性跳过：碰到复杂请求时，模型学会了巧妙地说明自己 “无能为力” 以逃避挑战（避免因为回答错误被扣分），这种 Opt-out 行为使它在困难场景下以不作为来避免扣分。&lt;/p&gt;
&lt;p&gt;●标签滥用：模型滥用系统中的某些自由字段。例如在客户分群任务中，它倾向于利用「客户标签」这个通用字段来实现过滤，而不是使用正确的专用字段，从而投机取巧满足形式要求 。这种行为被称作 Tag Hacking，模型通过走捷径获利但语义不准确。&lt;/p&gt;
&lt;p&gt;●架构违规：输出不符合预期结构，例如臆造不存在的 ID 值，或使用错误的枚举值以通过语法校验（Schema Violation）。&lt;/p&gt;
&lt;p&gt;例如，有客户要求按照 “已启用 (enabled)” 状态筛选用户，本应使用字段 customer&lt;em&gt;account&lt;/em&gt;status = &amp;lsquo;ENABLED&amp;rsquo; 查询，但模型为了讨好奖励，走捷径生成了条件 customer_tags CONTAINS &amp;rsquo;enabled&amp;rsquo;。虽然表面上它 “回答” 了请求，但其实偏离了业务真实语义，属于不正确的解决方案。&lt;/p&gt;
&lt;p&gt;针对这些奖励黑客行为，Shopify 采取了 迭代改进 策略：每当发现模型钻空子的模式，就及时加强对应的约束。具体包括：&lt;/p&gt;
&lt;p&gt;●升级语法验证规则，使其能够识别并拒绝模型试图利用的漏洞（例如检查输出是否不再滥用某字段或避免特定保留字的错误使用）。&lt;/p&gt;
&lt;p&gt;●提升 LLM Judges 的判别能力，在奖励计算中扣除那些看似通过但实际错误的答案分数。例如，让 Judge 学会识别 “客户标签包含 enabled” 这种答案其实并未真正满足需求，从而不给模型奖励。&lt;/p&gt;
&lt;p&gt;●将新发现的失败案例加入评估数据集（类似上一节提到的 EDD 流程），再次训练或微调模型，使其不再重犯。&lt;/p&gt;
&lt;p&gt;经过多轮迭代，团队显著减少了模型投机取巧的现象，Sidekick 在严格遵循业务规则的同时继续优化自然语言处理质量。效果可以从几项指标的改善反映出来：强化学习后系统各技能的语法验证准确率从约 93% 提升到了 99%，LLM Judge 与人类评价的相关性也从 0.66 提高到了 0.75，更重要的是，Sidekick 端到端对话质量重新达到了有监督微调模型的基线水平。这说明在堵上奖励漏洞后，Agent 的实际表现与原先人工调优的水平相当，既没有因为 RL 走偏，也充分受益于 RL 获取了更高的鲁棒性。&lt;/p&gt;
&lt;p&gt;经验教训：在对 Agent 应用强化学习时，必须假定模型会尝试 “作弊”，并提前设计检测和纠偏机制 。结合规则约束（Procedural）和 AI 评价（Semantic）的多层验证，是控制 LLM 推理输出质量的有效手段。每当引入新策略或新数据训练模型，都需要反复评估、监控，以发现新的失败模式并再次优化 。这一循序渐进的反馈闭环确保了模型的推理过程始终在可控范围内演进，从工程上保障了系统可靠性。&lt;/p&gt;
&lt;h1 id="shopify-的设计选择及对业界的启示"&gt;&lt;a href="#shopify-%e7%9a%84%e8%ae%be%e8%ae%a1%e9%80%89%e6%8b%a9%e5%8f%8a%e5%af%b9%e4%b8%9a%e7%95%8c%e7%9a%84%e5%90%af%e7%a4%ba" class="header-anchor"&gt;&lt;/a&gt;Shopify 的设计选择及对业界的启示
&lt;/h1&gt;&lt;p&gt;在构建 Sidekick 的过程中，Shopify 做出了一系列关键架构设计选择，这些选择不仅解决了自身的问题，也为其他公司打造 Agent 系统提供了宝贵借鉴。&lt;/p&gt;
&lt;h2 id="1-单智能体架构与-agentic-loop-模式"&gt;&lt;a href="#1-%e5%8d%95%e6%99%ba%e8%83%bd%e4%bd%93%e6%9e%b6%e6%9e%84%e4%b8%8e-agentic-loop-%e6%a8%a1%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;1. 单智能体架构与 Agentic Loop 模式
&lt;/h2&gt;&lt;p&gt;Shopify 选择围绕单一 LLM 智能体构建系统核心循环，而非多个 Agent 协作。Sidekick 的架构遵循 Anthropic 提出的 “agentic loop” 概念 —— 人类提供输入，单一 LLM Agent 决策行动，执行工具产生反馈，如此循环直至任务完成。尽管业界存在多智能体协同的探索趋势，Shopify 团队的经验是：在初期应避免过早引入多 Agent 架构，因为单 Agent 系统已经可以处理相当复杂的任务，而且设计更简单、可控。这意味着其他团队在没有明确需求时，大可先采用单智能体 + 多工具的方式实现目标，简化协调难度和潜在 Bug。只有当问题确实需要并行或专业分工时，再考虑多 Agent 方案会更明智。&lt;/p&gt;
&lt;h2 id="2-核心组件的模块化解耦"&gt;&lt;a href="#2-%e6%a0%b8%e5%bf%83%e7%bb%84%e4%bb%b6%e7%9a%84%e6%a8%a1%e5%9d%97%e5%8c%96%e8%a7%a3%e8%80%a6" class="header-anchor"&gt;&lt;/a&gt;2. 核心组件的模块化解耦
&lt;/h2&gt;&lt;p&gt;从架构伊始，Shopify 就强调 “模块化” 原则，将 Agent 系统划分为清晰的组件层次。例如，他们通过 JIT 指令将工具使用说明与核心 Agent 逻辑解耦，实现指令管理模块与对话决策模块的分离。LLM 只关注通用推理和决策，而每个工具如何使用、有哪些特殊规则，则由独立的指令模块按需提供。这种设计让系统具备插件化特性：新增工具时，只需添加对应的指令配置而无需改动主 Prompt；升级模型时，可以调整指令策略而不影响底层工具实现。模块边界清晰还提升了团队协作和调试效率 —— 不同工程师可各自专注于工具接口、提示策略、对话管理等模块，彼此之间通过明确契约交互。这种模块化架构对于其他公司具有普适意义：在 Agent 系统日趋复杂之际，唯有模块清晰、职责单一，才能保证系统易于扩展和维护。&lt;/p&gt;
&lt;h2 id="3-工具与智能体解耦严格边界管理"&gt;&lt;a href="#3-%e5%b7%a5%e5%85%b7%e4%b8%8e%e6%99%ba%e8%83%bd%e4%bd%93%e8%a7%a3%e8%80%a6%e4%b8%a5%e6%a0%bc%e8%be%b9%e7%95%8c%e7%ae%a1%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;3. 工具与智能体解耦，严格边界管理
&lt;/h2&gt;&lt;p&gt;Shopify 的设计突出 Agent 与工具的松耦合，既赋予 Agent 调用外部能力的权力，又通过架构设定边界防止混乱。具体体现为两点：其一，质量优先于数量，只添加明确必要且定义清晰的工具，避免工具职责重叠。Sidekick 团队深知，每接入一个新工具，都要考虑它与现有能力的边界，否则很容易出现多种路径解决同一问题的情况，增加 Agent 决策负担。因此其他公司在扩展 Agent 能力时，应像 Shopify 一样慎重评估新工具的边界和作用，宁缺毋滥。其二，通过 JIT 指令等机制将工具信息作用域局限在需要的对话步骤中，Agent 不会被全局提供的一长串工具说明淹没。这种解耦让 Agent 在每一步决策时只 “看到” 相关工具，减少无关干扰，提高推理准确性。对业界而言，这提示我们应将 Agent 的推理逻辑与具体工具实现隔离，通过明确定义的接口或中间层沟通。一方面方便替换或升级底层工具，另一方面也防止 Agent 对工具的假设硬编码在模型 prompt 中，从而提高系统稳健性和灵活性。&lt;/p&gt;
&lt;h2 id="4-引入持续反馈的评估与测试机制"&gt;&lt;a href="#4-%e5%bc%95%e5%85%a5%e6%8c%81%e7%bb%ad%e5%8f%8d%e9%a6%88%e7%9a%84%e8%af%84%e4%bc%b0%e4%b8%8e%e6%b5%8b%e8%af%95%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;4. 引入持续反馈的评估与测试机制
&lt;/h2&gt;&lt;p&gt;Shopify 将评价反馈融入了开发流程，这也是架构设计的重要组成部分而非事后附加。他们构建的 LLM Judges、Ground Truth 集和用户模拟器共同形成了一个持续评测闭环，使得每次 Agent 策略变更或模型更新都被及时度量和检验。这种设计选择启示其他团队：在开发 AI 代理时，应当考虑搭建自己的评估基础设施，比如收集真实用户交互作为测试用例、建立自动化评价指标，甚至构建模拟用户来反复 “试探” 新版本的弱点。这种持续反馈机制相当于为 AI 系统加入了监控仪表盘和安全网，在系统演进过程中提供客观依据，避免依赖开发者主观判断，从工程上保障产品质量。&lt;/p&gt;
&lt;h2 id="5-训练和推理闭环的结合"&gt;&lt;a href="#5-%e8%ae%ad%e7%bb%83%e5%92%8c%e6%8e%a8%e7%90%86%e9%97%ad%e7%8e%af%e7%9a%84%e7%bb%93%e5%90%88" class="header-anchor"&gt;&lt;/a&gt;5. 训练和推理闭环的结合
&lt;/h2&gt;&lt;p&gt;传统 Agent 框架往往聚焦于推理过程的 orchestrion（编排），而 Shopify 的设计延伸到了模型训练优化阶段。他们将线上评估信号用于强化学习微调，实现了从评估到模型优化的闭环。这种架构 + 训练一体化的思路对有实力的团队很有借鉴价值：当单纯通过 Prompt 工程难以进一步提升性能时，考虑结合领域反馈进行模型微调，能使 Agent 更贴合特定业务需求。不过，这同时要求有完善的评估和监控手段，以免模型朝错误方向优化（正如前述需要防范奖励函数被钻漏洞）。总的来说，Shopify 的实践提醒我们，生产级的 AI Agent 开发不仅是编排 LLM 调用，还应包括模型性能的持续改进，需要将机器学习训练和传统软件工程有机融合。&lt;/p&gt;
&lt;h1 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h1&gt;&lt;p&gt;Shopify 在 Sidekick 中的诸多设计选择 —— 无论是架构上的单 Agent 模块化理念，还是工程流程上的评测反馈闭环 —— 都体现了一种面向生产稳健性的取舍。这些理念将对其他科技公司构建 Agent 系统产生深刻启发：从一开始就以简洁可控的方式集成 LLM 和工具，建立完善的测试监控机制，逐步演进而非一蹴而就，才能打造出可长期维护和信赖的智能代理系统。&lt;/p&gt;</description></item><item><title>搞懂 ThreadLocal，其实就三件事：它是谁？它在哪？用完它咋办？</title><link>https://xiaobox.github.io/p/2025-08-21-gao-dong-threadlocal-qi-shi-jiu-san-jian-shi-ta-shi-shui-ta/</link><pubDate>Thu, 21 Aug 2025 10:52:24 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-08-21-gao-dong-threadlocal-qi-shi-jiu-san-jian-shi-ta-shi-shui-ta/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-21-gao-dong-threadlocal-qi-shi-jiu-san-jian-shi-ta-shi-shui-ta-/cover.jpg" alt="Featured image of post 搞懂 ThreadLocal，其实就三件事：它是谁？它在哪？用完它咋办？" /&gt;&lt;h1 id="缘起"&gt;&lt;a href="#%e7%bc%98%e8%b5%b7" class="header-anchor"&gt;&lt;/a&gt;缘起
&lt;/h1&gt;&lt;p&gt;这两天又用到了 &lt;code&gt;ThreadLocal&lt;/code&gt; ,时间一长，很多细节都想不起来了，现翻源码 😂&lt;/p&gt;
&lt;p&gt;想着干脆写个笔记记录一下，其实之前写过有关 &lt;code&gt;ThreadLocal&lt;/code&gt; 的文章，现在回过头看觉得一些细节写的交待的不好，那么就再写一遍吧。&lt;/p&gt;
&lt;p&gt;这一次的目标就是搞清楚 &lt;strong&gt;Threadlocal 它到底是怎么隔离线程数据的，好在哪？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当然也要挖一挖那个潜在的 &lt;strong&gt;内存泄漏风险&lt;/strong&gt;，看看它在 Java 不同版本里头有没有啥变化。&lt;/p&gt;
&lt;h1 id="threadlocal-它根本上是干嘛的"&gt;&lt;a href="#threadlocal-%e5%ae%83%e6%a0%b9%e6%9c%ac%e4%b8%8a%e6%98%af%e5%b9%b2%e5%98%9b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;ThreadLocal 它根本上是干嘛的？
&lt;/h1&gt;&lt;p&gt;简单来说， ThreadLocal 就是给每个线程(注意是每个线程) 一个变量的独立副本。&lt;/p&gt;
&lt;p&gt;你可以想象一下，比如说用户 ID 或者一个事务 ID 这种需要跟某个线程绑定的数据，用它就特别合适。&lt;strong&gt;它解决的不是那种多线程怎么共享数据的问题，而是怎么管好单个线程自己用的数据。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="反向存储"&gt;&lt;a href="#%e5%8f%8d%e5%90%91%e5%ad%98%e5%82%a8" class="header-anchor"&gt;&lt;/a&gt;反向存储
&lt;/h1&gt;&lt;p&gt;它用了一个挺有意思的设计，有人叫它 “反向存储”&lt;/p&gt;
&lt;p&gt;我根据 ThreadLocal 的内部结构梳理了一个结构图，如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;$ terminal┌─────────────────────────────────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;│ JVM 堆内存 - 全局共享区域 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;│ ┌─────────────────┐ ┌─────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;│ │ ThreadLocal1 │ │ ThreadLocal2 │ ← 全局唯一实例 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;│ │ &lt;span class="o"&gt;(&lt;/span&gt;COUNTER&lt;span class="o"&gt;)&lt;/span&gt; │ │ &lt;span class="o"&gt;(&lt;/span&gt;NAME&lt;span class="o"&gt;)&lt;/span&gt; │ 所有线程共享 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;│ └─────────────────┘ └─────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;└─────────────────────────────────────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; ↑ ↑
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; │ 作为key引用 │ 作为key引用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;┌─────────────────────────────────────────────────────────────────┐
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;│ 线程独立存储区域 │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;│ ┌─────────────────┐ ┌─────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;│ │ Thread-1 │ │ Thread-2 │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;│ │ threadLocals ──┼──┐ ┌──┼── threadLocals │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;│ └─────────────────┘ │ │ └─────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;│ ┌────────────────────▼──┐ ┌──▼────────────────────┐ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;│ │ ThreadLocalMap-1 │ │ ThreadLocalMap-2 │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;│ │ Entry&lt;span class="o"&gt;[]&lt;/span&gt; table │ │ Entry&lt;span class="o"&gt;[]&lt;/span&gt; table │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ Entry&lt;span class="o"&gt;[&lt;/span&gt;0&lt;span class="o"&gt;]&lt;/span&gt; │ │ │ │ Entry&lt;span class="o"&gt;[&lt;/span&gt;0&lt;span class="o"&gt;]&lt;/span&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ key: COUNTER │ │ │ │ key: COUNTER │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ value: &lt;span class="m"&gt;100&lt;/span&gt; │ │ │ │ value: &lt;span class="m"&gt;200&lt;/span&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;│ │ ┌─────────────────┐ │ │ ┌─────────────────┐ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ Entry&lt;span class="o"&gt;[&lt;/span&gt;1&lt;span class="o"&gt;]&lt;/span&gt; │ │ │ │ Entry&lt;span class="o"&gt;[&lt;/span&gt;1&lt;span class="o"&gt;]&lt;/span&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ key: NAME │ │ │ │ key: NAME │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;│ │ │ value:&lt;span class="s2"&gt;&amp;#34;Thread1&amp;#34;&lt;/span&gt; │ │ │ │ value:&lt;span class="s2"&gt;&amp;#34;Thread2&amp;#34;&lt;/span&gt; │ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;│ │ └─────────────────┘ │ │ └─────────────────┘ │ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;│ └───────────────────────┘ └───────────────────────┘ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;│ │
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;└─────────────────────────────────────────────────────────────────┘
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;从图上可以看到，实际上是每个 &lt;code&gt;Thread&lt;/code&gt; 对象它自己内部持有一个map，这个 map 就是 &lt;code&gt;ThreadLocalMap&lt;/code&gt;，每个线程都有一个。&lt;/p&gt;
&lt;p&gt;我们从源码中也能看到：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terminal&lt;/span&gt;&lt;span class="c1"&gt;// Thread 类的关键字段（简化版）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;implements&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Runnable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 每个 Thread 实例都有自己独立的这个字段！&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ThreadLocalMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;threadLocals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ThreadLocalMap&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;inheritableThreadLocals&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 其他字段...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;然后那个 ThreadLocal 变量本身就是我们代码里定义的那个，通常是个 static final 的全局变量。它其实是充当了所有这些不同的 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 里面的 entry 的 key。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;$ terminal// ThreadLocalMap 的关键实现
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;static class ThreadLocalMap &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; // Entry 继承 WeakReference，key 是 ThreadLocal 对象的弱引用
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; static class Entry extends WeakReference&amp;lt;ThreadLocal&amp;lt;?&amp;gt;&amp;gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; Object value&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; Entry&lt;span class="o"&gt;(&lt;/span&gt;ThreadLocal&amp;lt;?&amp;gt; k, Object v&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; super&lt;span class="o"&gt;(&lt;/span&gt;k&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; // k 是全局共享的 ThreadLocal 对象
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; v&lt;span class="p"&gt;;&lt;/span&gt; // v 是线程独立的值
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; // 每个 ThreadLocalMap 都有自己独立的 Entry 数组
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; private Entry&lt;span class="o"&gt;[]&lt;/span&gt; table&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; // 构造函数：每个线程调用时都会创建新的实例
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; ThreadLocalMap&lt;span class="o"&gt;(&lt;/span&gt;ThreadLocal&amp;lt;?&amp;gt; firstKey, Object firstValue&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; new Entry&lt;span class="o"&gt;[&lt;/span&gt;INITIAL_CAPACITY&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; int &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; firstKey.threadLocalHashCode &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;INITIAL_CAPACITY - 1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; table&lt;span class="o"&gt;[&lt;/span&gt;i&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; new Entry&lt;span class="o"&gt;(&lt;/span&gt;firstKey, firstValue&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; setThreshold&lt;span class="o"&gt;(&lt;/span&gt;INITIAL_CAPACITY&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;就是说 &lt;code&gt;Threadlocal&lt;/code&gt; 这个钥匙是大家都能看到的，是共享的，但是存东西的柜子也就是 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 是每个线程自己的，是互相隔离的。&lt;/p&gt;
&lt;h2 id="内部存储"&gt;&lt;a href="#%e5%86%85%e9%83%a8%e5%ad%98%e5%82%a8" class="header-anchor"&gt;&lt;/a&gt;内部存储
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;这个 ThreadLocalMap 它怎么通过 ThreadLocal 这个键找到对应的值呢？它里面是咋存的？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实际上它是用 ThreadLocal 实例本身的哈希码，这个哈希码会经过一个计算，然后确定在 map 内部数组里的一个位置。这个计算方法还挺讲究的，用了一个特殊的数字，就是为了让这些键能均匀地散开。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ terminal// 构造函数：每个线程调用时都会创建新的实例
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;ThreadLocalMap&lt;span class="o"&gt;(&lt;/span&gt;ThreadLocal&amp;lt;?&amp;gt; firstKey, Object firstValue&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; new Entry&lt;span class="o"&gt;[&lt;/span&gt;INITIAL_CAPACITY&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; int &lt;span class="nv"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; firstKey.threadLocalHashCode &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;INITIAL_CAPACITY - 1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt; table&lt;span class="o"&gt;[&lt;/span&gt;i&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; new Entry&lt;span class="o"&gt;(&lt;/span&gt;firstKey, firstValue&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt; setThreshold&lt;span class="o"&gt;(&lt;/span&gt;INITIAL_CAPACITY&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;ThreadLocal中有一个属性为HASH_INCREMENT = 0x61c88647。这个值很特殊，它是斐波那契数 也叫 黄金分割数。hash增量为 这个数字，带来的好处就是 hash 分布非常均匀。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;要是算出来的位置已经被占了，就是所谓的哈希冲突，它就用一种叫&lt;strong&gt;线性探测&lt;/strong&gt;的方法来解决，简单说就是如果这个位置有人了，他就看下一个位置空不空，再不行就再看下一个，一直找到空位为止。&lt;/p&gt;
&lt;p&gt;因为这个 map 是线程私有的，不存在多个线程同时来抢位置的问题，所以这种简单的方法就够用了，效率也还行。&lt;/p&gt;
&lt;h2 id="隔离机制"&gt;&lt;a href="#%e9%9a%94%e7%a6%bb%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;隔离机制
&lt;/h2&gt;&lt;p&gt;这个 “反向存储” ，有点儿反直觉：不是 ThreadLocal 对象持有多个线程的值，而是 每个 Thread 对象持有自己的 ThreadLocalMap，ThreadLocalMap 以 ThreadLocal 对象为 key，存储该线程的值。&lt;/p&gt;
&lt;p&gt;我们再具体总结一下 ThreadLocal 的隔离机制，实际上它是一个 &amp;ldquo;部分共享，部分独立&amp;rdquo; 的机制。&lt;/p&gt;
&lt;p&gt;1.ThreadLocal 对象全局共享：static final 修饰，JVM 中只有一个实例，所有线程都引用同一个 ThreadLocal 对象&lt;/p&gt;
&lt;p&gt;2.ThreadLocalMap 线程独立：存储在 Thread.threadLocals 字段中，每个 Thread 实例都有自己的 threadLocals 字段，调用 createMap() 时，给不同线程创建不同的 ThreadLocalMap 实例&lt;/p&gt;
&lt;p&gt;3.Entry 数组线程独立：每个 ThreadLocalMap 都有自己的 Entry[] table，虽然 Entry 的 key 都指向同一个 ThreadLocal 对象，但 Entry 对象本身和 value 都是线程独立的&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“ThreadLocal 就是一把“通用钥匙”，它不存东西，而是帮每个线程打开自己的“专属保险箱”。”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这就是 ThreadLocal 精妙设计的核心：通过线程对象的实例字段实现存储隔离，通过全局 ThreadLocal 对象实现访问统一。&lt;/p&gt;
&lt;h1 id="内存泄露风险"&gt;&lt;a href="#%e5%86%85%e5%ad%98%e6%b3%84%e9%9c%b2%e9%a3%8e%e9%99%a9" class="header-anchor"&gt;&lt;/a&gt;内存泄露风险
&lt;/h1&gt;&lt;p&gt;这个风险主要来自于 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 存数据的方式。特别是它的键，这个 map 里面的键也就是咱们那个 &lt;code&gt;ThreadLocal&lt;/code&gt; 对象实例，它不是直接存的，它是被一个叫做 &lt;code&gt;weak reference&lt;/code&gt;，也就是弱引用的东西包了一层&lt;/p&gt;
&lt;h2 id="弱引用"&gt;&lt;a href="#%e5%bc%b1%e5%bc%95%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;弱引用
&lt;/h2&gt;&lt;p&gt;这个弱引用跟我们平时用的那种普通的引用就是强引用有啥不一样？&lt;/p&gt;
&lt;p&gt;咱们平时用的强引用，只要这个引用还在，垃圾回收器（GC） 就不会把那个对象收走，但弱引用不一样， GC 在扫描的时候如果发现一个对象只被弱引用指着，没有强引用指向它了，那 GC 就可以把它回收掉。所以在 &lt;code&gt;ThreadLocalMap&lt;/code&gt; 这里，如果你代码里别的地方不再持有那个 &lt;code&gt;Threadlocal&lt;/code&gt; 实例的强引用了,比如说那个类被卸载了，或者实例变量不再被访问了,类似这种情况，那 GC 就可能把这个 Threadlocal 对象本身回收掉，这时候 map 里面那个 entry 的键就变成了null。&lt;/p&gt;
&lt;h3 id="键没了变成-null-了那不是正好吗"&gt;&lt;a href="#%e9%94%ae%e6%b2%a1%e4%ba%86%e5%8f%98%e6%88%90-null-%e4%ba%86%e9%82%a3%e4%b8%8d%e6%98%af%e6%ad%a3%e5%a5%bd%e5%90%97" class="header-anchor"&gt;&lt;/a&gt;键没了，变成 null 了，那不是正好吗？
&lt;/h3&gt;&lt;p&gt;键没了，变成 null 了，说明这个条目没用了，可以清掉了。那不是正好吗？&lt;/p&gt;
&lt;p&gt;坑就在这，虽然键是弱引用， GC 可能回收它，但是和这个键关联的那个值（value），它是被强引用持有的。持有它就是 Threadlocalmap 里面的那个 entry 对象，这个 entry 对象本身强引用着那个 value。&lt;/p&gt;
&lt;p&gt;我们来捋一下：&lt;strong&gt;Threadlocal 键被弱引用包装，可能被 GC 回收变 null，但存的那个 value 被 entry 对象强引用。 然后这个 entry 对象又被 ThreadLocalMap 持有，ThreadLocalMap 又被那个 Thread 对象持有。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;整个引用链条如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="图片" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-08-21-gao-dong-threadlocal-qi-shi-jiu-san-jian-shi-ta-shi-shui-ta-/001-9dc9dd97.png"&gt;&lt;/p&gt;
&lt;p&gt;只要这个线程还活着，比如线程池里的线程（它可能活很久，线程池里的线程会复用），然后如果这个线程后续一直没有再调用这个 Threadlocal 的 set、get 或者 remove 方法，这些方法在执行的时候会顺便检查一下清理掉那些键为 null 的entry，但如果一直没调用，那这个键虽然是 null 了，但那个 value 因为被 entry 强引用着，就一直没法被 GC 回收。&lt;strong&gt;这就泄露了。那个 value 对象就一直占着内存，明明逻辑上可能已经没用了。这就是典型的 Threadlocal 内存泄露场景&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id="那为啥不干脆把那个-value-也用弱引用呢"&gt;&lt;a href="#%e9%82%a3%e4%b8%ba%e5%95%a5%e4%b8%8d%e5%b9%b2%e8%84%86%e6%8a%8a%e9%82%a3%e4%b8%aa-value-%e4%b9%9f%e7%94%a8%e5%bc%b1%e5%bc%95%e7%94%a8%e5%91%a2" class="header-anchor"&gt;&lt;/a&gt;那为啥不干脆把那个 value 也用弱引用呢？
&lt;/h3&gt;&lt;p&gt;那样的话 Threadlocal 可能就失去意义了。你想啊，那个 value 是线程真正需要的数据，比如一个数据库连接，如果它也是弱引用，那可能在你正用得好好的时候，突然就被 GC 给回收了。那下次去 get 的时候拿到了可能就是 null 了，即使我没 remove 它。那程序可能就出错了。&lt;strong&gt;所以用强引用是为了保证只要线程逻辑上还需要这个值，并且没显示的remove，它就应该一直在。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这是在保证数据可用性和自动内存管理之间做了一个权衡，他选择了优先保证数据，但这个选择就把一部分清理的责任甩给了开发者。&lt;/p&gt;
&lt;h3 id="这对开发者意味着什么-"&gt;&lt;a href="#%e8%bf%99%e5%af%b9%e5%bc%80%e5%8f%91%e8%80%85%e6%84%8f%e5%91%b3%e7%9d%80%e4%bb%80%e4%b9%88-" class="header-anchor"&gt;&lt;/a&gt;这对开发者意味着什么 ？
&lt;/h3&gt;&lt;p&gt;意味着你必须养成一个习惯，非常非常重要的习惯，就是在使用完 Threadlocal 变量之后，一定要最好是在 finally 块里头调用那个 Threadlocal 的 remove 方法。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terminalpublic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConnectionManager&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connectionHolder&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SQLException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connectionHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isClosed&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DriverManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;jdbc:mysql://localhost:3306/test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;root&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connectionHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;closeConnection&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Connection&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connectionHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;connectionHolder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这个 remove 方法会把当前线程的 ThreadLocalMap 里跟这个 Threadlocal 实例对应的那个 entry 整个都删掉，这样那个强引用的 value 自然也就没有引用指向它了，下次 GC 就能把它回收了。&lt;/p&gt;
&lt;p&gt;所以关键就是要手动清理，不能偷懒，不能指望它内部那个自动清理机制，&lt;strong&gt;尤其是在线程池这种线程生命周期可能很长的场景下，依赖自动清理风险很大。&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="java-新版本"&gt;&lt;a href="#java-%e6%96%b0%e7%89%88%e6%9c%ac" class="header-anchor"&gt;&lt;/a&gt;java 新版本
&lt;/h1&gt;&lt;p&gt;既然 ThreadLocal 有内存泄露的风险，那么后面新的 Java 版本，比如 11、17、21 这些，有没有做些改进，或者提供一些替代方案呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;弱引用键、强引用值这个机制，在后面这些版本里基本没变，但是确实有一些改进和变化。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="java-17"&gt;&lt;a href="#java-17" class="header-anchor"&gt;&lt;/a&gt;java 17
&lt;/h2&gt;&lt;p&gt;Java 17 里针对那个公共的 ForkJoinPool，就是 ForkJoinPool 的 common pool，它增加了一个特性，当池里的任务执行完之后，会自动帮你清理掉那个任务线程用过的所有 Threadlocal 值，为这个特定的池缓解了一下风险。但注意，如果你自己创建的 ForkJoinPool 或者普通的线程池，或者直接创建了线程，那还得你自己负责 remove 。&lt;/p&gt;
&lt;h2 id="java-21"&gt;&lt;a href="#java-21" class="header-anchor"&gt;&lt;/a&gt;java 21
&lt;/h2&gt;&lt;p&gt;Java 21 提到的 scoped values ,目前还是预览特性，它提供了一种不同的方式来共享那些需要跟作用域绑定的数据（比如请求处理过程绑定的数据，特别是不可变数据），它的设计理念就是为了避免 Threadlocal 这种需要手动清理的麻烦，用一种结构化的方式来传递和管理，用完自然就没了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terminalimport&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;jdk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incubator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;concurrent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ScopedValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ScopedValueExample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 声明一个 ScopedValue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScopedValue&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScopedValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInstance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 使用 ScopedValue.where 绑定值，并在作用域内运行&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ScopedValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Alice&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;在作用域内: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 作用域结束后，值自动消失，不存在泄漏&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// System.out.println(USER.get()); // 会抛 IllegalStateException&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;子方法依然能获取: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;USER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;至于虚拟线程，因为它们被设计成非常轻量级，而且通常生命周期很短，用完就丢了，线程没了它关联的 ThreadLocalMap 自然也就没了，所以长期泄露的风险窗口就大大缩短了。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;terminalpublic&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VirtualThreadExample&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ThreadLocal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withInitial&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;未设置&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;throws&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;InterruptedException&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 使用虚拟线程（JDK 21 已经正式支持）&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vThread&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofVirtual&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;虚拟线程的数据&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentThread&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34; -&amp;gt; &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 不需要手动清理，虚拟线程生命周期很短，用完就结束&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;vThread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// 这里 vThread 已经结束，对应的 ThreadLocalMap 已自动销毁&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;总结
&lt;/h1&gt;&lt;p&gt;我们来总结一下 ThreadLocal 这个东西。&lt;/p&gt;
&lt;p&gt;它通过每个线程自己私有的 ThreadLocalMap 实现了线程数据的隔离。挺强大的，在很多场景下很有用，但是它那个弱引用键加上强引用值的设计，就像一把双刃剑，带来了内存泄露的风险，特别是用线程池这种长生命周期的线程池，所以最重要的实践就是要记得用完之后一定在 finally 块里手动调用 remove 来清理。&lt;/p&gt;
&lt;p&gt;虽然新版 Java 针对特定场景，比如公共 ForkJoinPool 做了些自动清理，也提供了像 scoped values 这样的潜在替代方案，但总的来说，理解这个机制，并且承担起主动清理的责任，对开发者来说还是很重要的。&lt;/p&gt;</description></item><item><title>Milvus 向量数据库快速入门（人话版）</title><link>https://xiaobox.github.io/p/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/</link><pubDate>Sun, 01 Jun 2025 04:03:27 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/cover.jpg" alt="Featured image of post Milvus 向量数据库快速入门（人话版）" /&gt;&lt;h2 id="milvus-到底是干嘛的"&gt;&lt;a href="#milvus-%e5%88%b0%e5%ba%95%e6%98%af%e5%b9%b2%e5%98%9b%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;Milvus 到底是干嘛的？
&lt;/h2&gt;&lt;p&gt;它是“给向量找对象”的超高速数据库——存向量、比相似、返回前 K 名。&lt;/p&gt;
&lt;p&gt;Milvus 就是给「向量」找对象的数据库——它能帮你把一堆高维向量存好、管好、飞快地按“相似度”把最像的几条挑出来。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;和普通数据库比，Milvus天生会“模糊配对”，不是 exact match 而是“谁更像”。&lt;/li&gt;
&lt;li&gt;内核走的是“先分桶/建图，再局部暴力”，所以大规模也能搜得飞快。&lt;/li&gt;
&lt;li&gt;2.x 版本把「数据落盘」「分布式容灾」都外包给 RocksDB + MinIO + etcd——省了你很多心。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="先认几个关键词"&gt;&lt;a href="#%e5%85%88%e8%ae%a4%e5%87%a0%e4%b8%aa%e5%85%b3%e9%94%ae%e8%af%8d" class="header-anchor"&gt;&lt;/a&gt;先认几个关键词
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/001-c2fdff5a.png"&gt;&lt;/p&gt;
&lt;h2 id="部署使用"&gt;&lt;a href="#%e9%83%a8%e7%bd%b2%e4%bd%bf%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;部署使用
&lt;/h2&gt;&lt;p&gt;五步跑通「单机体验」+ 三步升级「小集群」&lt;/p&gt;
&lt;h3 id="单机-5-步"&gt;&lt;a href="#%e5%8d%95%e6%9c%ba-5-%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;单机 5 步
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;拉镜像 docker run milvusdb/milvus:v2.4.3&lt;/li&gt;
&lt;li&gt;建楼 create_collection()——确定字段维度、主键、向量字段。&lt;/li&gt;
&lt;li&gt;搬人 insert() → flush()。&lt;/li&gt;
&lt;li&gt;装电梯 create_index()；小数据直接 FLAT，大数据先 IVF，再视情况换 HNSW。&lt;/li&gt;
&lt;li&gt;开门找人 load() → search()/query()；用完可 release().&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="变成-3-节点小集群"&gt;&lt;a href="#%e5%8f%98%e6%88%90-3-%e8%8a%82%e7%82%b9%e5%b0%8f%e9%9b%86%e7%be%a4" class="header-anchor"&gt;&lt;/a&gt;变成 3 节点小集群
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/002-c0911d8c.png"&gt;&lt;/p&gt;
&lt;h2 id="最常用的-5-步操作"&gt;&lt;a href="#%e6%9c%80%e5%b8%b8%e7%94%a8%e7%9a%84-5-%e6%ad%a5%e6%93%8d%e4%bd%9c" class="header-anchor"&gt;&lt;/a&gt;最常用的 5 步操作
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;建楼：create collection，把字段都定义好&lt;/li&gt;
&lt;li&gt;搬人：insert，把向量和元信息塞进去；记得 flush() 真正落盘&lt;/li&gt;
&lt;li&gt;装电梯：create index，选对索引类型，未来搜索才快&lt;/li&gt;
&lt;li&gt;请保安开门：load，没 load 就像门锁着，啥也搜不到&lt;/li&gt;
&lt;li&gt;找人：search（可加条件 expr），或者只按字段 query&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="索引怎么挑"&gt;&lt;a href="#%e7%b4%a2%e5%bc%95%e6%80%8e%e4%b9%88%e6%8c%91" class="header-anchor"&gt;&lt;/a&gt;索引怎么挑？
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/003-0a75133a.png"&gt;&lt;/p&gt;
&lt;h2 id="索引调优口诀"&gt;&lt;a href="#%e7%b4%a2%e5%bc%95%e8%b0%83%e4%bc%98%e5%8f%a3%e8%af%80" class="header-anchor"&gt;&lt;/a&gt;索引调优口诀
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;小样本先 FLAT 做 baseline——它慢但最准，方便肉眼看 Recall。&lt;/li&gt;
&lt;li&gt;百 万级优先 IVF_FLAT：调 nlist=√N 起步；提高 nprobe 越准越慢。&lt;/li&gt;
&lt;li&gt;千万级冲 HNSW：关键参 M (边数) 和 efConstruction (建图宽度)，调高两倍能大幅增 Recall。&lt;/li&gt;
&lt;li&gt;超高并发记得“机＋内存”一起扩——索引放内存，多副本才分摊 QPS。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="别踩这些坑-"&gt;&lt;a href="#%e5%88%ab%e8%b8%a9%e8%bf%99%e4%ba%9b%e5%9d%91-" class="header-anchor"&gt;&lt;/a&gt;别踩这些坑 💡
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;向量维度要统一：128 就全 128，别混着来。&lt;/li&gt;
&lt;li&gt;插完别忘 flush：不 flush 就像东西放购物车没结账，搜索不到。&lt;/li&gt;
&lt;li&gt;没 load 就搜索：会报错，先 load()。&lt;/li&gt;
&lt;li&gt;内存不够全加载：用 Partition，分批 load()。&lt;/li&gt;
&lt;li&gt;精度不满意：调 nprobe（IVF）或换 HNSW 试试。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="十大踩坑--急救方案"&gt;&lt;a href="#%e5%8d%81%e5%a4%a7%e8%b8%a9%e5%9d%91--%e6%80%a5%e6%95%91%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;十大踩坑 + 急救方案
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/004-d5e81d65.png"&gt;&lt;/p&gt;
&lt;h2 id="再进阶一点点"&gt;&lt;a href="#%e5%86%8d%e8%bf%9b%e9%98%b6%e4%b8%80%e7%82%b9%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;再进阶一点点
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Hybrid Search：边比向量相似度，边过滤价格 &amp;lt; 500 这种条件，SQL 味道更浓。&lt;/li&gt;
&lt;li&gt;一致性模式：默认够用；真要跨机房强一致性就选 Strong。&lt;/li&gt;
&lt;li&gt;持久化：Milvus 本身用 RocksDB ＋ MinIO 存数据，你不用操心怎么落盘。&lt;/li&gt;
&lt;li&gt;与 RAG 的关系：大模型把文本→向量，Milvus 负责“最近邻检索”，再把查到的文档喂回模型。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="跟其它工具怎么配"&gt;&lt;a href="#%e8%b7%9f%e5%85%b6%e5%ae%83%e5%b7%a5%e5%85%b7%e6%80%8e%e4%b9%88%e9%85%8d" class="header-anchor"&gt;&lt;/a&gt;跟其它工具怎么配？
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;LangChain / LlamaIndex：把 Milvus VectorStore 接进去即可，RAG 极速上线。&lt;/li&gt;
&lt;li&gt;Spark / Flink：批量离线写入 Milvus；确保分批 1 万条以内避免 RPC 超时。&lt;/li&gt;
&lt;li&gt;Airflow：定时 ETL → Embedding → Milvus；flush、compact 都能写成 task。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="到底需要多大机器粗算公式"&gt;&lt;a href="#%e5%88%b0%e5%ba%95%e9%9c%80%e8%a6%81%e5%a4%9a%e5%a4%a7%e6%9c%ba%e5%99%a8%e7%b2%97%e7%ae%97%e5%85%ac%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;“到底需要多大机器？”——粗算公式
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;内存 ≈ （向量维度 × 4 bytes × 向量条数 × 1.4 倍索引系数）﹢ 元数据大小&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;例：1 亿条 768 维 → 768×4×1e8×1.4 ≈ 430 GB（得至少 512 GB 机器，或分区加载）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;硬盘 ≈ 内存 × 1.2（索引 + RocksDB + 日志）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;512G 内存看起来有点儿夸张，所以如果内存吃紧，可以参考以下方法进行优化：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-06-01-milvus-xiang-liang-shu-ju-ku-kuai-su-ru-men-ren-hua-ban/005-35ceb743.png"&gt;&lt;/p&gt;
&lt;h2 id="python-端到端-demo-含增删改查"&gt;&lt;a href="#python-%e7%ab%af%e5%88%b0%e7%ab%af-demo-%e5%90%ab%e5%a2%9e%e5%88%a0%e6%94%b9%e6%9f%a5" class="header-anchor"&gt;&lt;/a&gt;Python 端到端 Demo （含增删改查）
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pymilvus&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;utility&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FieldSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CollectionSchema&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;connections&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;localhost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;19530&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 1. 建楼（如果已存在就删掉重建）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;utility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;has_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;utility&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CollectionSchema&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FieldSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INT64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;is_primary&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auto_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FieldSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FieldSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FieldSchema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;emb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DataType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FLOAT_VECTOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dim&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;demo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 2. 插 10 条数据&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;titles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;商品&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;vecs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;titles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;vecs&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 3. 建 IVF 索引 &amp;amp; 加载&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;emb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;IVF_FLAT&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;metric_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;L2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;params&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;nlist&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 4. 搜索 + 过滤价格 &amp;lt; 50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;qv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tolist&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;hits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;emb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;metric_type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;L2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;params&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;nprobe&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;}},&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;price &amp;lt; 50&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;distance&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 5. 删除一条，再查&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;del_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;id in [&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;del_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;]&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Java老兵的十字路口：坚守还是突围？</title><link>https://xiaobox.github.io/p/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/</link><pubDate>Sat, 29 Mar 2025 05:33:55 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/cover.jpg" alt="Featured image of post Java老兵的十字路口：坚守还是突围？" /&gt;&lt;h2 id="java在技术江湖中的现状稳固基石还是夕阳西下"&gt;&lt;a href="#java%e5%9c%a8%e6%8a%80%e6%9c%af%e6%b1%9f%e6%b9%96%e4%b8%ad%e7%9a%84%e7%8e%b0%e7%8a%b6%e7%a8%b3%e5%9b%ba%e5%9f%ba%e7%9f%b3%e8%bf%98%e6%98%af%e5%a4%95%e9%98%b3%e8%a5%bf%e4%b8%8b" class="header-anchor"&gt;&lt;/a&gt;Java在技术江湖中的现状：稳固基石还是夕阳西下？
&lt;/h2&gt;&lt;p&gt;Java 作为企业级开发的常青树，至今在大量核心系统中扮演着中流砥柱的角色。然而，资深 Java 开发者也明显感受到技术环境的变化。一方面，在银行、政府、互联网巨头等复杂业务场景中，Java 的地位依然稳固；大量遗留系统和核心业务仍运行在 Java 上，短期内很难被完全替换。以优酷的版权管理系统为例，这套长达10年的老系统采用了过时的技术框架，积累了 81万行 Java 代码和大量“没人敢动”的if-else逻辑，可谓技术债累累。像这样的遗留系统，全面重构风险极高，只能在业务推动下逐步演进。因此，在许多传统领域，Java 作为“老码农”的看家本领，仍是不可或缺的基石。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/001-40dd5948.png"&gt;&lt;/p&gt;
&lt;p&gt;另一方面，新兴业务和初创项目对技术栈的选择更加多元。近年Go语言等后起之秀在性能、并发和开发效率上表现出色，成为云原生时代的“宠儿”。不少互联网大厂开始在核心服务中引入 Go：例如谷歌、滴滴、Uber、腾讯等都用 Go 开发高并发、高性能的服务。业界一度流传着“Java 老旧笨重、Go 崭新酷炫”的声音。面对这种冲击，不少资深 Java 工程师难免焦虑：Java 会不会像当年的 COBOL 一样淡出主流舞台？&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/002-28c85ce1.png"&gt;&lt;/p&gt;
&lt;p&gt;事实证明，这种担忧有些过度。Java 拥有数百万开发者和完整生态，并非轻易就能被取代的工具。正如有分析指出，Go 的崛起为行业提供了新选择，但并不是对 Java 的简单替代；两种语言各有优势，未来将长期共存。换言之，Java 依旧是技术江湖里的定海神针，只是江湖规矩变了——老兵们需要适应新玩法。&lt;/p&gt;
&lt;h2 id="java-面临的核心挑战笨重背后的突围"&gt;&lt;a href="#java-%e9%9d%a2%e4%b8%b4%e7%9a%84%e6%a0%b8%e5%bf%83%e6%8c%91%e6%88%98%e7%ac%a8%e9%87%8d%e8%83%8c%e5%90%8e%e7%9a%84%e7%aa%81%e5%9b%b4" class="header-anchor"&gt;&lt;/a&gt;Java 面临的核心挑战：笨重背后的突围
&lt;/h2&gt;&lt;p&gt;资深 Java 开发者在项目实践中遇到的挑战，往往并非语言本身跑不动，而是架构转型带来的“不适感”。近年来微服务、云原生风潮兴起，Java 传统的开发模式在新环境下面临诸多掣肘。&lt;/p&gt;
&lt;h3 id="微服务部署压力"&gt;&lt;a href="#%e5%be%ae%e6%9c%8d%e5%8a%a1%e9%83%a8%e7%bd%b2%e5%8e%8b%e5%8a%9b" class="header-anchor"&gt;&lt;/a&gt;微服务部署压力
&lt;/h3&gt;&lt;p&gt;将单体应用拆成数十上百的微服务后，每个服务都需要独立部署运行。Java 应用启动慢、内存占用高的问题被放大。在同样功能下，一个简单的 Go 容器镜像可能只有十几MB，而等价的 Java（如采用 Helidon 框架）镜像初始体积高达 1.4GB！即使使用模块化手段缩减JDK体积（JLink可降至150MB左右），Java应用的容器仍显得臃肿。如此高的基础开销让追求极致弹性的微服务架构如临大敌：部署50个 Java 微服务可能需要远超预期的硬件资源，这正是很多团队转向更轻量语言的原因之一。有开发者调侃：“即便Java性能再好，内存占用是别人的 2～10倍，成本账算下来也让人犹豫。”而且在无服务器（Serverless）场景下，Java 冷启动延迟更是令人头疼——函数实例首次启动可能耗时数秒到十几秒。这一问题长期无法回避，直到 AWS 推出 SnapStart 等黑科技，把 Java 函数冷启动时间缩短到毫秒级，才算勉强止血。但需要注意，这类优化是云厂商额外提供的特殊支持，侧面说明 Java 在边缘计算、FaaS 等领域的先天劣势依然存在。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/003-c6bc61f8.png"&gt;&lt;/p&gt;
&lt;h3 id="启动速度与样板代码"&gt;&lt;a href="#%e5%90%af%e5%8a%a8%e9%80%9f%e5%ba%a6%e4%b8%8e%e6%a0%b7%e6%9d%bf%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;启动速度与样板代码
&lt;/h3&gt;&lt;p&gt;“万事开头难”在 Java 世界格外贴切。传统 Java Web 应用往往需要预热 JVM、加载大量类和配置，启动一个服务动辄数十秒甚至几分钟。这在强调弹性伸缩的云环境下难以接受。此外，Java 以模板代码多著称，同样的业务逻辑，用 Java 写可能需要冗长的类定义和 Getter/Setter，而用 Go/Kotlin 等语言则简洁得多。例如，Go 以极简的语法实现高并发，让开发者专注于业务；相比之下，过去的 Java 代码显得繁琐累赘，不少老兵自己也调侃天天在写“体力活”。虽然 Java 近年通过 Lambda、Streams、Records 等特性在不断精简，但是遗留项目中的大量样板代码依旧是维护负担。缺乏某些现代语言特性（如模式匹配、代数数据类型）也使Java在表达某些逻辑时不够优雅。这些痛点促使部分团队尝试用 Kotlin 等 JVM 语言替换 Java，以期获得更简洁的语法和更少的冗余。&lt;/p&gt;
&lt;h3 id="并发与性能困境"&gt;&lt;a href="#%e5%b9%b6%e5%8f%91%e4%b8%8e%e6%80%a7%e8%83%bd%e5%9b%b0%e5%a2%83" class="header-anchor"&gt;&lt;/a&gt;并发与性能困境
&lt;/h3&gt;&lt;p&gt;高并发一直是 Java 的强项，但实现方式却日益受到挑战。传统 Java 使用操作系统线程实现并发，每个线程都对应一定的内存和调度开销，在大规模场景下显得“沉重”。为绕过这个瓶颈，过去几年兴起了基于 Reactive 异步编程的微服务框架，通过单线程事件循环避免线程阻塞。然而异步风格代码复杂度高、调试困难，让许多工程师“叫苦不迭”。好消息是，JDK 19/20 引入了虚拟线程（Project Loom）并在 JDK 21 成为正式特性。虚拟线程是由 JVM 管理的超轻量线程，实现了 Go 协程式的并发模型。这意味着开发者可以用以往同步阻塞的简单代码，实现过去需要复杂回调/响应式才能处理的高并发任务。正如 Java 架构师 Brian Goetz 所言：Loom 的出现有望“一举终结Reactive编程的必要”——因为过去Reactive是为解决线程不足的权宜之计。虚拟线程让我们能够创建成千上万个并发任务而不必担心线程耗尽，大幅降低了编写和维护高并发代码的心智负担。然而，新事物也伴随不确定性：老项目若迁移到虚拟线程模型，需要评估线程本地变量、同步机制等是否还能正常工作；调优方式也与传统线程有所不同。目前虚拟线程虽强大，但毕竟是新特性，在大型生产环境中的考验还不充分。Java 老兵们既期待它带来性能飞跃，也需要保持一份观望和谨慎。&lt;/p&gt;
&lt;h3 id="graalvm-与原生执行"&gt;&lt;a href="#graalvm-%e4%b8%8e%e5%8e%9f%e7%94%9f%e6%89%a7%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;GraalVM 与原生执行
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/004-3147eb4e.png"&gt;&lt;/p&gt;
&lt;p&gt;为了解决启动慢、内存高的问题，Java 社区近年另一“大招”是 GraalVM 原生镜像。通过提前编译(AOT)，可以将 Java 应用直接打包成本地可执行文件，启动时间和内存占用都有数量级的优化。这项技术被视为让 Java 重返边缘计算和 Serverless 舞台的希望：原生镜像下，一个 Spring Boot 微服务的“Hello World”容器镜像可小到 ~几十MB；运行时不需要JVM，冷启动延迟大幅降低。实际测试中，采用 GraalVM 原生镜像的 Java 服务在某些基准下性能甚至超越 Go：平均延迟仅0.25毫秒，每秒处理事务达到82426次，吞吐率是 Go 实现的两倍多！这种结果令人振奋，仿佛看到了 Java 打了一场漂亮的翻身仗。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/005-846f3af1.png"&gt;&lt;/p&gt;
&lt;p&gt;然而，理想很丰满，现实有时比较骨感。将复杂应用迁移到 GraalVM 原生镜像并非易事。例如，反射、动态代理等机制需要额外配置支持，许多成熟库在AOT编译下可能行为异常。构建原生镜像的过程也比较繁琐，往往需要调整代码、引入特定插件，并忍受较长的编译时间。调试诊断也更具挑战——原生应用无法使用 JVM 的丰富调试工具，需要新的手段排查问题。此外，引入 GraalVM 还意味着团队需要掌握一套新的知识体系。在的总结中作者就指出：“将 Spring Boot 应用打包为 Native 镜像并非没有挑战”，直接迁移复杂项目可能遇到种种坑，需要开发者充分评估和测试。因此，GraalVM 不是万能灵药，而是有门槛的新武器：用得好，Java 如虎添翼；用不好，反而可能引入新的不稳定因素。&lt;/p&gt;
&lt;p&gt;综上，资深 Java 开发者面对的不是“Java 不行了”，而是如何让 Java 行得更轻、更快、更优雅。JVM 社区显然没有躺在功劳簿上吃老本，而是在微服务、云原生时代积极求变。从 Spring Boot 3 对原生镜像的支持，到 JDK 连续的功能升级（如Records、Pattern Matching等），Java 正在努力破除“笨重”的刻板印象。老兵们需要做的，是与时俱进地拥抱这些变化，用新工具、新思路来武装自己的Java技能库。&lt;/p&gt;
&lt;h2 id="用舍之道哪些场景放弃-java哪些场景坚持-java"&gt;&lt;a href="#%e7%94%a8%e8%88%8d%e4%b9%8b%e9%81%93%e5%93%aa%e4%ba%9b%e5%9c%ba%e6%99%af%e6%94%be%e5%bc%83-java%e5%93%aa%e4%ba%9b%e5%9c%ba%e6%99%af%e5%9d%9a%e6%8c%81-java" class="header-anchor"&gt;&lt;/a&gt;用舍之道：哪些场景放弃 Java，哪些场景坚持 Java？
&lt;/h2&gt;&lt;p&gt;技术选型从来都不是非黑即白，对于 Java 的去留更是如此。究竟在哪些业务场景下应该考虑放弃 Java？哪些场景下 Java 仍是首选？结合真实案例和趋势，我们可以做出以下判断：&lt;/p&gt;
&lt;h3 id="场景一边缘计算与serverless--谨慎使用-java"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%b8%80%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e4%b8%8eserverless--%e8%b0%a8%e6%85%8e%e4%bd%bf%e7%94%a8-java" class="header-anchor"&gt;&lt;/a&gt;场景一：边缘计算与Serverless – 谨慎使用 Java。
&lt;/h3&gt;&lt;p&gt;对于运行环境受限、对启动延迟敏感的场景，选择 Java 需要非常慎重。典型如物联网设备、边缘网关、函数计算等，这些环境往往内存有限且要求冷启动极快。在这些场合，运行一个庞大的 JVM 显然不如直接使用原生语言（C/C++、Rust）或轻量脚本语言（JavaScript、Python）来得高效。过去不少团队在实现事件驱动的小型服务时，就倾向于用 Node.js 或 Python 编写——不是因为Java不能实现功能，而是因为Java在每次调用都要“热身”的开销让人难以忍受。虽然有 GraalVM Native Image 可以大幅优化，但对小型团队而言，引入它的复杂度可能得不偿失。因此，对于边缘和无服务函数等应用，除非有充足理由和相应优化手段，否则倾向于选择更轻便的技术栈。当然，规则也非绝对：如果业务逻辑需要调用大量现有的 Java 类库（例如进行某种算法运算，而相关库只有Java实现），那么即便在 Serverless 环境下通过原生镜像等方式使用 Java 也是可以考虑的。总的来说，在这些场景，“能不用Java就不用”是较为实际的指导原则。&lt;/p&gt;
&lt;h3 id="场景二高性能微服务--视情况取舍"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%ba%8c%e9%ab%98%e6%80%a7%e8%83%bd%e5%be%ae%e6%9c%8d%e5%8a%a1--%e8%a7%86%e6%83%85%e5%86%b5%e5%8f%96%e8%88%8d" class="header-anchor"&gt;&lt;/a&gt;场景二：高性能微服务 – 视情况取舍
&lt;/h3&gt;&lt;p&gt;在互联网分布式系统中，每种语言都有用武之地。如果团队主要目标是极致的性能和资源利用率，并且成员对 Go/Rust 等语言驾轻就熟，那么把部分微服务用新语言实现未尝不可。例如某些网关服务、实时通信服务，行业里确有用 Rust 或 Go 重写后延迟降低、内存减半的成功案例。特别是低延迟、高并发的基础设施组件（消息队列、代理服务器等），很多开源项目早就避开 Java 转投 Go/Rust 怀抱，这是技术基因所致（Java 更擅长业务逻辑，系统编程领域C系语言传统更强）。但是，对于业务逻辑复杂的微服务，Java 仍然具有难以替代的优势：强大的生态提供了各种中间件客户端、成熟的 ORM 和事务框架、安全完备的验证和监控工具等等。这些“全家桶”式的支持使Java在开发业务系统时如鱼得水，大大减少了造轮子的成本。如果纯粹为了追新把此类服务改用另一种语言，可能会发现需要重建许多Java自带的轮子，得不偿失。因此，我们的立场是：对性能极限有追求的核心组件，可以考虑非Java实现以挖掘潜力；但大多数微服务尤其是业务导向的微服务，Java 依然是稳妥且高效的选择。况且随着Quarkus、Micronaut等专为云环境优化的Java框架出现，以及JVM自身的持续优化，Java 微服务的“笨重”正在被削平。正如某次测试所示，在较大的机器上，Java 的吞吐甚至可与 Go 持平甚至略胜一筹——只要用对了方式，Java 完全能胜任高性能微服务。&lt;/p&gt;
&lt;h3 id="场景三大型核心系统--坚定拥抱-java"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%b8%89%e5%a4%a7%e5%9e%8b%e6%a0%b8%e5%bf%83%e7%b3%bb%e7%bb%9f--%e5%9d%9a%e5%ae%9a%e6%8b%a5%e6%8a%b1-java" class="header-anchor"&gt;&lt;/a&gt;场景三：大型核心系统 – 坚定拥抱 Java
&lt;/h3&gt;&lt;p&gt;对于那些 复杂度高、生命周期长、需要强一致性和可靠性的核心业务，Java 无疑仍是值得长期信赖的编程语言。例如银行的核心账务系统、航空公司的订票系统、阿里的电商交易中台等，这些系统往往经历多年演化，业务规则繁多且严谨，需要大量业内验证过的中间件支撑。Java 的严格类型体系和成熟框架在这里如鱼得水。特别是在金融、政府等对稳定性要求极高的领域，“Java + 大型商用中间件”的组合几乎是默认标配。从技术债的角度考虑，这类系统虽然也面临老旧架构的问题，但重构时通常还是在 Java 体系内升级（比如从 Struts 升级到 Spring Boot，或引入分布式事务框架等），而不会轻易迁移到一门全新的语言上。这不仅因为重写成本高，更因为 Java 多年沉淀的安全性和可靠性难以替代。可以说，在复杂业务长跑中，Java 是一匹稳健的“长途马”，跑得也许不算最快，但足够稳当，生态中现成的工具能够覆盖方方面面，让架构师和开发者更安心。基于这些原因，我们坚定认为：在复杂业务和核心系统场景下，坚持使用 Java 是明智之举。即便引入新的技术插件，也是作为补充而非颠覆，比如用 Python 做小部分AI预测，再把结果喂给Java主系统等等。Java 老兵在这些战场上大可发挥深厚经验，将系统设计得健壮且易于维护，为业务保驾护航。&lt;/p&gt;
&lt;p&gt;归纳来说，用舍有道，视需而定：Java 并非万能，同样也远未过时。关键在于根据项目需求选择最合适的工具。在前沿领域不妨多尝试新语言新架构，以保持竞争力；而在关系到企业命脉的长线工程上，Java 依然值得我们托付。&lt;/p&gt;
&lt;h2 id="java老兵的自我进化坚守阵地or华丽转型"&gt;&lt;a href="#java%e8%80%81%e5%85%b5%e7%9a%84%e8%87%aa%e6%88%91%e8%bf%9b%e5%8c%96%e5%9d%9a%e5%ae%88%e9%98%b5%e5%9c%b0or%e5%8d%8e%e4%b8%bd%e8%bd%ac%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Java老兵的自我进化：坚守阵地or华丽转型？
&lt;/h2&gt;&lt;p&gt;面对风起云涌的技术浪潮，10年以上经验的 Java 老将们该何去何从？是固守舒适圈，还是勇敢拓展边界？以下几点建议或许对处在十字路口的你有所启发：&lt;/p&gt;
&lt;h3 id="拥抱java新特性跟上生态演进"&gt;&lt;a href="#%e6%8b%a5%e6%8a%b1java%e6%96%b0%e7%89%b9%e6%80%a7%e8%b7%9f%e4%b8%8a%e7%94%9f%e6%80%81%e6%bc%94%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;拥抱Java新特性，跟上生态演进
&lt;/h3&gt;&lt;p&gt;不要认为“学了十几年Java就没有新东西可学”。相反，Java生态在飞速更新，每年两个版本迭代。老兵们应该主动学习 JDK近几版的新功能（如Records、Sealed Class、Pattern Matching、虚拟线程等），这些特性能显著改善代码质量和性能，使你的技能焕发新生。例如，试着用 Loom 虚拟线程改造一个老的并发模块，体会一下开发模式的简化；或者研究 GraalVM 如何将现有服务无缝打包为原生镜像，了解其中的限制和调优手段。拥抱新技术不仅能提升生产力，也向团队展示了你与时俱进的技术热情。作为Java老兵，切忌固步自封——持续学习是对抗职业倦怠和时代冲击的最好武器。&lt;/p&gt;
&lt;h3 id="拓展多元技能栈成为t型人才"&gt;&lt;a href="#%e6%8b%93%e5%b1%95%e5%a4%9a%e5%85%83%e6%8a%80%e8%83%bd%e6%a0%88%e6%88%90%e4%b8%bat%e5%9e%8b%e4%ba%ba%e6%89%8d" class="header-anchor"&gt;&lt;/a&gt;拓展多元技能栈，成为“T型”人才
&lt;/h3&gt;&lt;p&gt;在保持Java优势的同时，建议横向拓展一到两门其它语言或领域技能。比如，可以尝试学习 Go 或 Python，用它们做些小项目，体会不同语言在思维模型上的差异。再比如，深入了解一下前端技术或移动开发，哪怕不做前端，也能与前端同事更高效协作。这种“T”字型的技能结构（既有一门精深的主力技术，又对相关技术有所涉猎）将使你在团队中更具价值。很多架构师在成为架构师前，都曾是精通数门语言、熟悉多种数据库和中间件的全能型工程师。对Java老兵来说，学习一门新语言还能帮助跳出现有思维框架，把新理念反哺到Java日常开发中。例如，借鉴函数式编程思想优化Java代码，或者用脚本语言编写自动化工具提升开发效率。多元化的技能还为你提供了职业备胎：万一某天真的不想写Java了，你在其他领域的积累也足以支撑转型，不至于手足无措。&lt;/p&gt;
&lt;h3 id="深入业务和架构提升不可替代性"&gt;&lt;a href="#%e6%b7%b1%e5%85%a5%e4%b8%9a%e5%8a%a1%e5%92%8c%e6%9e%b6%e6%9e%84%e6%8f%90%e5%8d%87%e4%b8%8d%e5%8f%af%e6%9b%bf%e4%bb%a3%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;深入业务和架构，提升不可替代性
&lt;/h3&gt;&lt;p&gt;随着工作年限增长，“懂业务、能设计”往往比单纯的编码能力更重要。Java老兵应该充分利用在一个行业浸润多年的经验，去深入理解业务领域 的本质问题，把握业务发展方向。将业务洞察与技术方案相结合，主动参与系统架构设计和重大技术选型，这会让你成为团队中不可或缺的核心人物。很多时候，业务专家+技术专家的复合型人才，比仅仅精通某种语法的程序员更有竞争力。如果你已经是某核心系统的Owner，不妨尝试推进架构优化和性能提升项目，展示自己在宏观层面的掌控力。同时，培养自己的系统设计能力，多研究业界大型系统的架构案例，学习它们如何权衡取舍。当你能从容驾驭分布式事务、异地多活、CQRS 等架构模式时，你的价值早已超越“Java 工程师”的范畴，而成为真正的技术专家。这种升级，无论未来Java的热度如何，都能让你的职业生涯保持上升。&lt;/p&gt;
&lt;h3 id="考虑转型技术管理或其他新领域"&gt;&lt;a href="#%e8%80%83%e8%99%91%e8%bd%ac%e5%9e%8b%e6%8a%80%e6%9c%af%e7%ae%a1%e7%90%86%e6%88%96%e5%85%b6%e4%bb%96%e6%96%b0%e9%a2%86%e5%9f%9f" class="header-anchor"&gt;&lt;/a&gt;考虑转型技术管理或其他新领域
&lt;/h3&gt;&lt;p&gt;并非每个人都要永远写代码。工作十年以上后，你也可以根据兴趣转型，选择最适合自己的道路。如果你热衷带团队和项目把控，可以逐步走向 技术管理 岗位，担任Team Leader、技术经理甚至CTO，把多年经验用于培养新人和决策把关。很多Java老兵在这一阶段选择带领团队，既可传承自己的开发哲学，又能获得管理成就感。又或者，你对某些新兴领域情有独钟，例如 人工智能、大数据、安全 等，不妨利用业余时间学习相关知识，寻求内部调岗或外部机会。资深程序员转做产品经理、解决方案架构师的例子也屡见不鲜——只要有心，完全可以跳出演员阵容，转到幕后编剧或导演的位置上。当然，做出转型决定前需要评估清楚：你的核心竞争力是什么，新领域是否真心喜欢，从头开始是否有心理准备。转型不是逃避，而是为了更长远的发展。无论选择深耕Java栈还是开拓新跑道，持续的学习和热情都是关键驱动力。&lt;/p&gt;
&lt;h2 id="最后"&gt;&lt;a href="#%e6%9c%80%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;最后
&lt;/h2&gt;&lt;p&gt;想对每一位焦虑中的Java老兵说：技术江湖瞬息万变，但真正的资深工程师价值从不局限于某种语法。Java 之父 James Gosling 曾打比方说：“Java就像一辆可靠的卡车”，或许它没有跑车那样光鲜，但能载着重载货物稳稳前行。这辆卡车如今也在不断改装升级，动力和油耗都在改进。我们作为司机，要做的不是弃车而逃，而是练就更高超的驾驶技巧，并且学会在不同道路上换合适的交通工具。坚守初心并不代表故步自封，拥抱变化也不意味着全盘否定过去。当我们既掌握了Java这门老牌利器，又勇于学习新招式、新套路，在变化的浪潮中依然能找到自己的方向和节奏。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/006-0c6fa2fa.png"&gt;&lt;/p&gt;
&lt;p&gt;10+年开发生涯沉淀下来的经验与智慧，是宝贵的财富。无论Java的流行曲线如何波动，真正优秀的工程师都会不断进化，拓展自己的边界。在这个过程中，我们既要有克制冷静的思考，看清技术演进的本质；也要保持对编程的热爱，不忘初心地享受技术创造的乐趣。愿每一位Java老兵都能在时代洪流中找到属于自己的位置：该出手时果断出手，该坚守时稳如磐石，在新的十年里续写属于你的传奇。&lt;/p&gt;</description></item><item><title>面试必问：ACID 你真的懂了吗？</title><link>https://xiaobox.github.io/p/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/</link><pubDate>Wed, 19 Feb 2025 07:45:55 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/cover.jpg" alt="Featured image of post 面试必问：ACID 你真的懂了吗？" /&gt;&lt;h2 id="acid-是什么"&gt;&lt;a href="#acid-%e6%98%af%e4%bb%80%e4%b9%88" class="header-anchor"&gt;&lt;/a&gt;ACID 是什么？
&lt;/h2&gt;&lt;p&gt;事务处理中的 ACID 是确保数据库操作可靠性和完整性的四个核心特性&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;&lt;strong&gt;属性&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;说明&lt;/strong&gt;&lt;/th&gt;
 &lt;th&gt;&lt;strong&gt;示例&lt;/strong&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;原子性（Atomicity）&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;事务是不可分割的最小操作单元，事务中的所有操作要么全部成功完成，要么全部失败回滚。&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;用户在线购买书籍时的支付流程：&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;①支付扣款 ②库存扣减 ③快递下单三个步骤必须全部成功——任一步骤失败时（如库存不足），系统自动取消已付金额，退回到购物未进行状态。&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;一致性（Consistency）&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;事务必须保证数据库从一个一致性状态转变到另一个一致性状态。一致性是指数据必须符合预定义的规则和约束，例如完整性约束、业务规则等。&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;银行转账场景：&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;账户A向账户B转200元后，两人账户总额保持不变（若A+B原为1000元，操作完成后仍为1000）。即便系统中途崩溃，恢复后也不会出现A扣200元但B未入账的金额&amp;quot;凭空消失&amp;quot;。&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;隔离性（Isolation）&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;多个事务并发执行时，每个事务都应该感觉不到其他事务的存在，就像在隔离的环境中执行一样。事务之间互相隔离，不会互相影响。&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;航班订座系统：&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;当乘客A和B同时选择最后一个座位，先完成支付者的订单立即锁定座位，另一用户将实时看到&amp;quot;无余票&amp;quot;提示——避免出现系统误判导致超售。&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;持久性（Durability）&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;一旦事务提交成功，对数据库的修改就应该是永久性的，即使系统发生崩溃或重启等意外情况，数据也不会丢失。&lt;/em&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;线上预约挂号确认：&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;用户成功提交预约后，即便医院服务器遭遇断电，重启后系统依然保留该条预约记录并发送确认短信，不会因突发意外丢失数据。&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;td&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="mysql-是如何保证-acid-的"&gt;&lt;a href="#mysql-%e6%98%af%e5%a6%82%e4%bd%95%e4%bf%9d%e8%af%81-acid-%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;MySQL 是如何保证 ACID 的？
&lt;/h2&gt;&lt;p&gt;MySQL 实现 ACID 特性主要依赖 日志系统（undo log 和 redo log）、锁机制 和 MVCC 多版本并发控制。下面是具体实现原理的详细分析：&lt;/p&gt;
&lt;h3 id="一atomicity原子性"&gt;&lt;a href="#%e4%b8%80atomicity%e5%8e%9f%e5%ad%90%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;一、Atomicity（原子性）
&lt;/h3&gt;&lt;p&gt;事务是不可分割的最小执行单位。原子性确保事务中的所有操作要么全部成功完成，要么全部失败回滚。不允许中间状态。MySQL 通过 &lt;strong&gt;Undo Log + 事务回滚&lt;/strong&gt; 实现原子性：&lt;/p&gt;
&lt;p&gt;当事务开始时，InnoDB 会记录事务修改前的数据（旧版本）到 &lt;code&gt;Undo Log&lt;/code&gt; 中，用于事务回滚时恢复原始状态。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;code&gt;Undo Log&lt;/code&gt; 记录结构包含：原始数据值、事务ID（trx_id）、回滚指针（roll_pointer）。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/001-8d841891.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Undo Log&lt;/code&gt; 记录的是逻辑操作，例如 &amp;ldquo;删除第 10 行&amp;rdquo;，&amp;ldquo;将字段 &amp;rsquo;name&amp;rsquo; 从 &amp;lsquo;old&amp;rsquo; 更新为 &amp;rsquo;new&amp;rsquo;&amp;rdquo; 等。&lt;/p&gt;
&lt;p&gt;举个简单例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 事务未提交时，其他事务通过Undo Log读取原始数据（MVCC）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;BEGIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;balance&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;-- 此时Undo Log会记录balance的旧值（如200）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ROLLBACK&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 使用Undo Log恢复数据
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果事务执行过程中发生错误或者用户显式执行 ROLLBACK，InnoDB 可以根据 &lt;code&gt;Undo Log&lt;/code&gt; 中的记录将数据恢复到事务开始之前的状态，从而实现事务的回滚&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="二isolation隔离性"&gt;&lt;a href="#%e4%ba%8cisolation%e9%9a%94%e7%a6%bb%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;二、Isolation（隔离性）
&lt;/h3&gt;&lt;p&gt;隔离性 (Isolation) 是 ACID 特性中的关键一环，它确保在多个事务并发执行时，每个事务都仿佛独立运行，互不干扰。 换句话说，一个事务的中间状态和操作不应该被其他并发事务感知到，从而避免数据混乱和不一致。 为了实现这种隔离效果，MySQL 的 InnoDB 存储引擎主要依赖于两大核心机制：&lt;strong&gt;锁机制 (Locking)&lt;/strong&gt; 和 &lt;strong&gt;多版本并发控制 (MVCC)&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id="1-锁机制"&gt;&lt;a href="#1-%e9%94%81%e6%9c%ba%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;1. 锁机制
&lt;/h4&gt;&lt;p&gt;首先，&lt;strong&gt;锁机制是最基础的隔离手段&lt;/strong&gt;。InnoDB 实现了多种锁类型，以适应不同的并发场景和隔离需求。 其中，&lt;code&gt;行级锁&lt;/code&gt; 是 InnoDB 并发控制的核心，它允许事务仅锁定需要修改的数据行，最大程度地提高了并发度。 行级锁又细分为 &lt;code&gt;共享锁 (S 锁)&lt;/code&gt; 和 &lt;code&gt;排他锁 (X 锁)&lt;/code&gt;，前者允许多个事务同时读取同一行数据，而后者则保证在更新或删除数据时，只有一个事务可以独占该行。&lt;/p&gt;
&lt;p&gt;除了行级锁，MySQL 还提供 &lt;code&gt;表级锁&lt;/code&gt;，它会锁定整个表，虽然并发度较低，但在某些特定场景（如执行 LOCK TABLES 语句）下仍然适用。&lt;/p&gt;
&lt;p&gt;为了更高效地管理锁，InnoDB 引入了 &lt;code&gt;意向锁 (Intention Locks)&lt;/code&gt;，它在表级别上预先声明事务对行级锁的意图，从而优化锁的检查和兼容性。&lt;/p&gt;
&lt;p&gt;此外，在 REPEATABLE READ 和 SERIALIZABLE 这两个较高的隔离级别下，为了解决幻读问题，InnoDB 还使用了 &lt;code&gt;间隙锁 (Gap Locking)&lt;/code&gt;，它不仅锁定已存在的记录，还锁定索引记录之间的间隙，防止其他事务在该间隙中插入新记录，从而彻底避免幻读。&lt;/p&gt;
&lt;p&gt;我们通过一个具体的例子来说明 InnoDB 的 间隙锁（Gap Locking） 如何解决幻读问题：&lt;/p&gt;
&lt;p&gt;假设有一张表 &lt;code&gt;students&lt;/code&gt;，存储学生信息，主键为 &lt;code&gt;id&lt;/code&gt;，当前数据如下：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;id&lt;/th&gt;
 &lt;th&gt;name&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;Alice&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;Bob&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;Charlie&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;现在有两个事务 &lt;strong&gt;事务A&lt;/strong&gt; 和 &lt;strong&gt;事务A&lt;/strong&gt;，操作顺序如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;事务A&lt;/strong&gt; 执行范围查询并加锁：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;BEGIN;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;SELECT * FROM students WHERE id BETWEEN 1 AND 5 FOR UPDATE;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;-- 查询结果：id=1, 3, 5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;InnoDB 会为 &lt;code&gt;id&lt;/code&gt; 索引加上 &lt;strong&gt;Next-Key Lock&lt;/strong&gt;（行锁 + 间隙锁），锁定的范围包括：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(-∞, 1]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(1, 3]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(3, 5]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(5, +∞)&lt;/code&gt;
（注：假设表中无其他数据）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;事务B&lt;/strong&gt; 尝试插入新数据：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;BEGIN;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;INSERT INTO students (id, name) VALUES (2, &amp;#39;David&amp;#39;); -- 尝试插入到间隙 (1,3)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;-- 或者
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;INSERT INTO students (id, name) VALUES (4, &amp;#39;Eve&amp;#39;); -- 尝试插入到间隙 (3,5)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;由于 &lt;strong&gt;事务A&lt;/strong&gt; 的间隙锁锁定了 &lt;code&gt;(1,3)&lt;/code&gt; 和 &lt;code&gt;(3,5)&lt;/code&gt; 的间隙，&lt;strong&gt;事务B&lt;/strong&gt; 的插入操作会被阻塞，直到 &lt;strong&gt;事务A&lt;/strong&gt; 提交或回滚。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;事务A&lt;/strong&gt; 提交：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;COMMIT;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;事务B&lt;/strong&gt; 的插入操作才会继续执行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;结果对比&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;没有间隙锁&lt;/strong&gt;：
若事务A未加间隙锁（例如使用 &lt;code&gt;READ COMMITTED&lt;/code&gt; 隔离级别），事务B可以插入 &lt;code&gt;id=2&lt;/code&gt; 或 &lt;code&gt;id=4&lt;/code&gt;。当事务A再次执行 &lt;code&gt;SELECT&lt;/code&gt; 时，会看到新插入的行（id=2 或 4），导致&lt;strong&gt;幻读&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;有间隙锁&lt;/strong&gt;：
事务B的插入操作被阻塞，直到事务A释放锁。事务A在事务执行期间始终看到相同的数据（id=1,3,5），&lt;strong&gt;避免幻读&lt;/strong&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="关键点"&gt;&lt;a href="#%e5%85%b3%e9%94%ae%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;关键点&lt;/strong&gt;
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;间隙锁的锁定范围&lt;/strong&gt;：
InnoDB 的间隙锁不仅锁定已存在的行，还会锁定索引记录之间的“间隙”（例如 &lt;code&gt;(1,3)&lt;/code&gt; 和 &lt;code&gt;(3,5)&lt;/code&gt;），阻止其他事务在间隙中插入新数据。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Next-Key Lock 的作用&lt;/strong&gt;：
Next-Key Lock = 行锁（锁定已存在记录） + 间隙锁（锁定间隙）。例如，对 &lt;code&gt;id=3&lt;/code&gt; 的行锁会锁定范围 &lt;code&gt;(1,3]&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;隔离级别的影响&lt;/strong&gt;：
间隙锁仅在 &lt;code&gt;REPEATABLE READ&lt;/code&gt; 隔离级别下生效。在 &lt;code&gt;READ COMMITTED&lt;/code&gt; 级别下，InnoDB 会禁用间隙锁，幻读仍可能发生。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在实际场景中，比如在电商系统中，若一个事务正在统计某商品（例如库存范围在 100~200）的订单数量，间隙锁可以防止其他事务插入新的订单记录（例如库存为 150 的商品），确保统计结果的一致性。&lt;/p&gt;
&lt;h4 id="2-mvcc"&gt;&lt;a href="#2-mvcc" class="header-anchor"&gt;&lt;/a&gt;2. MVCC
&lt;/h4&gt;&lt;p&gt;为了进一步提升并发性能，尤其是在读多写少的场景下，InnoDB 引入了 &lt;code&gt;多版本并发控制 (MVCC)&lt;/code&gt;。 &lt;strong&gt;MVCC 的核心思想是允许事务在读取数据时，访问数据在某个时间点的快照版本，而不是直接读取最新的数据。&lt;/strong&gt; 这样，读操作就不需要等待写操作完成，从而实现读写并发执行，显著提高了系统吞吐量。 MVCC 的实现依赖于 &lt;code&gt;Undo Log&lt;/code&gt; 和 &lt;code&gt;Read View (快照读)&lt;/code&gt;。 &lt;code&gt;Undo Log&lt;/code&gt; 用于记录数据的历史版本，而 &lt;code&gt;Read View&lt;/code&gt; 则定义了事务在读取数据时应该看到哪个版本的数据。MVCC 主要应用于 READ COMMITTED 和 REPEATABLE READ 这两个隔离级别，在这两个级别下，MVCC 可以有效减少锁的竞争，提升并发性能。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/002-9516a24c.png"&gt;&lt;/p&gt;
&lt;p&gt;总结来说：MVCC 就是基于隐藏字段、undo_log 链和 ReadView 来实现的&lt;/p&gt;
&lt;h4 id="3-隔离级别与策略对比"&gt;&lt;a href="#3-%e9%9a%94%e7%a6%bb%e7%ba%a7%e5%88%ab%e4%b8%8e%e7%ad%96%e7%95%a5%e5%af%b9%e6%af%94" class="header-anchor"&gt;&lt;/a&gt;3. 隔离级别与策略对比
&lt;/h4&gt;&lt;p&gt;最后，为了满足不同应用场景对隔离程度和性能的不同需求，MySQL 提供了 四种事务隔离级别。 从最低的 READ UNCOMMITTED (读未提交) 到最高的 SERIALIZABLE (串行化)，隔离级别依次增强，但并发性能也随之降低。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;READ UNCOMMITTED 允许脏读，隔离性最弱，但性能最高；&lt;/li&gt;
&lt;li&gt;READ COMMITTED 避免了脏读，但可能出现不可重复读；&lt;/li&gt;
&lt;li&gt;REPEATABLE READ (&lt;strong&gt;InnoDB 默认级别&lt;/strong&gt;) 在 READ COMMITTED 的基础上解决了不可重复读，但仍可能存在幻读（在某些情况下，InnoDB 通过 Next-Key Locking 尝试解决幻读）；&lt;/li&gt;
&lt;li&gt;SERIALIZABLE 通过强制事务串行执行，彻底避免了所有并发问题，但并发性能也最低。&lt;/li&gt;
&lt;/ul&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;th&gt;实现方式&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;READ UNCOMMITTED&lt;/td&gt;
 &lt;td&gt;✔️&lt;/td&gt;
 &lt;td&gt;✔️&lt;/td&gt;
 &lt;td&gt;✔️&lt;/td&gt;
 &lt;td&gt;无锁&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;READ COMMITTED&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;✔️&lt;/td&gt;
 &lt;td&gt;✔️&lt;/td&gt;
 &lt;td&gt;每个SELECT生成新Read View&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;REPEATABLE READ*&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;✖️△&lt;/td&gt;
 &lt;td&gt;首SELECT生成Read View + 间隙锁&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;SERIALIZABLE&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;✖️&lt;/td&gt;
 &lt;td&gt;所有SELECT隐式转成&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;

 &lt;blockquote&gt;
 &lt;p&gt;△：MySQL通过Next-Key Lock（行锁+间隙锁组合）在REPEATABLE READ级别实际消除幻读。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id="三durability持久性"&gt;&lt;a href="#%e4%b8%89durability%e6%8c%81%e4%b9%85%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;三、Durability（持久性）
&lt;/h3&gt;&lt;p&gt;持久性 (Durability) 是 ACID 特性中保障数据安全性的最后一道防线。 它确保一旦事务成功提交，对数据库所做的所有更改都必须被永久地保存下来，即使系统随后发生崩溃、断电或任何其他类型的故障，已提交的数据也绝不会丢失。 为了实现这种强大的数据保障，MySQL 的 InnoDB 存储引擎采用了一系列精密的机制，其中最核心的是 &lt;code&gt;Redo Log&lt;/code&gt; (重做日志)，并辅以 &lt;code&gt;Write-Ahead Logging (WAL)&lt;/code&gt; 策略、 &lt;code&gt;Doublewrite Buffer&lt;/code&gt; (双写缓冲区) 和灵活的 刷盘 (Flush to Disk) 机制，同时，&lt;code&gt;Binlog&lt;/code&gt; (二进制日志) 也从更广泛的层面为数据持久性提供了支持。&lt;/p&gt;
&lt;p&gt;首先，Redo Log 是 InnoDB 实现持久性的基石。 当一个事务执行过程中，InnoDB 并不会立即将数据页的修改直接写入磁盘上的数据文件，而是先将这些修改操作，例如插入、更新或删除的具体内容，以一种紧凑、高效的形式，顺序地记录到 Redo Log Buffer 中。 这里的 Redo Log 记录的是物理层面的修改，例如“将数据页 X 的偏移量 Y 处的 Z 个字节修改为新的值”。 为了保证效率，Redo Log Buffer 存在于内存中，但为了确保持久性，InnoDB 会定期或者在事务提交时，将 Redo Log Buffer 中的内容刷新到 Redo Log 文件 这一磁盘上的持久化存储。&lt;/p&gt;
&lt;p&gt;为了进一步确保数据在极端情况下的安全性，InnoDB 遵循 &lt;code&gt;Write-Ahead Logging&lt;/code&gt; (WAL) 预写式日志 策略。 这意味着，在任何数据页的实际修改被写入磁盘数据文件之前，必须先将相应的 Redo Log 记录落盘到 Redo Log 文件中。 &lt;strong&gt;这种 “先写日志，后写数据” 的机制至关重要，它保证了即使在数据页尚未完全刷入磁盘时系统发生崩溃，已经提交的事务的所有修改操作也已经安全地记录在 Redo Log 中，从而为后续的数据恢复提供了保障。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/003-3900f38d.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Doublewrite Buffer&lt;/code&gt; (双写缓冲区) 是 InnoDB 为了应对数据页“部分写失效 (Partial Write)” 问题而引入的增强机制。 在数据页从内存刷新到磁盘数据文件的过程中，可能会因为断电等意外情况，导致数据页只写入了一部分，造成数据损坏。 为了避免这种情况，InnoDB 在数据页最终写入数据文件之前，会先将其完整地写入 Doublewrite Buffer 区域。 Doublewrite Buffer 是磁盘上一个连续的存储区域，InnoDB 会先顺序写入，保证写入的原子性。 之后，再将数据页从 Doublewrite Buffer 拷贝到真正的数据文件位置。 这样，即使在数据页写入过程中发生崩溃，InnoDB 在重启恢复时，可以通过 Doublewrite Buffer 检查数据页的完整性。 如果发现数据页写入不完整或已损坏，可以从 Doublewrite Buffer 中找到该数据页的完整副本进行恢复，从而有效地避免了数据页部分写入导致的数据丢失。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Flush to Disk&lt;/code&gt; 机制 则提供了对 Redo Log 和数据页刷盘行为的精细控制。 MySQL 提供了多个参数，例如 innodb_flush_log_at_trx_commit 参数控制 Redo Log 何时刷盘，可以设置为每次事务提交都刷盘 (最安全，但性能较低)，或者定期刷盘 (性能较高，但可能在崩溃时丢失少量已提交事务)。 innodb_flush_method 参数则控制数据页刷盘的具体方式，例如是否绕过操作系统缓存直接写入磁盘，以满足不同的性能和可靠性需求。 通过调整这些刷盘策略，用户可以在数据安全性和性能之间进行权衡，根据实际业务场景选择合适的配置。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-19-mian-shi-bi-wen-acid-ni-zhen-de-dong-le-ma/004-7797e2c0.png"&gt;&lt;/p&gt;
&lt;p&gt;最后，虽然 Binlog (二进制日志) 的主要用途是用于数据库的主从复制和时间点恢复，但它也间接地为数据持久性做出了贡献。 Binlog 记录了数据库中所有的数据变更操作 (逻辑操作，例如 SQL 语句)，这些日志可以用于数据库的备份和恢复，特别是当需要进行全量或增量备份，或者需要恢复到某个特定的时间点时，Binlog 就显得至关重要。 虽然 Binlog 的关注点和 Redo Log 略有不同 (Redo Log 侧重于崩溃恢复，Binlog 侧重于时间点恢复和复制)，但它们都为确保数据的长期安全性和可恢复性提供了重要的支持。&lt;/p&gt;
&lt;p&gt;总结来说：&lt;strong&gt;MySQL InnoDB 通过 Redo Log + WAL 策略 保障事务提交的修改能够被可靠地记录下来， Doublewrite Buffer 增强了数据页写入的可靠性，Flush to Disk 机制 提供了灵活的刷盘控制，而 Binlog 则从更广泛的层面支持数据备份和时间点恢复&lt;/strong&gt;。 这些机制相互配合，共同构建了持久性保障体系。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="四consistency一致性"&gt;&lt;a href="#%e5%9b%9bconsistency%e4%b8%80%e8%87%b4%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;四、Consistency（一致性）
&lt;/h3&gt;&lt;p&gt;事务必须保证数据库从一个一致性状态转变到另一个一致性状态。一致性是指数据库的完整性约束没有被破坏。例如，主键唯一性、外键约束、CHECK 约束等。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;约束 (Constraints): MySQL 支持各种约束，如主键 (PRIMARY KEY)、外键 (FOREIGN KEY)、唯一键 (UNIQUE)、非空 (NOT NULL)、检查约束 (CHECK) 等。这些约束在数据写入时被强制执行，确保数据满足预定义的规则。&lt;/li&gt;
&lt;li&gt;触发器 (Triggers): 触发器是与表关联的存储程序，在特定事件 (如 INSERT、UPDATE、DELETE) 发生时自动执行。触发器可以用于实现更复杂的业务规则和一致性检查。&lt;/li&gt;
&lt;li&gt;应用程序逻辑: 虽然 MySQL 提供了约束和触发器，但最终的数据一致性也需要应用程序逻辑来保证。例如，业务逻辑需要确保事务操作符合业务规则，才能维持数据库的一致性状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实对于一致性来说，&lt;strong&gt;它是其他三者（原子性、隔离性、持久性）的综合结果，辅以数据库约束和应用校验来共同保障最终一致性。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="总结"&gt;&lt;a href="#%e6%80%bb%e7%bb%93" class="header-anchor"&gt;&lt;/a&gt;&lt;strong&gt;总结&lt;/strong&gt;
&lt;/h2&gt;&lt;p&gt;MySQL通过以下核心机制实现ACID：&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;ACID特性&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;strong&gt;原子性&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Undo Log + 事务状态管理&lt;/td&gt;
 &lt;td&gt;Undo Log、事务控制块&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;一致性&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;约束 + ACID协同&lt;/td&gt;
 &lt;td&gt;主键、外键、触发器&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;隔离性&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;MVCC + 锁 + Next-Key Locks&lt;/td&gt;
 &lt;td&gt;Read View、行锁、间隙锁&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;strong&gt;持久性&lt;/strong&gt;&lt;/td&gt;
 &lt;td&gt;Redo Log + Doublewrite Buffer&lt;/td&gt;
 &lt;td&gt;Redo Log、双写缓冲区&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;我们经常说的&lt;strong&gt;最终一致性&lt;/strong&gt;是其他三个特性协同作用的结果，而非独立机制。当你理解了这些底层原理将会有助于优化事务设计（如合理选择隔离级别）和故障排查（如分析锁冲突）。&lt;/p&gt;</description></item><item><title>Redis6.0 以后为什么使用了多线程？</title><link>https://xiaobox.github.io/p/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/</link><pubDate>Mon, 18 Nov 2024 02:04:32 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/cover.jpg" alt="Featured image of post Redis6.0 以后为什么使用了多线程？" /&gt;&lt;p&gt;Redis 所谓的单线程并不是所有工作都是只有一个线程在执行，而是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的，Redis 在处理客户端的请求时包括获取 (socket 读）、解析、执行、内容返回 (socket 写） 等都由一个顺序串行的主线程处理。&lt;/p&gt;
&lt;p&gt;这就是所谓的“&lt;strong&gt;单线程&lt;/strong&gt;”。这也是 Redis 对外提供键值存储服务的主要流程。
由于 Redis 在处理命令的时候是单线程作业的，所以会有一个 Socket 队列，每一个到达的服务端命令来了之后都不会马上被执行，而是进入队列，然后被线程的事件分发器逐个执行。如下图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/001-b7c0094d.png"&gt;&lt;/p&gt;
&lt;p&gt;至于 Redis 的其他功能， 比如持久化、异步删除、集群数据同步等等，其实是由额外的线程执行的。 可以这么说，Redis 工作线程是单线程的。但是在 4.0 之后，对于整个 Redis 服务来说，还是多线程运作的。&lt;/p&gt;
&lt;h2 id="60-之前为什么要使用单线程"&gt;&lt;a href="#60-%e4%b9%8b%e5%89%8d%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e4%bd%bf%e7%94%a8%e5%8d%95%e7%ba%bf%e7%a8%8b" class="header-anchor"&gt;&lt;/a&gt;6.0 之前为什么要使用单线程
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/002-55bc246f.png"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在使用 Redis 时，Redis 主要受限是在内存和网络上，CPU 几乎没有性能瓶颈的问题。&lt;/li&gt;
&lt;li&gt;以 Linux 系统为例子，在 Linux 系统上 Redis 通过 pipelining 可以处理 100w 个请求每秒，而应用程序的计算复杂度主要是 O(N) 或 O(log(N)) ，不会消耗太多 CPU。&lt;/li&gt;
&lt;li&gt;使用了单线程后，提高了可维护性。多线程模型在某些方面表现优异，却增加了程序执行顺序的不确定性，并且带来了并发读写的一系列问题，增加了系统复杂度。同时因为线程切换、加解锁，甚至死锁，造成一定的性能损耗。&lt;/li&gt;
&lt;li&gt;Redis 通过 AE 事件模型以及 IO 多路复用等技术，拥有超高的处理性能，因此没有使用多线程的必要&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="60-之后的多线程主要解决什么问题"&gt;&lt;a href="#60-%e4%b9%8b%e5%90%8e%e7%9a%84%e5%a4%9a%e7%ba%bf%e7%a8%8b%e4%b8%bb%e8%a6%81%e8%a7%a3%e5%86%b3%e4%bb%80%e4%b9%88%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;6.0 之后的多线程主要解决什么问题
&lt;/h2&gt;&lt;p&gt;近年来底层网络硬件性能越来越好，Redis 的性能瓶颈逐渐体现在网络 I/O 的读写上，单个线程处理网络 I/O 读写的速度跟不上底层网络硬件执行的速度。&lt;/p&gt;
&lt;p&gt;Redis 在处理网络数据时，调用 epoll 的过程是阻塞的，这个过程会阻塞线程。如果并发量很高，达到万级别的 QPS，就会形成瓶颈，影响整体吞吐能力&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/003-e1c54fcd.png"&gt;&lt;/p&gt;
&lt;p&gt;既然读写网络的 read/write 系统调用占用了 Redis 执行期间大部分 CPU 时间，那么要想真正做到提速，必须改善网络 IO 性能。我们可以从这两个方面来优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;提高网络 IO 性能，典型实现方式比如使用 DPDK 来替代内核网络栈的方式&lt;/li&gt;
&lt;li&gt;使用多线程，这样可以充分利用多核 CPU，同类实现案例比如 Memcached。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;协议栈优化的这种方式跟 Redis 关系不大，所以最便捷高效的方式就是支持多线程。总结起来，redis 支持多线程就是以下两个原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可以充分利用服务器 CPU 的多核资源，而主线程明显只能利用一个&lt;/li&gt;
&lt;li&gt;多线程任务可以分摊 Redis 同步 IO 读写负荷，降低耗时&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;6.0 版本优化之后，主线程和多线程网络 IO 的执行流程如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-11-18-redis6-0-yi-hou-wei-shen-me-shi-yong-le-duo-xian-cheng/004-51bc3556.png"&gt;&lt;/p&gt;
&lt;p&gt;具体步骤如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主线程建立连接，并接受数据，并将获取的 socket 数据放入等待队列；&lt;/li&gt;
&lt;li&gt;通过轮询的方式将 socket 读取出来并分配给 IO 线程；&lt;/li&gt;
&lt;li&gt;之后主线程保持阻塞，一直等到 IO 线程完成 socket 读取和解析；&lt;/li&gt;
&lt;li&gt;I/O 线程读取和解析完成之后，返回给主线程 ，主线程开始执行 Redis 命令；&lt;/li&gt;
&lt;li&gt;执行完 Redis 命令后，主线程阻塞，直到 IO 线程完成 结果回写到 socket 的工作；&lt;/li&gt;
&lt;li&gt;主线程清空已完成的队列，等待客户端新的请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本质上是将主线程 IO 读写的这个操作 独立出来，单独交给一个 I/O 线程组处理。
这样多个 socket 读写可以并行执行，整体效率也就提高了。同时注意 Redis 命令还是主线程串行执行。&lt;/p&gt;
&lt;p&gt;利用多核来分担 I/O 读写负荷。在事件处理线程每次获取到可读事件时，会将所有就绪的读事件分配给 I/O 线程，并进行等待，在所有 I/O 线程完成读操作后，事件处理线程开始执行任务处理，在处理结束后，同样将写事件分配给 I/O 线程，等待所有 I/O 线程完成写操作。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;int handleClientsWithPendingReadsUsingThreads(void) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; /* Distribute the clients across N different lists. */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; listIter li;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; listNode *ln;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; listRewind(server.clients_pending_read,&amp;amp;li);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; int item_id = 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; // 将等待处理的客户端分配给 I/O 线程
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; while((ln = listNext(&amp;amp;li))) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; client *c = listNodeValue(ln);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; int target_id = item_id % server.io_threads_num;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; listAddNodeTail(io_threads_list[target_id],c);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; item_id++;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; /* Wait for all the other threads to end their work. */
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; // 轮训等待所有 I/O 线程处理完
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; while(1) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; unsigned long pending = 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; for (int j = 1; j &amp;lt; server.io_threads_num; j++)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; pending += io_threads_pending[j];
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; if (pending == 0) break;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; return processed;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;本质上是利用多核的多线程让多个 IO 的读写加速。&lt;/p&gt;
&lt;h3 id="局限性"&gt;&lt;a href="#%e5%b1%80%e9%99%90%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;局限性
&lt;/h3&gt;&lt;p&gt;6.0 版本的多线程并非彻底的多线程，I/O 线程只能同时执行读或者同时执行写操作，期间事件处理线程一直处于等待状态，并非流水线模型，有很多轮训等待开销。&lt;/p&gt;</description></item></channel></rss>