zizi bb7c2baf7b docs(arch-atlas): 完整性补图16张——新人据图通读全系统
9 opus 子代理新人视角审查→补 16 图(P0+P1+P2),目标=读图即懂整个系统:
- 00: 端到端跨域全链泳道(8泳道15步+钱流并联,旗舰)+ 系统上下文外部依赖边界(C4 L1)
- 01 B端客户旅程 / 02 逻辑→单体物理拓扑+契约跨域接缝+模块状态热力图 / 03 postMessage信封数据模型
- 04 核心对象生命周期状态机+两套身份 / 05 对话式创作闭环+生成任务UI闭环+feed交互
- 06 运营状态机合集+IP授权锁风来源+短信vs邀请码 / 07 端到端trace贯穿
5 新 SVG 经脚本核(良构/零溢出/脚注/转义)+ 11 Mermaid 配平;8 篇纯追加(既有图零改)。
子代理忠于源码/设计档纠偏:B端P-BIZ编号/feed第四互动=举报/BIZ五态按设计档真态/契约命名漂移诚实标。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:12:15 +00:00

30 KiB
Raw Blame History

date, topic, status
date topic status
2026-06-22 运维图说——绘境AI 架构图集按领域拆分·07 现行为主 · 每图具体映射源档见该图脚注 / 内联小注 · 通用图例见 00-系统总览图说

07 · 运维图说

本篇是绘境AI 架构图集的一部分(全 8 篇:00 系统总览 + 0107 各域)。通用图例、状态码、按角色读路径见 00 · 系统总览图说本域命门(最该据图 review 的设计缝):观测 = 待接、k3s = 缓做,别画成现行。


本域讲什么故事:运维域回答三件事——这套系统部署在哪些机器上、代码怎么从一行 commit 变成在运行的服务、靠什么确认它还活着并且活得够好。这份图说把这三件事,连同两件「设计已出但还没接上」的事(线上观测体系、k3s 迁移),用一套图加配套讲解画清楚。判断「这套运维方案是不是想要的那个」看这份图就够建立全局轮廓,逐字节的命令仍去 deploy/ 脚本与 .agents/skills/staging-ops.md

最该据图 review 的命门缝:运维域的特殊之处是它同时承载「现行已建」「设计已出待接线」「收窄后缓做」三类内容,绝不能把后两类画成已建。图 6(观测)整体是(待接线)、图 8(k3s)整体是(缓做、排在 W-G1 之后),其余六张是。每张图内部还会用实心块 / 虚线框 + 「待接线」「缓做」小标把单个元素的状态再标一层。

本域阅读约定与同步纪律:本图说不另立设计,只把运维域三份 canonical 设计档画出来——部署链与四机分工出自 运维/README.md,线上观测体系出自 观测体系.md,k3s 迁移出自 k8s迁移.md。frontmatter 记了这三份的当前 commit hash 作为防漂移门。设计一变动,本图说与对应 SVG 必须同步更新。三种状态贯穿全图,必须看清:运维域同时承载「现行已建」「设计已出待接线」「收窄后缓做」三类内容,绝不能把后两类画成已建。

本域徽章(主要关心其中四个):可靠(#16a34a):隔离验证安全变体、备份 JAR 一键回滚、冒烟门退出码可卡口、观测旁路降级、k8s 共存保护与 rollout undo。可观测(#0ea5e9):OTel 统一采集、Grafana 三联下钻、夜莺告警必达——这是图 6 的主轴。安全(#dc2626):push 走 Gitea 中转(scp/rsync 被分类器拦)、Collector 集中脱敏、admin 观测入口权限守门不裸暴露后端。成本(#16a34a):内网物理机 + 人工部署而非重 CI、不硬凑第二台机器上多节点、缓存命中率哨兵——都是 < ¥5,000/月成本盒子的取舍。(另两个维度 数据 #475569伸缩 #7c3aed 在 k3s 图上点到。)运维域图说里,虚线还额外承担一层状态语义:凡是「待接线」(观测)或「缓做」(k3s)的元素,一律用虚线框 + 文字小标画出,与「现行已建」的实心块在视觉上一眼区分开。这是本域最该守的纪律点——观测体系和 k3s 迁移都是「设计已出、代码未接」,绝不能因为画得好看就让人误以为已经建好了。

运维图 1 · 四机分工拓扑〔引·现〕

这张图回答「服务跑在哪里」。绘境AI 在内网阶段刻意不上云托管、不上 Kubernetes,而是用一组通过 Tailscale 连起来的物理机,按角色硬分工:lili-mac 是创作者本地工作站,跑开发主会话与快构建,但它会休眠合盖,所以不进 Tailscale 调度、也不承担任何「门」;mini-desktop 是权威验收机,staging 全栈、重型构建、浏览器端到端测试都在这里,且与生产同构;mini-infra 是共享基建机,Gitea / PostgreSQL / Redis / MinIO / new-api 都在这台,它绝不跑项目 app 或重型构建;6c6g 是常驻无人值守机,跑常开的编排、定时任务、git 操作,但禁跑重活(重型前端构建会 OOM)。

flowchart TB
  subgraph dev["开发侧(不进调度)"]
    MAC["lili-mac · ARM<br/>主会话 / 本地 dev / 快构建<br/>(会休眠 · 不出镜像与门)"]
  end
  AGIT["Aliyun Gitea · 101.200.34.71<br/>:2222 SSH push(默认远程)<br/>:3000 HTTP 匿名读"]
  subgraph tnet["Tailscale 内网"]
    direction TB
    subgraph desk["mini-desktop · 100.64.0.7 · x86(权威验收 · 与生产同构)"]
      BE["huijing 后端单体<br/>:48080"]
      ST["game-studio 产品端<br/>:4173"]
      AD["game-admin 管理后台<br/>:4174"]
      DBL["隔离 MySQL / Redis<br/>(仅 staging)"]
    end
    subgraph infra["mini-infra · 100.64.0.8(共享基建 · 不跑 app)"]
      GIT["Gitea 代码仓<br/>(自建底座 · 非默认 push 目标)"]
      SQL["PostgreSQL / Redis"]
      OSS["MinIO 对象存储"]
      NAPI["new-api 模型网关"]
    end
    SIX["6c6g(常驻无人值守)<br/>编排 / cron / 批跑 / git<br/>(禁跑重活)"]
  end

  MAC -- "git push(默认远程)" --> AGIT
  AGIT -- "HTTP :3000 匿名 clone/pull" --> desk
  BE -- "出网调模型" --> NAPI
  BE --> SQL
  BE --> OSS
  SIX -- "调度 / 批跑" --> desk

(图源:运维/README.md §1.1)

读这张图要抓住一条最硬的边界:Mac 是 ARM 架构,产出的 JAR 与前端产物架构中立可用,但 Docker 镜像与冒烟门必须在 x86 的 mini-desktop 出,否则镜像架构与生产不同构,等于半假的门。这条边界在后面图 8(k3s 迁移)里会再次出现并延伸——到了 k8s 时代,「进集群的镜像必须 mini-desktop x86 出」依然成立。这张图没有现行/远期的灰度,它就是当前真相;唯一的「设计缝」其实在别处——README §1.1 自己点明 mini-infra 上跑的关系型库是 PostgreSQL,而 mini-desktop 上是隔离的 staging MySQL,两者不是一回事,端点口径以凭据 SoT《内网凭据与端点.md》为准,别混。

运维图 2 · 部署链:push → pull → 重建 → 验门SVG新·时序·现

运维图 2 部署链 push→pull→重建→验门

这张时序图回答「代码怎么变成在跑的服务」。它要先破除一个误解:开发团队版文档里那套「push 触发 CI、自动构建镜像、滚动更新」是目标态蓝图,与现实系统性脱节(那份文档顶部自带审计横幅说明这点)。现行的部署没有 docker-compose、也没有自建 CI 流水线,而是人工经标准序在 mini-desktop 上完成——图里把它画成四条泳道之间的一串动作,并在底部用一个虚线框单独把这套蓝图标成「现实未建·勿当现状」,免得有人照着蓝图去找不存在的 CI。

核心三步在图上从左到右展开。第一步同步代码:代码经 git push 推到 Aliyun Gitea(默认远程,SSH :2222),mini-desktop 再从同一个 Gitea 以匿名 HTTP :3000 拉取(注意是 HTTP 只读端口、不是 SSH)。这里画出了两条已经踩过的坑:直接 scp / rsync 源码树会被分类器拦下,所以必须走 Gitea 中转;大资产 push 在途时再发 push 会撞 Gitea 的 ref 锁,所以同仓 push 要串行。第二步重建:在 mini-desktop 上 git reset --hard 到目标分支、mvn clean install 出新的 fat JAR,收口前以字节码实证新 JAR 确实含本次改动(曾因「构建源 ≠ 运行的 JAR」翻过车,图上单画一格强调)。第三步验门:重启后验 health 200、四模板就绪、Flyway up-to-date,再跑冒烟门(即图 4)。

整张图最该让人看出的,是中间那个橙色虚线框——隔离验证安全变体。高风险或整机变更不直接动 live,而是先用新 JAR 在隔离端口 :48090 起一个实例验全,live 的 :48080 全程不动,验全过才切;任一步失败就用 /tmp 里的备份 JAR 把 :48080 恢复回去。这套「验全才切、失败即回」的安全变体是运维域可靠性的支点,后面图 8 把它原样平移到了 k8s 的隔离端口切换。图底还有一条红线值得记住:API 全绿 ≠ UI 通,编排器旁路会掩盖 UI 缺陷,用户可见波次收口前必须另做一次真浏览器 UI 走查。

运维图 3 · 隔离验证安全变体Mer新·流程·现

这张图把图 2 里那个橙框单独放大,讲清「为什么验全才切、失败怎么退」。它是一条状态/分支流程:新 JAR 不直接顶替 live,而是先在隔离端口 :48090 起一个实例,跑完整验证(health、四模板、Flyway、冒烟门 12 项);只有全部通过才停旧实例、释放端口、把新实例切到 :48080;任一步失败,立即用 /tmp 备份 JAR 把 :48080 恢复,回到改动前的形态,全程 live 不受影响。这条流程让一次高风险发版的下行风险被牢牢兜住——最坏情况也只是「白验一次、live 没动」,而不是「切了一半、服务挂了、退不回去」。

flowchart TB
  START["新 JAR 已构建<br/>(字节码实证含本次改动)"] --> ISO["在隔离端口 :48090<br/>起一个新实例"]
  LIVE["live 后端 :48080<br/>全程不动 · 继续服务"]:::live
  ISO --> V{"在 :48090 验全?<br/>health 200 · 四模板就绪<br/>Flyway up-to-date · 冒烟门 12 项"}
  V -- "全过" --> SWITCH["停旧实例 → 释放端口<br/>把新实例切到 :48080"]
  V -- "任一失败" --> ROLLBACK["/tmp 备份 JAR<br/>把 :48080 恢复回去<br/>(5 分钟内 · live 本就没动)"]:::danger
  SWITCH --> REVERIFY["切后复验冒烟门<br/>+ 真 UI 走查"]
  SWITCH -.->|稳定观察一两天后| CLEAN["再删 /tmp 备份 JAR"]
  ROLLBACK --> BACK["回到改动前形态<br/>排查后重来"]

  classDef live fill:#eff6ff,stroke:#2563eb,stroke-width:2px;
  classDef danger fill:#fee2e2,stroke:#dc2626,stroke-width:2px;

这张图没有现行/远期边界,它就是现行高风险发版的标准动作;它和图 2 的关系是「图 2 给全链路、图 3 把其中最关键的安全机制放大」。要注意的设计缝只有一个:这套变体是人工经标准序执行的,不是声明式的——这正是图 8(k3s)想用 kubectl rollout undo 改进的地方,但在 k8s 落地前、乃至落地后的过渡期,这条人工兜底路始终保留。

运维图 4 · 冒烟门 12(+1)项SVG新·流程·现

运维图 4 冒烟门 12+1 项

这张图回答「靠什么确认它健康」。每次 staging 部署之后跑一遍 deploy/smoke-test.sh——它是「部署后是否健康」的就绪检查(秒级、以读为主、可反复跑),逐项 PASS / FAIL,末尾给总判与退出码(0 = 全过,1 = 有 FAIL),所以能直接挂到部署脚本里当卡口。图上把 12 个默认项按「基础设施 / 鉴权 + 五条端到端闭环链路」分组画出来,让人一眼看出冒烟门验的不是零散接口,而是五条业务链的关键 API 是否都在岗:① 创作→生成→预览、② 发布→审核→游戏流、③ 试玩→互动→分享、④ 广告→收益→钱包、⑤ 数据回路(遥测→质量分→feed 重排)。

读这张图要抓三个细节。其一,12 项里唯一的写操作是建一条无副作用的草稿(POST /app-api/studio/draft,status 0、不发布、无下游副作用),用于验证创作入口 + 鉴权 + gameId 分配在岗;其余全是读。其二,--deep 才加的第 13 项(图上用橙色虚线框单独标「按需」)走真实写路径,触发一次生成入队 + 遥测落库,默认不跑、只验入队 200/code=0,不等生成完成。其三,图上用红框钉死了一个反复出现的坑:feed 列表项的 packageUrl 字段恒为 null,真实取游戏包要走 GET /app-api/runtime/package/{versionId},别拿 packageUrl 当取包入口。

这张图的「设计缝」画在右下角那个虚线框里:冒烟门是「部署那一刻的就绪点检」,它不是深度 e2e(深度 e2e 由 agent-loop 编排器批跑,职责不重叠),也还没有常态监控来补它的另一半——这正好衔接到图 6 的观测体系。衔接点写在图上:冒烟门那 12 项的结果可以打成指标推给 Prometheus,让「每次部署的健康度」也进可观测历史,而不是只在部署当时的终端里闪一下。

运维图 5 · 可用性 SLO / Error BudgetMer新·讲解·现

这张图回答「健康到什么程度才算达标」。运维域的硬指标是服务可用性 ≥ 99.5%,与之配套的是 MVP 基础设施成本控制在 < ¥5,000/月——这两个数字是运维域所有取舍(不上云托管、单体而非微服务、人工部署而非重 CI)的约束来源。图把可用性目标拆成三档 SLO(来自 security-and-reliability.md §5.1:游戏流 99.5%/月 ≈ error budget 3.6 小时、AI 生成 99%/月 ≈ 7.2 小时、支付 99.9%/月 ≈ 43 分钟),并点明这些目标怎么落进成本盒子。

flowchart TB
  GOAL["可用性目标 ≥ 99.5%(MVP)<br/> 基础设施成本 ¥5,000/月以内"]:::goal
  subgraph SLO["三档 SLO + Error Budget(security-and-reliability §5.1)"]
    direction LR
    S1["游戏流<br/>99.5% / 月<br/>容错预算 ≈ 3.6 小时"]
    S2["AI 生成<br/>99% / 月<br/>容错预算 ≈ 7.2 小时"]
    S3["支付<br/>99.9% / 月<br/>容错预算 ≈ 43 分钟"]
  end
  subgraph COST["成本盒子怎么撑住目标"]
    direction LR
    C1["内网物理机<br/>不上云托管"]
    C2["单体<br/>而非微服务"]
    C3["人工部署<br/>而非重 CI"]
  end
  GOAL --> SLO
  GOAL --> COST
  SLO -.->|"现状:只有一个目标数字,<br/>没有东西在持续度量它"| GAP["缺口:error budget 无人消耗与记录<br/>→ 由观测体系落地(图 6)<br/>用 health 成功率算实际可用性 + burn rate 看板"]:::gap

  classDef goal fill:#f0fdf4,stroke:#16a34a,stroke-width:2px;
  classDef gap fill:#fff7ed,stroke:#d97706,stroke-width:2px,stroke-dasharray:5 4;

这张图最该让人看出的是那条虚线指向的缺口:≥99.5% 现在是一个目标数字,但没有任何东西在持续度量它、没有 error budget 在被消耗和记录。换句话说,达标线画出来了,举证手段还没建。这个缺口的归宿就是图 6 的观测体系——用 health 探测的成功率算游戏流 API 的实际可用性,对着三档 SLO 做 burn rate 看板和告警,让「达没达标」从一句承诺变成一张有数据、有容错预算余量的看板。所以图 5 和图 6 是一对:图 5 立目标、点缺口,图 6 给落地手段。

运维图 6 · 观测体系:OTel → Grafana / 夜莺SVG新·架构·接(设计已出·待接线)

运维图 6 观测体系 OTel→Grafana/夜莺

这张图回答「线上靠什么持续观测」——也是本域最该验状态纪律的一张。它整体是(待接线):创始人 2026-06-21 定了栈(OTel 统一采集 + Prometheus + Grafana 主看板 + 夜莺主告警),正式设计稿已出,但代码大半没接、没后端。所以图上做了一件最要紧的事:把「现在已经有什么」和「还得新建什么」用实心块 / 虚线框严格分开,绝不让人误以为观测已经建好。

图按「采集 → 管道 → 存储 → 看板/告警」一条链画,逐段标状态。唯一已就位的实心块在图正下方高亮:aigc-server 已引入 spring-ai-alibaba-starter-graph-observation,SAA 裸图每个节点已发 spring.ai.alibaba.graph.node.<id> 的 Micrometer observation(带 trace 和耗时,失败反映在指标里)——这是后端唯一一段已经在以标准产出、且依赖已就位的观测信号。但图上同样标清楚:它缺一个把它收走的后端、缺一个非 NOOP 的 ObservationRegistry(要 actuator/micrometer 装配在席)。其余全是虚线框 + 「待接线」:OTel Java Agent(自动 span,monitor starter 当前根本没进 aigc 部署单元)、五个生成业务指标(gen_task_total / gen_duration_seconds / gen_queue_depth / gen_gate_fail_total / llm_cost,目前在代码里全部零命中,要补 MeterRegistry 注册才有数据,其中成本指标还额外依赖一个尚未落地的 new-api logs.quota 采集薄片)、OTel Collector、Prometheus / trace 后端 / 日志后端(trace 倾向 Tempo、日志倾向 Loki,但都标「选型待定」)、Grafana、夜莺、通知通道、admin 观测入口。

读这张图要抓住三层设计意图。其一,为什么 Collector 居中而不是各组件直连后端:让每个进程直接推各自后端会把地址、协议、脱敏逻辑硬编码进每个应用,上 k8s 或换后端就得改一圈;中间放一个 Collector 当统一入口,脱敏(手机号/token)、批处理、采样、路由全在这一层集中做,换后端只改 Collector 导出配置、应用零改动——这正是「先单体可跑、后平滑上 k8s」能成立的技术支点。其二,Grafana 和夜莺不是冗余而是分工:Grafana 负责「你主动去看时看得清」(三联下钻),夜莺负责「你没在看时它把你叫来」(把 P0P3 升级链变成真规则真通道,补上「告警通道一根没接」这个最大的空);创始人点名的三类关键告警都在图上有家——可用性走 P0、错误率(5xx>2%)走 P1、生成失败率(<70%)走 P1。其三,图底那条红线是观测体系的底线:埋点开关默认关、经 profile 显式开,agent 异步批量上报、Collector 不可达时本地丢弃,硬验收是「停掉 Collector,主链路与各 API 行为不变、冒烟门仍全绿」——观测栈本身挂掉,绝不能影响被观测的服务。

除图上画出的「trace/日志后端选型、admin 嵌入方式与 SSO(详见图 7)」这几条待核外,源档《观测体系.md》§6 还留了两条待创始人拍板的口径,读图时一并记住、别当已定:一是观测数据保留期与成本——trace 与 metrics 存多久直接吃 mini-desktop 的磁盘,要对一下账「预算里留的那笔监控冗余够不够这套栈的存储」;二是生成成功率告警阈值——现在 P1 写的是「成功率 < 70%」(已经很糟该紧急处理的线),而验收线是 ≥80%,是否要在 70% 之外再加一条 80% 的趋势预警,让成功率从 80% 往下掉时就先有提醒、而不是等到 70% 才报,也待拍。

运维图 7 · admin 观测入口Mer新·流程·接(待接线)

这张图把图 6 右侧「admin 观测入口」那一块放大,讲清运营怎么从后台一键进监控大盘。它整体也是——而且这块的现状是,要整套新建:game-admin/src 下没有任何观测相关的路由、菜单或组件,后端也没有任何给 admin 用的观测查询接口。所以「控制台一键进盘」不是接一根线,而是要补齐前端菜单/路由/组件、后端管理员专属查询接口、嵌入安全处理三件,缺一不可。图用虚线把这三件标成「待接线」,并画出 admin 进盘的链路与那个必须解决的体验问题:别让运营进个监控还要再登一次 Grafana

flowchart LR
  OPS["运营<br/>(system_users 已登录 admin)"] --> ADMIN["game-admin 控制台<br/>『观测』菜单(待建)"]:::todo
  ADMIN -->|"观测大图:嵌入只读大盘"| EMBED["Grafana 嵌入面板<br/>可用性/错误率/生成成功率"]:::todo
  ADMIN -->|"深挖跳转"| PROXY["admin 后端 auth-proxy<br/>透传 system_users 身份头(待建)"]:::todo
  PROXY -->|"可信头免登(SSO)"| GRAF["Grafana 完整大盘<br/>trace/metrics/log 三联下钻"]
  ADMIN -.->|"告警概览 API"| N9E["夜莺告警列表<br/>最近告警 / 值班状态"]
  EMBED --> GRAF

  classDef todo fill:#fff7ed,stroke:#d97706,stroke-width:1.5px,stroke-dasharray:5 4;

这张图要让人看出两处尚未拍板的设计缝,都还待核,不能当成已定。其一,嵌入方式:iframe 嵌 Grafana 不是写个 <iframe src> 就完——Grafana 默认带 X-Frame-Options: deny 和自身 CSP,浏览器会直接拒绝被嵌入,要让嵌入成立得开 grafana.iniallow_embedding 并放 CSP frame-ancestors;另一条路是不嵌 iframe、改由 admin 后端查询接口取数、前端自渲核心曲线,绕开嵌入安全开关但要自己画图。其二,SSO 免登:倾向 auth-proxy 透传(admin 后端把已登录的 system_users 身份经可信请求头透给 Grafana),体验最好,但要核 Grafana auth proxy 在 iframe 场景下的 cookie/同源限制,以及 admin 代理层的安全边界——不能让未授权 admin 用户读到观测数据,更不能把 Grafana / Prometheus 裸暴露出去。这两处待核是这块落地前必须先定的,图上据实标出、不臆造结论。

运维图 8 · k3s 单节点迁移(收窄 + 缓做)SVG新·架构·缓(缓做·排 W-G1 之后)

运维图 8 k3s 单节点迁移

这张图回答「部署形态往哪走」——也是本域第二个最该验状态纪律的图。它整体是:创始人 2026-06-22 拍板「范围收窄、时机缓做」,所以图上整张是目标态、本阶段不做,用紫色虚线框 + 「缓做」标清楚,绝不能画成现行已建。范围收窄为单节点 k3s 跑在 mini-desktop 上、只迁 staging 应用与观测栈、数据库不进集群、多节点 / 高可用 / prod 全部推迟;时机缓做、排在 W-G1(生成质量到 80%)告一段落之后(其中阶段 0 容器化零 k8s 风险、是上云前提,可先行)。

图分三块。左上是目标拓扑(紫虚线):mini-desktop 单节点 k3s(control-plane 与 worker 同机),里面是后端单体 Deployment、studio/admin Deployment、observability namespace 的三件观测 workload;旁边两个实心块是迁移期全程保留的东西——隔离 MySQL/Redis 裸容器(标「不进集群」,因为有状态迁移是另一个量级的风险)和「裸 JAR + /tmp 备份」逃生口(用红框单画,强调任一阶段、任一时刻 kubectl 出问题就 5 分钟内退回今天形态)。右侧三块是 mini-infra(共享基建,不进集群、不装 kubelet,集群经 ExternalName Service 走 Tailscale IP 访问它)、6c6g(不当节点、不跑 workload,只当 kubectl 控制端)、lili-mac(镜像必须 x86 出,与图 1 的边界一脉相承)。底部是四阶段迁移路:阶段 0 容器化(绿色实心、可先行)→ 阶段 1 装 k3s(空集群、live 仍裸跑)→ 阶段 2 后端上集群(流量切换在此、风险最高)→ 阶段 3 前端 + 观测栈上集群,每阶段都标了独立验收门与回滚手法。

读这张图,最要让人看清的是「买到什么、买不到什么」那一栏:本阶段上 k8s 买到的是声明式部署 + rollout undo 一键回滚 + requests/limits 共存保护 + 观测栈编排;买不到的是高可用——单节点 k3s 一旦节点挂了,集群和所有 workload 一起没,这和今天裸 JAR 挂了没本质区别,反而多背了一整套 k8s 控制平面的复杂度。如果创始人的预期是「上了 k8s 就更稳了」,这里要先校准:高可用要等多节点,而多节点要等有第二台有内存余量的 x86 机器(现在没有)。技术细节上还有两个被图钉住的关键决定:后端走 hostNetwork: true(一举两得——既直占宿主 48080 让对外端口零变化,又能连只 bind 在 127.0.0.1 的库,hostAliases 和 host-gateway 都做不到);隔离期 Pod 用 server.port=48092 起(避开正被 live 裸 JAR 占着的 48080),这正是图 2/图 3 的隔离验证安全变体平移到了 k8s。

运维图 9 · 一句话生成游戏 · 端到端 trace 贯穿Mer新·时序·接(待接线)

这张图回答一个最具体的问题:观测体系做好了到底能干嘛。前面图 6 画的是观测栈的零件怎么搭、图 5 点的是「达标线画出来了但没人在度量」,而这张图把那套栈兑现成一个看得见摸得着的能力——一个 trace_id 把「一句话生成游戏」这条最值钱的链,从用户在前端点下「生成」那一刻,一路串到便宜模型返回、九门放行、产物编译完成,中途经过的每一跳都挂在同一棵 trace 树上。这正是源档《观测体系.md》§5.1 列的头号验收判据:发起一次生成,在 Grafana 里用同一个 trace_id 就能查到「网关入口 → SAA 各节点 → new-api 调用」的完整链路,而且这个 trace_id 恰好等于响应头回写的那个 trace-id——前端报错时把响应头里的 trace-id 抄下来,运营贴进 Grafana 就能直接定位这次生成卡在哪个节点、哪道门、调模型报了什么。

整张图的状态是(待接线):它画的是观测做好之后的目标能力,而不是今天已经能做到的事。所以图上用一条贯穿的虚线把「这条 trace 链」整体标成待接线,中途凡是要新建埋点才有数据的节点(自动 span、九门指标、终态指标、成本/积压)一律虚线框 + 「待接」小标;唯一一个实心绿块是 SAA 各节点的 observation——它是后端目前唯一一段依赖已就位、已在以 Micrometer 标准产出的观测信号(详见图 6 正下方那条高亮),其余全靠这条链补齐才能点亮。

flowchart LR
  U["前端点击「生成游戏」<br/>game-studio"]:::todo
  U -->|"注入 traceparent(W3C)"| GW

  subgraph BE["huijing 后端单体(同一棵 trace 树)"]
    direction LR
    GW["网关入口 · TraceFilter<br/>起 root span<br/>响应头回写 trace-id"]:::todo
    AIGC["aigc 服务<br/>建 game_aigc_task"]:::todo
    subgraph SAA["SAA 裸图编排 · 每节点一条 observation span"]
      direction LR
      NB["node: brief<br/>需求拆解"]:::done
      NG["node: gen_source<br/>调 new-api(出网 span)"]:::done
      NN["node: nine_gates<br/>九门校验"]:::done
      NBD["node: build<br/>build-from-source"]:::done
    end
    GW --> AIGC --> SAA
    NB --> NG --> NN --> NBD
  end

  NG -->|"OTel agent 自动 span"| NAPI["new-api 网关<br/>→ 便宜 LLM(M3/DS-V4)"]:::todo

  AIGC -. "终态回填 status<br/>gen_task_total{status}" .-> M
  NN -. "gen_gate_fail_total{gate}" .-> M
  SAA -. "gen_duration_seconds / gen_queue_depth / llm_cost" .-> M
  M[("指标平面<br/>成功率 / 耗时 / 积压 / 成本")]:::metric

  GW -. "同一 trace_id 关联" .-> M
  M -. "trace ↔ metrics 同 id" .-> T[("trace 平面<br/>= 响应头 trace-id")]:::tracehl

  classDef done fill:#dcfce7,stroke:#16a34a,stroke-width:2px;
  classDef todo fill:#fff7ed,stroke:#d97706,stroke-width:1.5px,stroke-dasharray:5 4;
  classDef metric fill:#1e293b,color:#fff,stroke:#475569,stroke-width:1.5px;
  classDef tracehl fill:#0c4a6e,color:#fff,stroke:#0ea5e9,stroke-width:2px;

读这张图要抓住三层意思。其一,实线链是「trace 怎么连成一根」,虚线指向是「metrics 怎么和这根 trace 同 id 挂钩」:一次生成在 trace 平面是一条完整时间轴(每个 SAA 节点一个 span、调 new-api 那一跳由 OTel agent 自动补 span),同时几个关键节点把业务指标(终态算成功率、九门算 gen_gate_fail、耗时/积压/成本)打到指标平面,两个平面经同一个 trace_id 对得上——所以既能看「这一次生成卡哪了」(trace 下钻),又能看「最近一千次生成的成功率趋势」(metrics 看板),这是图 6 那套栈接好后最直接的回报。其二,绿块与虚线框的对比就是本图的状态纪律:别因为这张图画得顺就以为链路已经通了——除了 SAA 节点 observation 这一段地基真在,前端 traceparent 注入、网关 root span 与 trace-id 对齐、自动 span、那几个业务指标的埋点,在代码里全是待新建(图 6 散文已逐件点过,五个生成指标当前零命中)。其三,这张图同时钉死了一条最该据图 review 的设计缝:响应头那个 trace-id 现在取自 SkyWalking 的 TracerUtils,而 OTel agent 走的是 W3C traceparent 自成一套——两者不打通,前端抄回来的 trace-id 就和 Grafana 里 OTel trace 的 id 对不上号、查不到链路。源档《观测体系.md》§6 把「trace context 头怎么对齐成一根」列为第一期落地就要先打通的头号待核,这张图把那条缝画在「网关 root span ↔ 响应头 trace-id ↔ 指标/ trace 同 id」这条线上;它一旦没对齐,整张图承诺的「一个 id 贯穿到底」就断在前后端边界,所以它既是观测体系的头号验收判据,也是落地时第一个要验的点。

运维域图清单与状态表

# 图名 家族 形式 状态 覆盖内容
1 四机分工拓扑 引(内联 README §1.1) lili-mac / mini-desktop / mini-infra / 6c6g 角色边界 + Gitea 中转 + ARM/x86 镜像门铁律
2 部署链 push→pull→重建→验门 SVG新 Gitea 中转 + push 串行 + 字节码实证 + 隔离验证安全变体 + 真 UI 走查红线 + 目标态蓝图对照
3 隔离验证安全变体 Mer新(内联) 新 JAR 在 :48090 验全才切、live :48080 全程不动、失败用 /tmp 备份回滚
4 冒烟门 12(+1)项 SVG新 基础设施/鉴权 2 项 + 五链路 10 项 + --deep 写路径 1 项 + 退出码卡口 + packageUrl 恒 null 坑
5 可用性 SLO / Error Budget Mer新(内联) ≥99.5% + 成本 <¥5,000/月 约束来源 + 三档 SLO + 「无人持续度量」缺口指向图 6
6 观测体系 OTel→Grafana/夜莺 SVG新 采集→管道→存储→看板/告警全链;唯一已就位=SAA 节点 observation,其余待接线;Collector 居中/双看板分工/旁路铁律
7 admin 观测入口 Mer新(内联) 控制台一键进盘链路 + SSO 免登 + 嵌入方式/SSO 两处待核;现状=零、整套新建
8 k3s 单节点迁移 SVG新 单节点 k3s + staging 应用 + 观测栈,库不进集群;四阶段迁移路;裸 JAR 兜底全程保留;买到/买不到边界
9 一句话生成游戏 · 端到端 trace 贯穿 Mer新(内联) 一个 trace_id 串前端点击→网关→aigc→SAA 各节点 observation→new-api→九门→build;trace 与 metrics 同 id 关联、= 响应头 trace-id;唯一已就位=SAA 节点 observation,余待接线;= 观测体系头号验收判据

状态分布:现 ×6、接 ×3(图 6/7/9,观测体系待接线)、缓 ×1(图 8,k3s 收窄缓做)。防漂移门:本文 frontmatter 记运维域三份源档的 commit hash(README/k8s迁移 @ 15b707fd、观测体系 @ 3e71715a),源档变更即比对。