官方 check-deadlinks 抓出 验收门-W-G1→验收门 改名后子树外 3 处死链,修复: agent-specs/_index、architecture/README、运维/观测体系。 门结果:全仓死链=0 · 品牌「造梦AI」零命中 · canonical 4 topic 各一份 · 入口卫生(SoT 活档无个人绝对路径/硬编码端口)零命中。 整理 scaffold plan 自退役(内容已落 SoT①/003/memory)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
运维域 · 设计主文档
这是什么:绘境AI 运维域的设计主文档,回答"这套系统部署在哪些机器上、怎么从代码变成在运行的服务、靠什么确认它还活着"。它衔接在架构域(系统怎么搭)之后,讲的是"搭好之后怎么让它跑起来、并且持续跑得住"。 给谁看:负责部署与值守的工程师、做发版的人、排查线上故障的人,以及关心可用性与运维成本的创始人。 怎么读:本页只画边界——环境长什么样、一次部署经过哪几步、用什么门确认健康、可用性目标是多少。真正逐字节、逐命令的操作手册不在这里(原因见下文第 2 节),按文末指针去取。 读这一页 = 运维域的当前真相骨架。具体命令、踩坑红线、机器口令都在指向的脚本与 playbook 里,本页不重复。
1. 边界:环境、部署、冒烟、可用性目标
运维域要回答四件事:服务跑在哪里、代码怎么变成运行中的服务、怎么知道它健康、以及健康到什么程度才算达标。逐一说清。
1.1 跑在哪里:四台机器各司其职
绘境AI 在内网阶段不用云托管、不用 Kubernetes,而是用一组通过 Tailscale(一种把分散机器组进同一虚拟内网的工具)连起来的物理机。它们按角色分工,边界很硬:
- lili-mac(创作者本地工作站):跑开发主会话、本地全栈 dev、快速构建迭代。它会休眠合盖,所以不进 Tailscale 调度,也不承担任何"门"。一条关键边界:Mac 是 ARM 架构,产出的 JAR 与前端产物架构中立可用,但 Docker 镜像与冒烟门必须在 x86 的 mini-desktop 出,否则镜像架构与生产不同构,等于半假的门。
- mini-desktop(100.64.0.7):权威验收机——staging 全栈、重型构建、浏览器端到端测试都在这里,且与生产同构。它跑着 huijing 后端单体(
:48080)、产品端 game-studio(:4173)、管理后台 game-admin(:4174),外加一套隔离的 MySQL / Redis 容器(只供 staging 用,不与共享基建混)。 - mini-infra(100.64.0.8):共享基建——Gitea(自建 Git 仓)、PostgreSQL、Redis、MinIO(对象存储)、new-api(模型网关,生成主线经它直连便宜大模型)都在这台。它绝不跑项目 app 或重型构建,只做底座。(注意:这台跑的关系型库是 PostgreSQL,与 §1.1 上一条 mini-desktop 上隔离的 staging MySQL 不是一回事;端点口径以凭据 SoT《内网凭据与端点.md》为准。)
- 6c6g:常驻无人值守机——常开的 agent 编排、定时任务、后台批跑、git 操作落在这里(因为 Mac 会休眠,常驻活只能交给它)。它禁跑重活(重型前端构建会 OOM)。
注(三个名词): "单体"指后端 13 个业务模块编译进一个 Spring Boot 进程一起启动(不是微服务那样各自独立部署);"staging"指上线前的预演环境,与生产同构、供验收;"生产同构"指机器的 OS / JDK / 浏览器版本与正式环境一致,这样验收结果才可信。
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
1.2 怎么部署:git push → pull → 重建 → 验门
内网阶段没有 docker-compose、也没有自建 CI 流水线——开发团队版文档里那套"push 触发 CI、自动构建镜像、滚动更新"是目标态蓝图,与现实已系统性脱节(那份文档顶部自带审计横幅说明这点)。现行的部署是人工经标准序在 mini-desktop 上完成的,核心三步:
- 同步代码:代码经
git push推到 Aliyun Gitea(101.200.34.71,默认远程,SSH:2222),mini-desktop 再从同一 Aliyun Gitea 以匿名 HTTP:3000clone / pull拉取(注意是 HTTP 只读端口,不是 SSH)。mini-infra 上另有一份自建 Gitea 作底座,但不是默认推送 / 拉取目标——端点口径以凭据 SoT《内网凭据与端点.md》与 staging-ops playbook 为准。这里有两条已踩过的坑:直接scp / rsync源码树会被分类器拦下,所以必须走 Gitea 中转;大资产 push 在途时再发 push 会撞 Gitea 的 ref 锁,所以同仓 push 要串行,等上一笔落地再发下一笔。 - 重建:在 mini-desktop 上
git reset --hard到目标分支,再mvn clean install出新的 fat JAR。收口前以字节码实证新 JAR 确实含本次改动(曾因"构建源 ≠ 运行的 JAR"翻过车)。高风险或整机变更走隔离验证安全变体:先用新 JAR 在隔离端口(如:48090)起一个实例验全,live 的:48080全程不动,验全过才切;任一步失败就用/tmp里的备份 JAR 把:48080恢复回去。 - 验门:重启后先验后端
health返回 200、四个游戏模板就绪、Flyway(数据库迁移工具)已 up-to-date,再跑冒烟门(下一节)。
1.3 怎么知道它健康:冒烟门
每次 staging 部署之后,跑一遍 deploy/smoke-test.sh——这是"部署后是否健康"的就绪检查(秒级,以读为主、可反复跑),不是深度端到端测试(深度 e2e 由 agent-loop 编排器批跑,职责不重叠)。它逐项 PASS / FAIL,末尾给总判与退出码(0 = 全过,1 = 有 FAIL),因此可以直接挂到部署脚本里当卡口:
- 默认以读为主 12 项(唯一一笔写=建一条无副作用草稿
POST /app-api/studio/draft,status 0、不发布、无下游副作用,用于验创作入口 + 鉴权 + gameId 分配在岗):覆盖基础设施 + 鉴权,以及五条端到端闭环链路的关键 API——①创作→生成→预览、②发布→审核→游戏流、③试玩→互动→分享、④广告→收益→钱包、⑤数据回路(遥测→质量分→feed 重排)。它验的是这五条链的关键 API 在岗、鉴权 / CORS(跨域放行)/ 质量分回灌的结构正常。 --deep加 1 项(共 13):走真实写路径,触发一次生成入队 + 遥测事件落库,按需开启。- 一个反复出现的坑被写进了脚本注释:feed 列表项的
packageUrl字段恒为 null,真实取游戏包要走GET /app-api/runtime/package/{versionId}——别拿packageUrl当取包入口。 - 还有一条红线:API 全绿 ≠ UI 通。编排器旁路会掩盖 UI 缺陷,所以用户可见的波次收口前,必须另做一次真 UI 走查。
1.4 健康到什么程度才算达标:可用性目标
运维域的硬指标是服务可用性 ≥ 99.5%(MVP 阶段目标)。与之配套的是 MVP 基础设施成本控制在 < ¥5,000/月(投资人版约 ¥4,300/月,约 ¥50k/年)——内网物理机 + 共享基建的选型,正是为了在这个成本盒子内撑住可用性目标。这两个数字是运维域所有取舍(不上云托管、单体而非微服务、人工部署而非重 CI)的约束来源。
2. 为什么这份主档是"薄"的
你可能预期一份运维主档会塞满 docker compose up、回滚命令、Nacos 路由权重之类的操作细节。这里刻意不放,原因有三:
- 可执行的脚本是单一真相源,散文复制只会过期:真正会运行的部署与冒烟逻辑在
deploy/目录下的脚本里(它们是机器执行的、可验证的)。把同样的命令再抄一遍到 Markdown,只会得到一份迟早与脚本不一致的影子文档——而文档治理的硬规则要求每个主题只有一份真相源。 - 操作手册属于范围外:本仓的文档分两层。运维主档属于"策展层",只承载当前真相的骨架(边界、目标、决策);逐机器的 setup、口令、踩坑红线属于可累积的"留痕 / playbook 层",归宿是
.agents/skills/。把它们混进主档会侵蚀主档的"读它=当前真相"这一属性。 - 环境随实战快速演进:机器角色、端口、构建配方在多波实战里持续被修正(例如命名空间改名、构建门从假门禁纠到真门禁)。让它们沉淀在贴近代码的 playbook 里、由实战驱动更新,比锁死在一份主档里更不容易腐化。
简言之:本页负责"为什么这么部署、达标线在哪";"具体怎么敲命令"交给下面的指针。
3. 指针:具体怎么做去这里
| 资源 | 回答什么 | 路径 |
|---|---|---|
| 冒烟门脚本 | 一次部署后跑哪 12(+1)项就绪检查、怎么传 BASE / TOKEN、--deep 走什么写路径 |
deploy/smoke-test.sh |
| staging 运维 playbook | 四机分工铁律、代码怎么同步(Gitea 中转 + push 串行)、后端重部署标准序、隔离验证安全变体、构建 / 测试门红线 | .agents/skills/staging-ops.md |
| 真 UI 走查 playbook | API 全绿之后,怎么经 CDP 在 mini-desktop 上做一次真浏览器 UI 走查 | .agents/skills/ui-walkthrough-cdp.md |
| 工程规范(运维相关红线) | 与部署 / 构建 / 机器口令相关的硬约束(与 playbook 互为索引) | .agents/rules/engineering-conventions.md |
| 当前进度与实操总账 | staging 上各模块的真实状态、历次部署踩坑实录 | docs/mvp/MVP进度总账.md |
纪律:运维域只讲"环境边界 / 部署链 / 健康门 / 可用性目标"这层骨架。一行命令该怎么敲,以
deploy/脚本和.agents/skills/staging-ops.md为准;两者冲突时,以会运行的脚本为准。本页与架构域(系统怎么搭)、运营域(合规上线与发行)互不重复、各管一段。
4. 待补设计:线上观测体系(创始人 2026-06-21 判定补充)
正式设计稿已出:观测体系.md(OTel 采集 + Grafana/夜莺看板告警 + admin 观测入口)与 k8s迁移.md(单节点 k3s 迁移方案 + 风险评估,范围/时机有待创始人拍板项)。本节保留立位与范围摘要,细节以正式稿为准。
上面 §1.3 / §1.4 回答了"怎么知道它健康(冒烟门)"和"健康到什么程度算达标(≥99.5%)",但"线上靠什么持续观测"这一问,现行档是留白的——蓝图画过 Prometheus / Grafana / Jaeger,现实只有秒级冒烟门加一个可用性数字,配不上持续观测的目标。创始人判定:补,而且给了明确方向。
创始人指定的栈与落点:
- 三件齐全:链路日志 + 指标观测 + 监控告警。采集侧走 OpenTelemetry(统一 trace / metrics / log 的采集标准),看板与告警用 Grafana 加 夜莺(Nightingale)。
- admin 侧补观测入口:在 admin 控制台补"观测跳转 / 观测大图",让运营和排查能从后台一键进到监控大盘,而不是各看各的散件。
- 部署形态:观测栈与服务编排往 k8s 收;可参考 yudao-cloud / vue-pro 已有的监控集成(它本身带了一套可观测性接入,能省掉从零搭的工夫)。
范围(创始人 2026-06-21 定全做 → 2026-06-22 修正:观测全做,k8s 收窄 + 缓做):
- 采集:OpenTelemetry 全栈埋点(trace / metrics / log 统一)。
- 看板告警:Grafana + 夜莺(Nightingale),关键告警含可用性 / 错误率 / 生成失败率。
- admin 观测入口:控制台补观测跳转 / 观测大图,一键进监控大盘。
- k8s 编排(收窄 + 缓做):创始人 2026-06-22 定 k8s 收窄为单节点 k3s + staging 应用 + 观测栈、数据库不进集群、推迟多节点 / 高可用 / prod;时机排在 W-G1 生成质量告一段落之后。观测栈先在单体 staging 上裸 docker 跑、不被 k8s 阻塞。详见 k8s迁移.md。
⚠️ blast radius(我作为工程师的提示):上 k8s 是部署架构的大变更——当前是单体 staging(mini-desktop 容器),迁 k8s 会改动 staging-ops 整套部署链与四机分工,影响面远大于其余三件观测工作。建议把"k8s 迁移"单独立一个设计 / 实施项,与观测埋点解耦推进,别让观测大盘被 k8s 迁移阻塞(观测栈本身在单体 staging 上也能先跑起来)。
下一步:由我出正式设计稿(观测体系 + k8s 迁移建议分两份);采集埋点、栈搭建、集群迁移的代码执行归 Mac。
5. 平台后台 job 调度面的运维归属(创始人 2026-06-22 历史回收判定捡回)
上面 §1.1 说"常驻定时任务落在 6c6g",但那指的是 agent 编排的 cron——和平台业务自己的那批后台 job 不是一回事,后者一直没有运维落点,这一节把它补上。
平台现在有一整批后台批处理 job,它们的业务逻辑各档讲清楚了,但"跑在哪台机、卡了谁发现"作为一个整体是空白的:变现链路有三个带 @TenantJob 的结算/补偿 job(tradeSettlementJob 日结算、WithdrawPayoutCompensateJob 打款补偿、RewardPayoutJob 打赏发放,业务面见运营域 变现端到端),telemetry 有一个把埋点事件日聚合成质量分的 job(见后端域 数据模型 §2.3),数据飞轮规划了一个把三源 join 出来落档的离线归档 job(见数据飞轮 §3.3)。再加上上面观测体系 §3.7 新增的 new-api 网关健康巡检,后台 job 已经成批,却没有一处回答"这些 job 由谁调度、跑在哪、失败谁知道"。
这些业务 job 跑在 yudao 自带的 XXL-Job 调度框架上(@TenantJob 是 huijing 对 XXL-Job 任务的多租户封装),它和 agent 编排 cron 是两套东西,所以运维归属要单独定:executor 注册与 job 实际运行落在 staging 机(mini-desktop)的后端进程内——这批 job 是后端单体的一部分,跟着 huijing-server 起,不另开机器;XXL-Job admin 调度控制台作为查看调度记录、手动触发、看失败重试的入口,部署落点与访问端点按内网铁律登记进 docs/内网凭据与端点.md;job 的失败告警接进观测栈的夜莺,与 §3.4 那张 P0–P3 表统一——结算 job 失败意味着创作者拿不到钱、归档 job 卡住意味着语料断供、网关巡检冻结阀拉起意味着生成命脉告急,这几类按其业务影响归入对应告警级别,而不是让它们静默失败到第二天才被人发现。
这条覆盖现有(三个 trade job + telemetry 聚合)和新增(飞轮归档 job + 网关健康巡检)的全部后台 job,是把第一轮只点了"三个结算 job 运维面空白"的盲区,扩成"全平台 job 调度面归属"统一定调。
待落地的运维落点(现在没有):① XXL-Job admin 控制台在 staging 机上的部署与端点登记(进内网凭据档);② executor 注册健康(executor 掉线会让所有 job 静默不跑)纳入观测——给 XXL-Job 的调度成功率/executor 在线数打指标推 Prometheus;③ 每个 job 的失败/超时夜莺告警规则。落地前,"结算 job 跑没跑、归档 job 卡没卡"只能靠人主动去 XXL-Job 控制台翻,没有告警兜底。
6. Feature Flag 生命周期纪律:上线稳定后强制清理(创始人 2026-06-22 历史回收判定捡回)
现行代码里 feature-flag 用得很密——W-G1 开闸的 aigc.control-plane.enabled、生成产线切换的 saaSourceMode、九门的 GATE_ALL9,以及观测体系新增的埋点开关、网关批跑冻结阀,都是"默认关、验稳了再 flip"的灰度开关。这套用法本身是对的(新能力先关着上线、隔离验证过再放量,见 staging-ops §3 的 -D 注入真验 flag 配方),但它有个一直没人管的后半段:flag flip 到稳态之后,该不该清、什么时候清,没有任何纪律约束。结果是默认关的开关只增不减,攒成一堆没人敢动的死代码——每个 flag 都对应一段"开了走这条、关了走那条"的分支逻辑,flag 永久留着,这些分支就永久背在身上,以后改这块代码的人得先搞清每个开关的死活才敢动。
纪律定为:一个 feature-flag 一旦 flip 到目标态并稳定运行一段时间(参照原蓝图口径约 2 周),就必须把开关连同它另一侧的废弃分支一起删掉,让代码只剩下胜出的那条路,不留"理论上还能切回去"的死开关。判断一个 flag 是不是该清,看它是不是已经完成了使命:控制平面验稳了、产线模式定了、九门固化了,这些 flag 的存在意义(灰度过渡)就消失了,该退役。需要长期保留的运行时可调参数(比如阈值、限额这类要随运营调整的)不算这一类——那是配置,不是过渡开关,二者要分清:过渡开关有明确的"用完即弃"终点,运行时参数没有。
这条之所以要写进运维域而不只是口头约定,是因为它是个会随时间累积的技术债红线:每次波次收口都可能新增几个默认关的 flag,没有强制清理纪律,死开关的数量只会单调上涨。波次收口时(见 .agents/skills/wave-close-checklist.md)应顺带盘一次"本波或更早 flip 过的 flag 有没有到清理点",把到点的开关清掉,避免攒到无人敢碰。
注:现行 flag 全靠
-D/yaml + 重部署生效,没有 admin 后台热改能力(那要等 Nacos 作为配置中心上线才解锁,future-state)。所以现在清 flag = 改代码删分支 + 重部署,不是后台点一下;这反而更要求"用完即清",否则每个死开关都是一段永久编译进 jar 的废逻辑。
7. 上线前数据可靠性前置清单(创始人 2026-06-22 历史回收判定捡回)
§1.4 把可用性硬指标定在 ≥99.5%,但可用性达标的另一半——数据不丢——现行档一直没有一处汇总。内网 staging 阶段用的是隔离容器 + seed 假数据、库可重建,丢了不心疼,所以这一摊一直没人接;可真上线后,数据保护是和可用性并列的硬约束,而且其中有一类资产丢了不可再生:创作者生成的游戏包和素材。游戏包不像缓存能重算,丢了就是创作者的作品没了。所以在真上生产之前,要有一份明确的数据可靠性清单逐项落地,而不是上线了才临时补。
清单覆盖四件,都是"上线前必须就位、内网 staging 阶段可暂缓"的生产约束:一是 MySQL 高可用——生产库走主从复制,主库故障从库能顶上,而不是单点;二是备份频率与保留——每日全量备份打底、binlog 增量兜到分钟级,明确备份留多久;三是 OSS(对象存储)容灾——游戏包和素材所在的对象存储做跨区域复制,异地留一份,防单区域故障把不可再生的创作者作品一锅端,这一条尤其要紧,因为它护的是丢了无法重算的资产;四是恢复演练——备份不是存了就算数,要定期真跑一次"从备份恢复"演练,确认备份真能恢复、恢复要多久(对应 SLO 里支付 43 分钟/月、游戏流 3.6 小时/月的 error budget,恢复时长直接吃这个预算)。前三件是"有没有备份/有没有冗余",第四件是"备份到底能不能用",缺第四件前三件都是纸面承诺。
这份清单的定位是运维域的"上线前数据保护门",和 §1.3 的冒烟门(部署后跑没跑得起来)、§4 的观测体系(线上跑得住不住)互补:冒烟门管"这次部署对不对",观测管"线上健不健康",这份清单管"数据丢不丢、丢了能不能恢复"。三者合起来才撑得住 ≥99.5% 可用性 + 数据不丢这两条硬约束。
现状与待落地:内网 staging 阶段四件均未落地(隔离容器单实例、无主从、无跨区复制、无恢复演练),这是有意降级——staging 数据可丢,不值得为它建主从。这份清单是"真上生产前必须逐项落地"的前置门,不是已建项;上生产前要把它从 staging 现状逐条抬到生产标准。其中 MySQL 主从、每日全量+binlog 在
security-and-reliability.md§5.2 留过一行口径,OSS 跨区域复制此前整棵树没有落点,这一节是它的家。