Files
memind/docs/release-canary-103.md
T
john 7a381db132
Memind CI / Test, build, and release guards (push) Has been cancelled
refactor: make production gate impact only
2026-07-28 01:13:28 +08:00

98 lines
5.4 KiB
Markdown
Raw 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.
# 103 用户级灰度发布
Portal 的首个生产动作必须是用户级灰度,不能直接整包替换稳定版本。
## 运行拓扑
| 组件 | 地址 | 说明 |
|---|---|---|
| 稳定 Portal | `103:8081` | 105 当前直接回源 `58.38.22.103:8081`;灰度发布不替换该进程 |
| 灰度路由器 | `103 127.0.0.1:18082` | 避开已占用的 18080;只通过专用灰度隧道暴露给 105 |
| 灰度边缘隧道 | `105 127.0.0.1:19082` | 反向 SSH 到 103 的 18082;不复用已退役的 19081 链路 |
| 候选 Portal | `127.0.0.1:18081` | 独立、无源码 runtime;使用被动运行时模式 |
| 候选 goosed | `127.0.0.1:18015` | 独立容器并挂载候选 runtime,不重建稳定 9 个容器 |
| DeepSeek 兼容代理 | `103 0.0.0.0:18036` | 仅供宿主机和候选容器使用;不接入 105 或公网入口 |
候选 Portal 通过 `MEMIND_PORTAL_H5_ROOT` 共享经审计的持久数据和数据库,但
`MEMIND_RUNTIME_ROLE=candidate` 会禁止重复启动 MindSpace、Plaza、提醒、订阅续期和
Agent 恢复扫描等单例后台任务。
候选 Portal 必须从候选 runtime 目录启动,使相对模块只解析到本次候选 artifact。
候选 goosed 在 Docker 内执行扩展,MCP Node 和入口路径必须固定为
`/usr/local/bin/node``/opt/portal/mindspace-sandbox-mcp.mjs`;禁止继承稳定
`.env` 中仅宿主机可见的 `/Users/...` 路径。若日志出现
`Failed to add extension` 和宿主机绝对路径的 `MODULE_NOT_FOUND`,应先核对候选
进程环境与 `/opt/portal` 容器挂载,禁止把宿主机文件复制进容器临时修补。
DeepSeek V4 的工具轮次还必须经过候选 runtime 中的
`deepseek-no-think-proxy.mjs`。候选启动脚本必须强制
`MEMIND_DEEPSEEK_DISABLE_THINKING=1`,并把 goosed 自定义 provider 指向
`http://host.docker.internal:18036/v1`。该代理通过独立 LaunchAgent 管理;
发布必须同时验证 `/health` 的兼容契约和候选容器到该地址的连通性。灰度路由器把
该健康地址纳入候选健康闭包,代理掉线时必须自动回落稳定 Portal。
若日志出现
`The reasoning_content in the thinking mode must be passed back to the API.`
说明 DeepSeek 请求绕过了兼容契约。禁止只在线补环境变量:必须检查 artifact、
LaunchAgent、Portal provider 同步和路由器依赖健康配置是否属于同一 release。
## 身份和回落规则
- `john`:按数据库中的唯一用户名或用户 ID 匹配。
- 微信用户“唐”:只按绑定微信身份的唯一账号标识 `wx_ul610et8` 匹配;该值在生产库中是唯一 `username/slug`,不是显示昵称。
- 显示昵称永远不参与路由。
- Cookie 会话通过 `h5_login_sessions` 的 token hash 只读解析。
- 微信回调通过 `FromUserName``h5_user_wechat_identities` 只读解析到绑定账号,再用唯一账号标识匹配。
- 未命中、无身份、过期/撤销会话、数据库异常:走稳定版本。
- 候选 `/api/status` 不健康:自动走稳定版本。
- DeepSeek 兼容代理不健康:自动走稳定版本。
- SSE、附件、公开页和普通 API 均由同一反向代理转发;微信 XML body 必须原样保留。
## 发布入口
候选必须来自干净且等于 `origin/main` 的完整 `main`CI 成功,并具有与
`.runtime/portal` 完全一致、未过期且可从线上 base commit 重算选择结果的 Core + Impact
Gate report。关键路径展开到预定义影响域;未映射路径或无效基线直接阻断。
```bash
bash scripts/release-portal-canary-prod.sh --yes
```
该脚本按顺序执行:
1. 验证源码、分支、远端主线、artifact hash;缺少有效报告时自动执行 Core + Impact Gate。
2. 执行 103 只读预检。
3. 上传经过验证的候选包。
4. 创建稳定 live 全量备份、持久目录备份和 105 两份活动 nginx 配置备份,并校验完整性。
5. 启动独立候选 goosed 和 DeepSeek 兼容代理,验证兼容契约及容器连通性。
6. 启动候选 Portal 和灰度路由器。
7. 验证不可变身份配置、稳定/候选/依赖健康和路由器诊断。
8. 启动独立的 `105:19082 -> 103:18082` 灰度隧道并从 105 验证。
9. 短暂阻止新 Agent Run,排空活动任务。
10. 逐文件原子替换、验证并 reload 105 nginx,把活动上游从 `58.38.22.103:8081` 切到 `127.0.0.1:19082`
11. 主动停止候选,证明目标身份会自动回落稳定版,再恢复候选。
任一步失败都会把 105 nginx 恢复到稳定 `58.38.22.103:8081`,停止灰度隧道和候选服务,
并保留候选目录与备份证据。活动 nginx 配置变更只能由已提交的发布脚本执行,禁止在线手改。
## 回滚
```bash
bash scripts/rollback-portal-canary-prod.sh --yes
```
回滚只执行以下动作:
- 把 105 两份活动 nginx 上游恢复到稳定 Portal `58.38.22.103:8081` 并验证 reload。
- 停止灰度隧道、灰度路由器、候选 Portal、候选 goosed 和 DeepSeek 兼容代理。
- 清除发布排水标记。
- 验证稳定 Portal 和 105 HTTPS 入口健康。
不会删除候选目录、备份或稳定运行目录。
## 全量晋升
`scripts/release-portal-runtime-prod.sh` 仍是整包替换脚本,不能把“灰度路由器已实现”
解释为自动获得全量切换权限。全量晋升必须使用同一 commit 和 artifact,保留灰度验收
证据,并再次取得明确批准;在晋升入口完成对应证据校验前,该脚本继续硬阻断非 dry-run。