|
|
@@ -0,0 +1,170 @@
|
|
|
+# 给主容器:重启 `agent-node-lj-test` 的 Gateway
|
|
|
+
|
|
|
+> 日期:2026-11-13
|
|
|
+> 目标:让插件新代码生效(代码已改好、`py_compile` 通过,但没重启)
|
|
|
+> 预计中断:**3 秒**(稳妥预期 30-60 秒)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 〇、为什么需要这次重启
|
|
|
+
|
|
|
+容器 `agent-node-lj-test` 的 `fmode-deliverable-hook.py` 已修复「无 threadRoot 就整批丢弃」的 bug,
|
|
|
+但 gateway 进程仍在跑旧代码,因此**文件上云链路依然不通**。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 一、目标容器定位(⚠️ 先核对,别找错)
|
|
|
+
|
|
|
+之前有过多起「按 docker 容器名找错容器」的事故。**只认这两项**:
|
|
|
+
|
|
|
+```bash
|
|
|
+docker exec agent-node-lj-test sh -c '
|
|
|
+echo "AGENT_ID = $(grep -E "^AGENT_ID=" /opt/data/.env | cut -d= -f2-)"
|
|
|
+echo "MATRIX_USER = $(grep -E "^MATRIX_USER_ID=" /opt/data/.env | cut -d= -f2-)"
|
|
|
+'
|
|
|
+```
|
|
|
+
|
|
|
+| 项 | 期望值 |
|
|
|
+|---|---|
|
|
|
+| `AGENT_ID` | `agent-node-lj-test` |
|
|
|
+| `MATRIX_USER` | `@zgdcmgnqcd:fmode.cn` |
|
|
|
+
|
|
|
+> ⚠️ docker 容器名是 `agent-node-lj-test` 的那台,`AGENT_ID` 可能为空(那是退役的旧身份)。
|
|
|
+> 真正目标的 docker 名可能是 `agent-node-u19170119836`。**以 AGENT_ID + MATRIX_USER 为准。**
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 二、🔴 关键:不要用 s6 重启(那是空槽)
|
|
|
+
|
|
|
+容器侧实测:
|
|
|
+
|
|
|
+```
|
|
|
+/run/service/gateway-default → down (exitcode 0) 7070 seconds ← 7000+ 秒前就挂了,是空的
|
|
|
+/run/s6/services/main-hermes → 只 exec sleep infinity,也是空的
|
|
|
+真正跑 gateway 的是 PID 94769,由 rc.init 的 `hermes gateway run --replace` 启动,不在 s6 监督下
|
|
|
+```
|
|
|
+
|
|
|
+**所以 `s6-svc -r /run/service/gateway-default` 重启的是一个本来就没运行的备用槽,等于没重启。**
|
|
|
+
|
|
|
+> 这也解释了历史上「改了配置说重启了、实际没生效」的原因。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 三、执行步骤(在容器内,需 root)
|
|
|
+
|
|
|
+### 1. 记录重启前状态
|
|
|
+
|
|
|
+```bash
|
|
|
+echo "=== 旧 PID ==="
|
|
|
+ps -eo pid,lstart,cmd | grep "gateway run" | grep -v grep
|
|
|
+cat /opt/data/gateway.lock 2>/dev/null
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 优雅重启
|
|
|
+
|
|
|
+```bash
|
|
|
+# ① 拿到当前 PID(应是 94769,若不同则用实际值)
|
|
|
+OLD=$(pgrep -f "hermes gateway run" | head -1)
|
|
|
+echo "OLD=$OLD"
|
|
|
+
|
|
|
+# ② 优雅退出
|
|
|
+kill -TERM "$OLD"
|
|
|
+
|
|
|
+# ③ 等它放锁(最多 20 秒)
|
|
|
+for i in $(seq 1 20); do
|
|
|
+ kill -0 "$OLD" 2>/dev/null || break
|
|
|
+ sleep 1
|
|
|
+done
|
|
|
+
|
|
|
+# ④ 20 秒不退再强杀
|
|
|
+kill -KILL "$OLD" 2>/dev/null || true
|
|
|
+sleep 1
|
|
|
+
|
|
|
+# ⑤ 确认进程已死、锁已释放,再清锁
|
|
|
+kill -0 "$OLD" 2>/dev/null && echo "STILL_ALIVE" || echo "EXITED"
|
|
|
+rm -f /opt/data/gateway.lock
|
|
|
+
|
|
|
+# ⑥ 重新拉起(与原启动方式一致)
|
|
|
+cd /opt/hermes && setsid nohup /opt/hermes/.venv/bin/hermes gateway run --replace \
|
|
|
+ > /tmp/gw-restart.log 2>&1 < /dev/null &
|
|
|
+
|
|
|
+sleep 8
|
|
|
+```
|
|
|
+
|
|
|
+> ⚠️ 第 ⑤ 步的 `rm -f gateway.lock` **必须在确认进程已死之后**,否则会两个进程抢锁。
|
|
|
+
|
|
|
+### 3. 恢复 s6 监督(推荐,一次性理顺)
|
|
|
+
|
|
|
+```bash
|
|
|
+/command/s6-svc -u /run/service/gateway-default
|
|
|
+sleep 3
|
|
|
+/command/s6-svstat /run/service/gateway-default # 期望 up (pid …)
|
|
|
+```
|
|
|
+
|
|
|
+> 若 `gateway-default` 拉起后因锁冲突立刻退出,说明 94769 还没死透 —— 回到第 2 步再等。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 四、验证(三项都要对上)
|
|
|
+
|
|
|
+### 1. 进程层
|
|
|
+
|
|
|
+```bash
|
|
|
+# 新 PID 启动时间必须晚于现在
|
|
|
+ps -o pid,lstart,etime,cmd -C python3 | grep "gateway run"
|
|
|
+cat /opt/data/gateway.lock # pid 应是新 PID
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 日志层(**决定性**:这两个关键字是本次改动独有的,旧代码不会打)
|
|
|
+
|
|
|
+```bash
|
|
|
+tail -100 /tmp/gw-restart.log | grep -E "strip_media|已上云|MEDIA|Traceback|connected"
|
|
|
+```
|
|
|
+
|
|
|
+期望看到三通道连上:`matrix` / `api_server` / `wecom` connected,且**无 Traceback**。
|
|
|
+
|
|
|
+### 3. 行为层(发一条消息触发)
|
|
|
+
|
|
|
+让主人在 App 里**随便发一句话**给这个容器,然后:
|
|
|
+
|
|
|
+```bash
|
|
|
+grep -n "strip_media" /tmp/gw-restart.log | tail -3
|
|
|
+```
|
|
|
+
|
|
|
+出现 `strip_media: 已剥离 MEDIA 标记` = 新插件已加载。
|
|
|
+若仍是老行为(只有 `deliverable-inject: 删除非白名单 URL` 而无 `strip_media`)= 新代码没进去。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 五、回滚(万一启动失败)
|
|
|
+
|
|
|
+```bash
|
|
|
+# 看失败原因
|
|
|
+tail -50 /tmp/gw-restart.log
|
|
|
+
|
|
|
+# 代码回滚(hook 有备份)
|
|
|
+ls /opt/data/scripts/fmode-deliverable-hook.py.bak* 2>/dev/null
|
|
|
+# 恢复后重新执行第三节的重启步骤
|
|
|
+```
|
|
|
+
|
|
|
+> 插件目录 `/opt/data/plugins/` 和 `/opt/data/scripts/` 都在**持久卷**,不会因重启丢失。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 六、回交内容
|
|
|
+
|
|
|
+1. §一 指纹核对的原始输出
|
|
|
+2. §三 第 1 步的旧 PID、第 2 步的 `EXITED` 确认
|
|
|
+3. §四 三项验证的**原始输出**
|
|
|
+4. 重启完成时间
|
|
|
+
|
|
|
+**不要只回「已重启」。** 判据是 §四.2 的三通道 connected + 无 Traceback。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 七、安全提醒
|
|
|
+
|
|
|
+`/tmp/gw-env.txt`(如果存在)里有明文 `WECOM_SECRET`、`FMODE_API_KEY`、`FMODE_SESSION_TOKEN`、dashboard 密码。
|
|
|
+**不要把该文件内容复制进日志、聊天记录或提交。**
|
|
|
+
|
|
|
+> 本项目已裁决「密钥允许明文存放、集中存放即可」,但没必要扩散到日志里。
|