d
This commit is contained in:
563
docs/ops_center_runtime/HANDOFF_20260420_1920.md
Normal file
563
docs/ops_center_runtime/HANDOFF_20260420_1920.md
Normal file
@@ -0,0 +1,563 @@
|
||||
# HANDOFF_20260420_1920
|
||||
|
||||
更新时间:2026-04-20 19:20 CST
|
||||
|
||||
## 本轮结论
|
||||
|
||||
这轮最重要的收口已经完成:
|
||||
|
||||
- 已确认当前工作机 `/www/wwwroot/getDomain` 所在主机是海外测试控制面,不是国内 `mainland-controller-01`
|
||||
- 已把“假冒 mainland-controller-01”的本机配置纠正为 `overseas-control-01`
|
||||
- 已把真正的 `mainland-controller-01` 重新接回控制面,并完成一次成功的远端发布
|
||||
- 已修正 `mainland-controller-01` 的 node-agent 身份上报问题
|
||||
- 现在控制面里看到的 `mainland-controller-01` 已经是真实主机:
|
||||
- `hostname = mainland-controller-01`
|
||||
- `ip = 121.204.244.188`
|
||||
|
||||
一句话说当前状态:
|
||||
|
||||
“主线已经从‘节点身份混乱’推进到‘真 controller 已经对正并重新进场’,下一步应只盯 controller 真机吞吐和 worker 恢复,不要再回展示层。”
|
||||
|
||||
## 当前环境认知
|
||||
|
||||
### 1. 当前 Codex 所在机器不是大陆 controller
|
||||
|
||||
本地执行结果:
|
||||
|
||||
- `hostname = v2202604268673447256`
|
||||
- `nproc = 12`
|
||||
|
||||
这台机器是海外控制面测试机,不是用户说的 112 核 controller。
|
||||
|
||||
真正的国内 controller 是:
|
||||
|
||||
- `node_code = mainland-controller-01`
|
||||
- `ssh_host = 121.204.244.188`
|
||||
|
||||
真正的国内 worker 是:
|
||||
|
||||
- `node_code = mainland-worker-01`
|
||||
- `ssh_host = 121.204.244.248`
|
||||
|
||||
### 2. 本机服务身份已经纠正
|
||||
|
||||
为避免海外测试机继续伪装成 mainland controller,已经改过这些环境文件:
|
||||
|
||||
- `/etc/default/domaincheck-worker`
|
||||
- `/etc/default/domaincheck-node-agent`
|
||||
- `/etc/default/domaincheck-api`
|
||||
|
||||
修正后的核心值:
|
||||
|
||||
- `NODE_CODE=overseas-control-01`
|
||||
- `NODE_REGION=overseas`
|
||||
- `SYNC_PUSH_ENABLED=false`
|
||||
|
||||
并且已经执行过:
|
||||
|
||||
- 停止本机 `domaincheck-worker`
|
||||
- 停止本机 `domaincheck-node-agent`
|
||||
- 重启本机 `domaincheck-api`
|
||||
|
||||
当前海外控制面 API 进程环境已确认是:
|
||||
|
||||
- `NODE_CODE=overseas-control-01`
|
||||
- `NODE_REGION=overseas`
|
||||
|
||||
## 本轮已完成的关键修复
|
||||
|
||||
## A. 修复 controller node-agent 上报 localhost / 127.0.0.1
|
||||
|
||||
修改文件:
|
||||
|
||||
- [domain-api/app/node_agent.py](/www/wwwroot/getDomain/domain-api/app/node_agent.py)
|
||||
|
||||
修复内容:
|
||||
|
||||
- `_hostname()` 不再优先相信 `localhost`
|
||||
- `_ip()` 不再使用容易得到 `127.0.0.1` 的旧逻辑
|
||||
- 优先通过控制面地址推导本机出口 IP
|
||||
- 回退时也会过滤 loopback
|
||||
|
||||
本轮新增/补过的测试:
|
||||
|
||||
- [domain-api/tests/test_node_agent_delivery_queue.py](/www/wwwroot/getDomain/domain-api/tests/test_node_agent_delivery_queue.py)
|
||||
|
||||
注意:
|
||||
|
||||
- 当前仓库里这份修复已经在真 controller 上生效
|
||||
- handover 已显示:
|
||||
- `agent_hostname = mainland-controller-01`
|
||||
- `agent_ip = 121.204.244.188`
|
||||
- `cluster_hostname = mainland-controller-01`
|
||||
- `cluster_ip = 121.204.244.188`
|
||||
|
||||
## B. 修复 control 节点发布健康检查窗口过短
|
||||
|
||||
修改文件:
|
||||
|
||||
- [domain-api/app/services/ops_release_service.py](/www/wwwroot/getDomain/domain-api/app/services/ops_release_service.py)
|
||||
|
||||
修改目标:
|
||||
|
||||
- control 节点发布时,`domaincheck-api` 启动偏慢,旧健康检查窗口太短,会误判失败并回滚
|
||||
|
||||
代码里已改成:
|
||||
|
||||
- `health_check_timeout_seconds = 20`
|
||||
- `health_check_retries = 6`
|
||||
- `health_check_interval_seconds = 3`
|
||||
|
||||
但要注意一个坑:
|
||||
|
||||
- 这份代码虽然已改进源码
|
||||
- 海外控制面的运行中 API 进程还没有通过这份新源码重新部署
|
||||
- 所以 `smart-rollout-preview` 里看到的默认值一度仍然是旧的 `10 / 2 / 2`
|
||||
|
||||
因此本轮实际是通过“手工 remote-agent deploy job 显式带长窗口 payload”打通了 controller 发布。
|
||||
|
||||
## C. 真 controller 发布已经成功一次
|
||||
|
||||
成功任务:
|
||||
|
||||
- `job_id = 331`
|
||||
- `job_code = ops-20260420183116-c33723`
|
||||
- `action = deploy.release`
|
||||
- `target_node_code = mainland-controller-01`
|
||||
- `execution_mode = remote-agent`
|
||||
- `status = success`
|
||||
|
||||
本次使用的 release:
|
||||
|
||||
- `release_id = 27`
|
||||
- `release_version = domaincheck_release_20260420_182916`
|
||||
|
||||
发布结果要点:
|
||||
|
||||
- 发布包下载成功
|
||||
- checksum 校验成功
|
||||
- `/opt/domaincheck/current` 切换成功
|
||||
- `domaincheck-api / domaincheck-worker / domaincheck-sync-agent` 重启成功
|
||||
- 健康检查最终通过
|
||||
|
||||
尤其要记住:
|
||||
|
||||
- 这次成功不是靠 smart rollout 默认值
|
||||
- 是通过显式下发以下健康窗口打通的:
|
||||
- `health_check_timeout_seconds = 20`
|
||||
- `health_check_retries = 6`
|
||||
- `health_check_interval_seconds = 3`
|
||||
|
||||
## D. controller node-agent 已经重新连回
|
||||
|
||||
后续又触发了:
|
||||
|
||||
- `job_id = 332`
|
||||
- `action = service.restart`
|
||||
- `payload.service_name = domaincheck-node-agent`
|
||||
|
||||
这个任务本身还停留在 `running`,原因很正常:
|
||||
|
||||
- node-agent 在“执行重启自己”的过程中会打断自身回执链
|
||||
- 所以作业状态可能不会自然收尾
|
||||
|
||||
但实际效果已经发生:
|
||||
|
||||
- API 日志已看到 `121.204.244.188` 在 `2026-04-20 18:35:16` 重新开始:
|
||||
- `POST /api/v1/ops/agent/heartbeat`
|
||||
- `POST /api/v1/ops/agent/pull?limit=1`
|
||||
|
||||
因此这条 job 332 可以视为“结果已生效,但状态未优雅回写”的典型自重启任务。
|
||||
|
||||
## 当前实机状态
|
||||
|
||||
按最新控制面查询:
|
||||
|
||||
### mainland-controller-01
|
||||
|
||||
- `agent_online = true`
|
||||
- `cluster_status = busy`
|
||||
- `agent_hostname = mainland-controller-01`
|
||||
- `agent_ip = 121.204.244.188`
|
||||
- `current_load` 在本轮观察中约 `210 - 233`
|
||||
- `detect_runtime.active_threads` 在本轮观察中约 `210 - 233 / 2000`
|
||||
- 已经有持续日志回传
|
||||
|
||||
### mainland-worker-01
|
||||
|
||||
- node-agent 在线
|
||||
- 但当前没有真正参与检测
|
||||
- 控制面返回的 `detect_runtime` 错误为:
|
||||
- `connection to server at "127.0.0.1", port 5432 failed: Connection refused`
|
||||
|
||||
这意味着:
|
||||
|
||||
- worker 机现在不是主要算力来源
|
||||
- 目前真正吃任务的是 `mainland-controller-01`
|
||||
|
||||
## 当前主线瓶颈
|
||||
|
||||
目前主线已经不是“谁是 controller”了,当前瓶颈明确是下面两个:
|
||||
|
||||
1. `mainland-worker-01` 未恢复到可参与检测状态
|
||||
2. `mainland-controller-01` 虽然真实线程已抬到 200+,但控制面队列视角仍存在:
|
||||
- `claimed` 偏高
|
||||
- `running/completed` 推进不够理想
|
||||
- 吞吐没有完全吃透机器
|
||||
|
||||
也就是说:
|
||||
|
||||
- “节点身份问题”已基本打穿
|
||||
- “发布链路问题”已基本打穿
|
||||
- 下一步该只盯“真 controller 吞吐”和“worker 恢复”
|
||||
|
||||
## 本轮新增发现
|
||||
|
||||
### 1. mainland-worker-01 的 `127.0.0.1:5432` 更像是 node-agent 侧遥测链路问题
|
||||
|
||||
本轮继续排查后,发现这个问题至少有两层:
|
||||
|
||||
1. `domainCheck` 默认配置本身就是:
|
||||
- `DB_HOST = localhost`
|
||||
2. worker 机上的 `domaincheck-node-agent` systemd unit 当前只加载:
|
||||
- `/etc/default/domaincheck-api`
|
||||
- `/etc/default/domaincheck-node-agent`
|
||||
|
||||
但大陆 worker 快速安装脚本真正写入数据库与 Redis 指向的是:
|
||||
|
||||
- `/etc/default/domaincheck-worker`
|
||||
|
||||
也就是说,worker 机上很可能出现这种情况:
|
||||
|
||||
- `domaincheck-worker` 进程拿到的是正确的 `DB_HOST=${MAINLAND_CONTROLLER_IP}`
|
||||
- 但 `domaincheck-node-agent` 没有继承 `/etc/default/domaincheck-worker`
|
||||
- node-agent 内部去跑 `get_detect_status()` 时,就会落回 `domain-api` 默认配置:
|
||||
- `db_host = 127.0.0.1`
|
||||
|
||||
于是控制面上看到的现象就变成:
|
||||
|
||||
- `mainland-worker-01 agent 在线`
|
||||
- 但 `detect_runtime` 里报:
|
||||
- `connection to server at "127.0.0.1", port 5432 failed: Connection refused`
|
||||
|
||||
### 2. node-agent 当前把“DB 查询失败”和“worker 进程离线”混成了一种失败
|
||||
|
||||
`domain-api/app/node_agent.py` 里的 `_detect_runtime_snapshot()` 原来是:
|
||||
|
||||
- `get_detect_status()` 和 `detect_worker_runtime()` 放在同一个总 `try` 里
|
||||
|
||||
这会导致:
|
||||
|
||||
- 只要前面的 DB 查询失败
|
||||
- 后面的 worker systemd 运行态也一起被吞掉
|
||||
- 最终上报成:
|
||||
- `worker_online = false`
|
||||
- `service_running = false`
|
||||
|
||||
即使真实情况其实可能只是:
|
||||
|
||||
- worker 服务还活着
|
||||
- 只是 node-agent 在采集 detect status 时查错 DB 了
|
||||
|
||||
### 3. 本轮已在源码里补的最小修复
|
||||
|
||||
已修改:
|
||||
|
||||
- [domain-api/app/node_agent.py](/www/wwwroot/getDomain/domain-api/app/node_agent.py)
|
||||
- [domain-api/deploy/systemd/domain-node-agent.service](/www/wwwroot/getDomain/domain-api/deploy/systemd/domain-node-agent.service)
|
||||
- [domain-api/tests/test_node_agent_delivery_queue.py](/www/wwwroot/getDomain/domain-api/tests/test_node_agent_delivery_queue.py)
|
||||
|
||||
修复内容:
|
||||
|
||||
1. `_detect_runtime_snapshot()` 改成分层降级:
|
||||
- 先拿 `detect_worker_runtime()`
|
||||
- 再单独尝试 `get_detect_status()`
|
||||
- 即使 detect status 因 DB 异常失败,也保留真实的 worker service 运行态
|
||||
2. `domaincheck-node-agent.service` 追加:
|
||||
- `EnvironmentFile=-/etc/default/domaincheck-worker`
|
||||
|
||||
这样 worker 机上的 node-agent 也能直接继承 worker 真实使用的 `DB_HOST / REDIS_HOST`。
|
||||
|
||||
### 4. 这次修复的生效边界
|
||||
|
||||
要特别注意:
|
||||
|
||||
- `node_agent.py` 代码修复可以通过常规 release 进入真实运行目录并生效
|
||||
- 但 `domaincheck-node-agent.service` 属于 `/etc/systemd/system/` 下的系统级 unit 文件
|
||||
- 常规 `deploy.release` 只会切换 `/opt/domaincheck/current`,不会自动重装 systemd unit
|
||||
|
||||
所以这两项应分开看:
|
||||
|
||||
1. `node_agent.py`:
|
||||
- 可以走最小 release 先上真机
|
||||
2. `domain-node-agent.service`:
|
||||
- 需要后续补 systemd unit 落地动作
|
||||
- 至少要包含:
|
||||
- 覆盖 unit 文件或 drop-in
|
||||
- `systemctl daemon-reload`
|
||||
- `systemctl restart domaincheck-node-agent`
|
||||
|
||||
### 5. 本轮测试结果
|
||||
|
||||
已通过的焦点测试:
|
||||
|
||||
- `PYTHONPATH=/www/wwwroot/getDomain/domain-api /opt/domaincheck/domainCheck/.venv/bin/python -m unittest tests.test_node_agent_delivery_queue -v`
|
||||
|
||||
结果:
|
||||
|
||||
- `11 tests`
|
||||
- `OK`
|
||||
|
||||
新增验证点:
|
||||
|
||||
- `test_detect_runtime_snapshot_degrades_to_worker_runtime_when_detect_status_fails`
|
||||
|
||||
它验证了:
|
||||
|
||||
- 即使 `get_detect_status()` 抛出
|
||||
- `connection to server at "127.0.0.1", port 5432 failed`
|
||||
- node-agent 依然会保留:
|
||||
- `worker_online = true`
|
||||
- `service_running = true`
|
||||
- `phase_detail = active/running`
|
||||
|
||||
## 本轮最小发布动作
|
||||
|
||||
### 1. 已生成新发布包
|
||||
|
||||
本轮最小修复已重新打包:
|
||||
|
||||
- `release_version = domaincheck_release_20260420_215734`
|
||||
- `release_id = 28`
|
||||
|
||||
生成结果:
|
||||
|
||||
- `archive = /www/wwwroot/getDomain/release/domaincheck_release_20260420_215734.tar.gz`
|
||||
- `sha256 = ad2c41625503dab1efaeadbf0641c8f6875a3cad0581a3b9c7835ccc37cdc718`
|
||||
|
||||
### 2. 已向真节点发起最小 release 下发
|
||||
|
||||
本轮只为把 `node_agent.py` 先推进真实运行目录,已创建:
|
||||
|
||||
- `job_id = 335`
|
||||
- `target = mainland-controller-01`
|
||||
- `action = deploy.release`
|
||||
- `job_id = 336`
|
||||
- `target = mainland-worker-01`
|
||||
- `action = deploy.release`
|
||||
|
||||
这两条 job 都已经越过“等待拉取”,进入了 agent 执行阶段,并至少记录到:
|
||||
|
||||
- `executor_received`
|
||||
- `deploy_download_started`
|
||||
|
||||
说明:
|
||||
|
||||
- 真 controller 和真 worker 都已经接到了这版最小 release
|
||||
- 当前至少已经在真实节点上进入下载/发布链
|
||||
|
||||
### 3. 这次发布的真实目标
|
||||
|
||||
这次发布不是为了一次性解决所有 worker 恢复问题,而是为了先把下面这条代码修复推到真节点:
|
||||
|
||||
- `domain-api/app/node_agent.py`
|
||||
|
||||
目的:
|
||||
|
||||
- 即使 worker 节点的 `detect_status` 查询因 DB 指向错误失败
|
||||
- node-agent 也不再把真实的 worker service 运行态一并吞掉
|
||||
- 控制面能先看到更接近真实的 `worker_online / service_running`
|
||||
|
||||
### 4. 这次发布的限制
|
||||
|
||||
这次最小 release 暂时还不能自动完成下面这件事:
|
||||
|
||||
- 把 `/etc/systemd/system/domaincheck-node-agent.service` 更新为新版本
|
||||
|
||||
原因:
|
||||
|
||||
- 常规 `deploy.release` 切的是 `/opt/domaincheck/current`
|
||||
- 不会自动重装 systemd unit
|
||||
|
||||
因此当前判断是:
|
||||
|
||||
1. `node_agent.py` 代码修复:
|
||||
- 已经进入真实节点发布链
|
||||
2. `domaincheck-node-agent.service` 的 `EnvironmentFile=-/etc/default/domaincheck-worker`:
|
||||
- 仍需要后续单独补系统级落地动作
|
||||
|
||||
### 5. 当前发布观察结论
|
||||
|
||||
截至本次交接整理时:
|
||||
|
||||
- `job 335 / 336` 已启动执行
|
||||
- 但尚未在本轮记录中拿到最终 `success / failed` 收尾结论
|
||||
- 因此后续接手时,先做的第一件事之一,就是复查这两条 job 的最终状态与事件流
|
||||
|
||||
## 当前不要再踩的坑
|
||||
|
||||
### 1. 不要再把本机当成 mainland-controller-01
|
||||
|
||||
当前工作机是海外测试控制面,只负责:
|
||||
|
||||
- API
|
||||
- 控制面
|
||||
- 同步接收
|
||||
|
||||
不是 112 核大陆 controller。
|
||||
|
||||
### 2. 不要直接用本机 Python service 函数创建 deploy job
|
||||
|
||||
坑点:
|
||||
|
||||
- 直接在 shell 里跑本地 Python 服务函数时,读取到的 `settings.node_code` 可能不是运行中 API 进程的真实环境
|
||||
- 之前就出现过把 job 错判为 `local-runtime` 的情况
|
||||
|
||||
正确做法:
|
||||
|
||||
- 优先通过“正在运行的 API HTTP 接口”创建 job
|
||||
- 不要优先走本机 Python service 入口
|
||||
|
||||
推荐路径:
|
||||
|
||||
- `POST /api/v1/ops/releases/from-package/latest`
|
||||
- `POST /api/v1/ops/jobs`
|
||||
- `POST /api/v1/ops/jobs/{job_id}/dispatch`
|
||||
|
||||
### 3. 不要把 job 332 当成硬故障
|
||||
|
||||
`job 332` 是 `service.restart domaincheck-node-agent`。
|
||||
|
||||
它卡在 `running` 不代表没生效,反而更像:
|
||||
|
||||
- node-agent 重启了自己
|
||||
- 回执链没能把 job 收尾
|
||||
|
||||
判断是否真的生效,应看:
|
||||
|
||||
- API 日志里有没有来自 `121.204.244.188` 的 heartbeat/pull
|
||||
- handover 里 `agent_hostname / agent_ip` 是否已刷新
|
||||
|
||||
### 4. 不要再回展示层
|
||||
|
||||
当前最值钱的推进方向仍然是:
|
||||
|
||||
- controller 真机吞吐
|
||||
- claim/running/completed 推进
|
||||
- worker 恢复
|
||||
|
||||
不要把额度再耗在页面、文案、展示层结构上。
|
||||
|
||||
## 这轮动过的关键文件
|
||||
|
||||
最关键的源码文件:
|
||||
|
||||
- [domain-api/app/node_agent.py](/www/wwwroot/getDomain/domain-api/app/node_agent.py)
|
||||
- [domain-api/app/services/ops_release_service.py](/www/wwwroot/getDomain/domain-api/app/services/ops_release_service.py)
|
||||
- [domain-api/tests/test_node_agent_delivery_queue.py](/www/wwwroot/getDomain/domain-api/tests/test_node_agent_delivery_queue.py)
|
||||
|
||||
此外,工作区还存在大量其他未提交改动,不要随意回滚:
|
||||
|
||||
- `domain-api/`
|
||||
- `domain-web/`
|
||||
- `domainCheck/`
|
||||
- `docs/ops_center_runtime/`
|
||||
|
||||
这是一个脏工作区,接手时必须小心,不要用破坏性 git 命令。
|
||||
|
||||
## 建议下一个 Codex 只做的两件事
|
||||
|
||||
### 任务 1:恢复 mainland-worker-01
|
||||
|
||||
目标:
|
||||
|
||||
- 让 `mainland-worker-01` 从“agent 在线但不参与检测”恢复到可执行检测
|
||||
|
||||
先查方向:
|
||||
|
||||
- 为什么它在本地检测态里访问 `127.0.0.1:5432` 失败
|
||||
- 是本机 PostgreSQL 没启
|
||||
- 还是 worker 的运行配置仍然错误指向本地 DB
|
||||
- 还是它本来就不该走本地 PostgreSQL,而应走远端/统一控制链
|
||||
|
||||
验收标准:
|
||||
|
||||
- `mainland-worker-01.detect_runtime.worker_online = true`
|
||||
- `mainland-worker-01.detect_runtime.active_threads > 0`
|
||||
- 能稳定进入参与节点
|
||||
|
||||
### 任务 2:继续抬 mainland-controller-01 真吞吐
|
||||
|
||||
目标:
|
||||
|
||||
- 只盯真 controller 的任务推进链,不碰页面
|
||||
|
||||
重点看:
|
||||
|
||||
- `claimed -> running -> completed` 是否持续推进
|
||||
- `items_claimed` 是否能下降
|
||||
- `processed_recent / processed_per_minute` 是否能抬起来
|
||||
- `active_threads` 与 `completed` 是否成正相关
|
||||
|
||||
验收标准:
|
||||
|
||||
- controller 持续有真实 completed 增长
|
||||
- running/claimed 更贴近“持续补位”而不是堆积
|
||||
- 机器性能使用能继续往上抬
|
||||
|
||||
## 可直接复用的验证点
|
||||
|
||||
### 1. 看节点 handover
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /api/v1/ops/nodes/mainland-controller-01/handover`
|
||||
|
||||
这一项已经能确认:
|
||||
|
||||
- 当前是不是对到了真 controller
|
||||
- agent/ip/hostname 是否正确
|
||||
- 当前 load / detect_runtime 是否在动
|
||||
|
||||
### 2. 看节点列表
|
||||
|
||||
接口:
|
||||
|
||||
- `GET /api/v1/ops/nodes`
|
||||
|
||||
重点字段:
|
||||
|
||||
- `agent_hostname`
|
||||
- `agent_ip`
|
||||
- `cluster_hostname`
|
||||
- `cluster_ip`
|
||||
- `detect_runtime.active_threads`
|
||||
- `current_load`
|
||||
|
||||
### 3. 看海外控制面 API 日志
|
||||
|
||||
已验证有效:
|
||||
|
||||
- `journalctl -u domaincheck-api -n 200 --no-pager`
|
||||
|
||||
尤其可以筛:
|
||||
|
||||
- `121.204.244.188`
|
||||
- `/api/v1/ops/agent/heartbeat`
|
||||
- `/api/v1/ops/agent/pull`
|
||||
|
||||
### 4. 发布 controller 的正确方式
|
||||
|
||||
推荐继续使用运行中的 API HTTP 接口,而不是本地 Python service 调用。
|
||||
|
||||
## 交接结论
|
||||
|
||||
这一轮真正解决掉的,不是“性能问题”本身,而是它前面最大的认知阻塞:
|
||||
|
||||
- 之前一直有一部分判断建立在“本机就是 controller”这个错误前提上
|
||||
- 现在这个前提已经纠正
|
||||
- 真 controller 已经重新纳入控制面,而且身份上报正确、远端发布成功、线程也已经真实抬起来
|
||||
|
||||
接手人从这里继续时,应该把主线收缩成一句话:
|
||||
|
||||
“只盯 `mainland-controller-01` 的真实吞吐推进,并恢复 `mainland-worker-01`,不要再回展示层,也不要再把海外 12 核测试机当成主算力机。”
|
||||
Reference in New Issue
Block a user