# 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 核测试机当成主算力机。”