Files
memind/docs/service-isolation-runbook.md

289 lines
9.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> **隔离说明:这份文档只讲生产 / 测试 / 预览边界和事故恢复,不是本地联调手册。要做本机开发,请看 [docs/local-dev.md](./local-dev.md)。**
# 生产 / 测试 / 预览隔离规程
这份文档的目标只有一个:开发预览不能再影响生产。
## 端口边界
| 环境 | 目录 | 用途 | 端口 |
|------|------|------|------|
| 生产 | `/Users/john/Project/Memind` | `g2.tkmind.cn` 当前在线服务(阿里云解析 → 105 → 本机 Mac) | `8081` |
| 生产 MindSpace Service | `/Users/john/MindSpace` | MindSpace 独立 RPC / agent / asset 服务,供 Portal remote adapter 调用 | `8082` |
| 生产 Plaza | `/Users/john/Project/Memind` + Plaza | `plaza.tkmind.cn` 当前在线服务 | `3001` |
| 生产入口 | `/Users/john/Project/Memind/scripts/g2-lb.Caddyfile` | 105 转发入口 / 反代配置 | `8090` |
| 测试 Portal | `/Users/john/Project/Memind` | 开发预览 API / Portal | `18081` |
| 测试 Vite | `/Users/john/Project/Memind` | 开发预览前端 | `15173` |
| 测试 Admin | `/Users/john/Project/Memind` | 开发预览后台 | `18082` |
| 测试 Plaza | `/Users/john/Project/Memind` | 开发预览 Plaza | `13001` |
| 测试 Ops | `/Users/john/Project/Memind` | 开发预览 Ops | `13002` |
硬规则:
- 不在开发预览中使用 `8081``3001``8090`
- 不在开发预览中使用生产 MindSpace service 的 `8082`
- **禁止 SSH 到 `105` 直接改业务源码**;105 是入口/代理层,变更须本地 commit 后发布。见 [105 服务器变更规范](./105-server-operations.md)。
- 不在生产目录里跑会清理端口的开发脚本。
- 103 当前生产拓扑以 [103-runtime-topology.md](./103-runtime-topology.md) 为准;不要用旧文档里的 `/Users/john/Project/Memind/MindSpace` 推断当前 MindSpace Service 根目录。
- 不执行 `scripts/install-prod-services.sh` 来做开发预览;它会释放生产端口。
- 不手动 kill `8081` 上的进程,除非目标就是恢复/重启生产,并且已经确认影响窗口。
## Harness 角色边界
Harness 是开发工具层,不是生产服务层。
正确关系:
```text
Codex / Cursor / Goose 开发过程
-> harness 记忆、recall、context pack、审计
-> Memind / tkmind_go 开发
-> 测试通过后发布到生产
```
禁止关系:
```text
g2.tkmind.cn 生产请求
-> 必须依赖 harness 才能运行
```
本机已有全局 harness
```text
/Users/john/Project/harness
```
它应该嵌入 Codex、Cursor 这类开发工具,让项目开发有记忆:
```bash
/Users/john/Project/harness/bin/codex-harness
/Users/john/Project/harness/bin/install_cursor_hooks.sh
/Users/john/Project/harness/bin/start_dashboard.sh
```
开发时按项目目录区分记忆上下文:
| 项目目录 | 记忆含义 |
|----------|----------|
| `/Users/john/Project/Memind` | 生产项目的开发记忆 |
| `/Users/john/Project/Memind` | 测试项目的开发记忆 |
| 测试 Goose 项目目录(按本机实际路径填写) | 测试 Goose 的开发记忆 |
硬规则:
- 生产启动脚本不能依赖 harness 才能启动。
- 生产请求链路不能调用 harness 才能响应。
- 测试开发可以使用 harness,但不能写入或覆盖生产运行数据。
- 如果要做测试专用长期记忆,优先放在 `/Users/john/Project/test/harness` 或明确的 test 项目命名空间。
## 当前生产如何确认
只读检查:
```bash
cd /Users/john/Project/Memind
lsof -nP -iTCP:8081 -sTCP:LISTEN
curl -s http://127.0.0.1:8081/api/status
cat .h5.pid
```
期望:
- `8081``node server.mjs` 监听。
- `/api/status` 返回 `ok`
- `.h5.pid` 指向当前生产进程。
不要用下面这些命令做普通预览:
```bash
pnpm dev
pnpm dev:server
pnpm start
pnpm start:plaza
node scripts/dev.mjs
node server.mjs
scripts/install-prod-services.sh
```
这些命令如果没有端口隔离,可能占用或释放生产端口。
## 下次开发要预览,怎么做
优先在测试目录进行:
```bash
cd /Users/john/Project/Memind
```
启动前先确认生产还在:
```bash
curl -s http://127.0.0.1:8081/api/status
lsof -nP -iTCP:8081 -sTCP:LISTEN
```
再用测试端口启动预览:
```bash
H5_PORT=18081 \
VITE_PORT=15173 \
ADMIN_PORT=18082 \
PLAZA_PORT=13001 \
OPS_PORT=13002 \
H5_PUBLIC_BASE_URL=http://127.0.0.1:15173 \
VITE_MINDSPACE_BASE=http://127.0.0.1:15173 \
pnpm dev
```
访问地址:
| 页面 | 地址 |
|------|------|
| H5 预览 | `http://127.0.0.1:15173/?preview=mindspace` |
| 测试 Portal | `http://127.0.0.1:18081/api/status` |
| 测试 Admin | `http://127.0.0.1:18082/healthz` |
| 测试 Plaza | `http://127.0.0.1:13001/plaza` |
| 测试 Ops | `http://127.0.0.1:13002/ops/` |
如果只改前端样式,优先只启动 Vite:
```bash
cd /Users/john/Project/Memind
VITE_PORT=15173 \
H5_PUBLIC_BASE_URL=http://127.0.0.1:15173 \
VITE_MINDSPACE_BASE=http://127.0.0.1:15173 \
pnpm dev:vite -- --host 127.0.0.1 --port 15173
```
这种方式不会碰 `8081`,适合做 UI 预览。
## Goose 测试服务
生产 Goose 端口当前不要复用。测试 Goose 放在:
```text
/Users/john/Project/test/test_tkmind_go
```
测试 Goose 端口建议固定为:
| 用途 | 端口 |
|------|------|
| 测试 goosed A | `18106` |
| 测试 goosed B | `18107` |
测试 H5 如果要连测试 Goose,必须在测试目录 `.env` 中显式设置对应地址,不能指向生产 Goose。
## goosed extension 堆积与文件句柄告警
如果 H5 / MindSpace 侧出现下面这类错误:
```text
会话策略同步失败:{"message":"Failed to add extension: IO error: Too many open files (os error 24)"}
```
先不要直接重启生产服务。这个报错在本机更常见的含义是:
- `goosed agent` 下面堆积了过多旧的 extension 子进程。
- macOS `launchctl limit maxfiles` 软上限偏低时,`/agent/add_extension` 会先撞到文件句柄上限。
- 如果恢复会话时无条件重启 agent,会把旧子进程堆得更快。
先做只读确认:
```bash
launchctl limit maxfiles
lsof -nP -iTCP -sTCP:LISTEN | rg '18006|18007|8081|3001|8090'
for pid in 51613 51657; do
echo "PID $pid $(ps -p $pid -o command=)"
lsof -n -p $pid 2>/dev/null | wc -l
done
ps -axo pid,ppid,etime,command | rg 'mindspace-sandbox-mcp|mcp-server-fetch|goosed agent'
```
判断口径:
- `8081``3001``8090` 正常监听,说明主站未必有故障。
- 如果某个 `goosed agent` 的 FD 数已经逼近或超过 `256`,优先怀疑 extension 子进程堆积。
- 如果同一个 `MindSpace/<id>` 在同一个 `goosed` 父进程下出现多个 `mindspace-sandbox-mcp.mjs`,通常只应保留最新一个。
安全处理顺序:
1. 先确认主站端口 `8081``3001``8090` 正常,不要把 route 问题误判成全站宕机。
2. 不先杀 `goosed agent` 主进程,也不要碰 `8081` 上的 `server.mjs`
3. 优先只清理同父进程、同 `MindSpace/<id>` 下重复堆积的旧 `mindspace-sandbox-mcp.mjs` 子进程,以及重复的旧 `mcp-server-fetch` 子进程。
4. 清理后立刻复查 `18006` / `18007``/status``goosed` FD 数,确认 agent 仍存活。
这次线上排查的经验值:
- 两个 `goosed agent` 的 FD 数曾达到 `263` / `209`,而 `launchctl limit maxfiles` 软上限是 `256`
- 仅清理重复 extension 子进程后,FD 数降到 `96` / `77``18006``18007``/status` 仍为 `ok`
- 代码层已改为仅在会话策略真正变化时才触发 agent 重启,减少恢复会话时继续堆积 extension 子进程的概率。
自动巡检:
- `scripts/monitor-goosed-fds.mjs` 每次检查 `goosed agent` FD 数和 `mindspace-sandbox-mcp.mjs` 子进程数。
- `scripts/install-goosed-monitor.sh` 会安装 `~/Library/LaunchAgents/cn.tkmind.goosed-monitor.plist`,默认每 60 秒运行一次。
- 默认策略只清理:
- orphan 的 sandbox MCP 子进程;
- 同一父进程、同一 `MindSpace/<id>` 下超过 2 个的重复 sandbox MCP
- 总 sandbox MCP 数超过 80 时最旧的一批。
- 默认不按存活时间清理单个老进程,避免误伤长会话。
- goosed FD 超过 `GOOSED_FD_WARN=180` 只记录 warning;超过 `GOOSED_FD_RESTART=3200` 才滚动重启对应 goosed。
硬规则:
- 不要因为 `Too many open files` 先去重启 `8081` 生产 Portal。
- 不要在未确认影响窗口前直接重启 `goosed agent` 主进程。
- 优先清理“明显重复”的 extension 子进程,而不是清空所有 agent 子进程。
## 数据库口径
短期如果沿用同一个 RDS 库,只允许做 UI 和非破坏性流程预览。
不能在共用库时做这些事:
- 改表结构。
- 跑迁移脚本。
- 批量清理数据。
- 测试支付回调、发布审核、权限变更等会污染真实用户状态的流程。
涉及数据结构或真实业务状态的开发,先建独立测试库,再预览。
## 发布前流程
完整步骤、分步发布、105 同步与回滚见 **[生产更新发布指南](./release-deploy.md)**。
最小检查:
1. 在测试端口(如 `18081`)完成开发与预览;本机联调目录请看 [docs/local-dev.md](./local-dev.md),不要把这份规程当成开发启动手册。
2. `pnpm run build` + `pnpm test`
3. `curl -s http://127.0.0.1:8081/api/status` 确认生产仍在线。
4. `./rsync_to_server.sh --dry-run` 预览 diff,确认后再执行正式发布。
## 事故恢复最小步骤
如果发现 `g2.tkmind.cn` 异常,先检查本地主机生产:
```bash
cd /Users/john/Project/Memind
lsof -nP -iTCP:8081 -sTCP:LISTEN
curl -s http://127.0.0.1:8081/api/status
```
如果 `8081` 没有监听,再恢复本地主机 Portal:
```bash
cd /Users/john/Project/Memind
nohup /opt/homebrew/opt/node@24/bin/node server.mjs >> h5.log 2>&1 &
echo $! > .h5.pid
sleep 2
curl -s http://127.0.0.1:8081/api/status
```
恢复后再检查入口反代:
```bash
curl -s http://127.0.0.1:8090/api/status
```