| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
[refractor] NodeManager代码微重构,提升代码可维护性 Co-authored-by: 吕有辉<lvyouhui@huawei.com> # message auto-generated for no-merge-commit merge: !620 merge refractor/node_manager into master [refractor] NodeManager代码微重构,提升代码可维护性 Created-by: codeDogPro Commit-by: 吕有辉 Merged-by: tobking Description: ## 1. 合入背景 ISSUE:https://gitcode.com/Ascend/MindIE-Motor/issues/368 ## 2. 修改内容 ### 2.1 Service Registry 重构 **文件**: motor/node_manager/core/services/registry.py 1. 用 typing.cast 消除 get_preparable() 中的 # type: ignore[arg-type,return-value]。 2. _MODULE_MAP 从模块级静态 dict 迁移到 _ServiceRegistry 实例属性,新增 add_discovery_path() API 支持动态注册后端模块路径。 3. 新增 get_active_sorted() 方法,将排序逻辑从 daemon.py 内联哨兵模式收归 registry。 4. _ServiceRegistration.is_active 改为 _is_active(active_backends) 方法,接受解析后的后端列表参数。 5. discover() 支持逗号分隔的多后端服务列表(如 "engine,memcache"),替代原来的单值匹配。 6. 删除 __slots__ 限制,2-3 个实例的内存节省可忽略。 7. 新增 tests/node_manager/core/services/test_registry.py,20 个单元测试覆盖 register、get_active 过滤、get_preparable 排序、discover 模块导入、重复注册、add_discovery_path、线程安全。 ### 2.2 服务协议提取 **文件**: motor/node_manager/core/services/protocols.py(新增) 1. DaemonService 和 PreparableService 从 registry.py 提取到 services/protocols.py。 2. daemon.py 直接从 protocols.py 导入,Protocol 定义与注册中心解耦。 ### 2.3 memcache LocalService 重构 **文件**: motor/node_manager/core/services/memcache/(新增目录) 1. **目录拆分**: services/memcache/ __init__.py worker.py ← LocalService 子进程入口(DistributedObjectStore().init(0)) lifecycle.py ← daemon 侧生命周期管理(@register_service、pull/stop/health_check) 2. **拉起方式改进**:pull() 使用 sys.executable -m motor.node_manager.core.services.memcache.worker 替代内联 -c 字符串,取消 PYTHON_EXEC_PATH 依赖。保持 subprocess.Popen(env=...) 确保与 Engine 子进程的 MMC_LOCAL_CONFIG_PATH 隔离。 3. **消除重复检查**:提取 _can_launch property,统一 should_launch() / pull() / prepare() 中的 enable、backend、mode 条件判断。 4. **简化 mark_dead()**:用 poll() 替代 wait(timeout=0) + 三重异常捕获。 5. **移除未使用的 _endpoints_count**:仅在日志中使用,改为局部变量。 ### 2.4 Engine 解耦 —— 服务配置化 **文件**: motor/node_manager/core/services/engine.py, motor/node_manager/core/daemon.py, motor/node_manager/main.py, motor/config/node_manager.py 1. @register_service(SERVICE_ENGINE) 新增 backend="engine",从 backend=None(始终激活)改为按配置激活。 2. KVCacheStoreConfig 新增 mode 字段("combined" / "separated"),通过 user_config.json 控制: json // Engine + KV 合体 Pod(默认) { "kv_cache_store_config": { "backend": "memcache", "mode": "combined" } } // KV 分离 Pod(只起 LocalService,不拉 Engine,不注册/心跳) { "kv_cache_store_config": { "backend": "memcache", "mode": "separated" } } // Engine only Pod {} 3. Daemon 新增 has_engine 属性,main.py 据此条件初始化 EngineManager / HeartbeatManager。 ### 2.5 main.py 重构 —— Application 基类 **文件**: motor/common/app/application.py(新增), motor/node_manager/node_manager.py(新增), motor/node_manager/main.py 1. **Application 基类** — 封装四个组件共享的 boilerplate: - 模块管理(add_module / get_module / stop_all_modules) - 配置热更新传播(on_config_updated → 先刷新自身间隔 _refresh_check_interval,再传播给所有带 update_config 的模块) - 可配置的 daemon loop 间隔(check_interval 参数,默认 1s,子类从 config 读取) - 信号处理(SIGINT / SIGTERM → threading.Event) - select-based daemon loop(stdin 读取 + stop_event.wait) - run() 模板方法:banner → init_modules → start_modules → config_watcher → daemon_loop → shutdown 2. **NodeManager(Application)**: - __init__ 传入 check_interval=config.basic_config.daemon_loop_interval(默认 5s,可在 user_config.json 中配置) - _refresh_check_interval():配置热更新时同步刷新间隔 - init_modules():根据 daemon.has_engine 动态注册模块 - _on_daemon_tick():每 tick 检查 HeartbeatManager 自杀标志 - exit_code:自杀时返回 -1(pod rescheduling) 3. **main.py 瘦身**:从 186 行 → 37 行 thin wrapper: python def main() -> int: config = NodeManagerConfig.from_json() reconfigure_logging(config.logging_config) run_port_setup_or_exit(apply_node_manager_ports, config) nm = NodeManager(config) return nm.run() 删除所有模块级全局变量(modules、_should_exit、config、config_watcher)和 7 个模块级函数。 ### 2.6 测试重构 1. 测试目录镜像源码结构: tests/node_manager/ __init__.py conftest.py test_config.py core/ __init__.py test_daemon.py test_engine_manager.py test_heartbeat_manager.py test_fault_reporter.py test_api_ready_event.py services/ __init__.py test_registry.py memcache/ __init__.py test_lifecycle.py 2. test_main_process_title.py 从 NodeManager 和 EngineServer 各一份合并为 tests/common/utils/test_process_title.py,NodeManager 用例适配新 NodeManager 类 API。 ### 2.7 改动文件清单 | 文件 | 改动类型 | |------|----------| | motor/common/app/__init__.py | 新增 | | motor/common/app/application.py | 新增 | | motor/node_manager/node_manager.py | 新增 | | motor/node_manager/core/services/protocols.py | 新增 | | motor/node_manager/core/services/memcache/__init__.py | 新增 | | motor/node_manager/core/services/memcache/worker.py | 新增 | | motor/node_manager/core/services/memcache/lifecycle.py | 重命名自 local_service.py | | tests/node_manager/core/__init__.py | 新增 | | tests/node_manager/core/services/__init__.py | 新增 | | tests/node_manager/core/services/memcache/__init__.py | 新增 | | tests/node_manager/core/services/test_registry.py | 新增 | | tests/node_manager/core/services/memcache/test_lifecycle.py | 重命名 | | tests/common/utils/test_process_title.py | 合并自两份拷贝 | | motor/node_manager/core/services/registry.py | 重构 | | motor/node_manager/core/daemon.py | 重构 | | motor/node_manager/core/services/engine.py | 改动 | | motor/node_manager/main.py | 瘦身 | | motor/config/node_manager.py | 改动 | | motor/node_manager/core/__init__.py | 删除多余版权声明 | | motor/node_manager/core/services/__init__.py | 删除多余版权声明 | | motor/node_manager/__init__.py | 删除多余版权声明 | ## 3. 资料变更 不涉及。 ## 4. 接口变更 不涉及(所有改动为内部重构,对外接口不变)。 ## 5. 测试结果 python -m pytest tests/node_manager/ tests/common/utils/test_process_title.py tests/engine_server/ -q 529 passed in 1.47s 测试覆盖: - **registry**:注册、过滤、排序、发现、线程安全(20 个用例) - **memcache lifecycle**:should_launch、prepare、pull、stop、health_check(11 个用例) - **daemon**:engine pull、参数校验、D2D peer、signal handler(13 个用例) - **heartbeat manager**:状态上报、端点管理、自杀检测 - **engine manager**:注册、re-register、ranktable、snapshot - **config**:配置解析、验证、热加载 - **process_title**:NodeManager + EngineServer 标题设置(4 个用例) ## 6. CheckList - [x] 代码注释完备 - [x] 正确记录维测日志 - [x] 是否有UT用例(新增 24 个用例,全量 529 passed) - [x] 若涉及多线程场景,考虑了并发场景,不存在死锁问题 - _ServiceRegistry 使用 threading.Lock 保护 _registrations 和 _module_map - daemon.py 和 engine.py 的锁均为短临界区非嵌套使用 - Application 的信号处理仅设 threading.Event,清理在主线程执行 See merge request: Ascend/MindIE-Motor!620 | 22 天前 | |
[Feature] 引擎重启兜底策略:容器不重启重拉引擎 + 自杀裁决收敛 Daemon Co-authored-by: 吕有辉<lvyouhui@huawei.com> # message auto-generated for no-merge-commit merge: !710 merge feature/engine_relaunch into master [Feature] 引擎重启兜底策略:容器不重启重拉引擎 + 自杀裁决收敛 Daemon Created-by: codeDogPro Commit-by: 吕有辉 Merged-by: tobking Description: ### 1. 合入背景 引擎进程死亡后,当前唯一兜底是「NodeManager 心跳 abnormal ×5(约 15s)→ 自杀 → k8s 重启 pod」,代价是全容器重建。本 PR 实现「容器不重启重拉引擎」:Controller 协同实例的**所有** NodeManager 一起重拉全部引擎(rank 集合通信组一致性),重拉失败再回退到容器重启;同时把自杀裁决权收敛到 Daemon(进程生命周期管理者)。 本 PR 基于最新主线:原生引擎拉起(#698:NodeManager 直接 spawn 原生 vLLM/SGLang,删除 EngineServer 层)与 EngineServer 删除收尾(#717)均已合入,重拉链路已对接原生拉起路径。 关联 ISSUE:#472 ### 2. 修改内容 1. **Controller 引擎重启兜底策略**( motor/controller/fault_tolerance/strategy/engine_relaunch.py,两阶段): - Phase 1 重启引擎:探活实例全部 NodeManager(任一不可达 → 直接 Phase 2,保证 rank 一致性)→ 逐个下发 POST /node-manager/engine-restart → 轮询 /node-manager/status 全部 NORMAL → 成功 - Phase 2 重启容器:仅对**未成功派发重启**的 NM 发 abort 解冻自杀(已派发 NM 保持 freeze,避免打断正在恢复的引擎)→ 心跳机制触发容器重启(k8s);abort 发送失败补 error 日志指名 NM;实例 DELETED/消失提前结束 - stop() 被打断时 fire-and-forget 发 abort(防冻结窗口内 NM 自杀被延迟) 2. **策略失败升级机制(可复用)**:StrategyBase.mark_failed() + InstanceMetadata.prev_strategy_failed——token 重推/UCE/弹性扩缩容等「期望引擎不重启快速恢复」的策略失败后,策略中心自动降级到 EngineRelaunchStrategy(兜底链);升级分支同样受 enable_engine_relaunch 门控 3. **level2_strategy 挂钩**:ENGINE_DEAD 软件故障直接触发 EngineRelaunchStrategy(开关关闭 → None) 4. **NodeManager 新路由** POST /node-manager/engine-restart(node_manager_api.py):body {"action": "restart"|"abort", "instance_id"};**薄路由**——restart 整体委托 Daemon.restart_engine(Daemon 内部解析启动参数、冻结自杀、暂停/恢复 FaultReporter、只停引擎服务重拉不动 KV store/监控线程),路由只做 HTTP 语义映射(并发 409、快照恢复中 409、未 start 400、失败 500,零锁零状态)。部分失联协同**复用既有 /node-manager/stop**(不在 restart 路由新加 shutdown action) 5. **自杀冻结**(daemon.py):截止时间制 freeze/unfreeze(abort 丢失自动过期,容器重启兜底永存);**freeze 仅在死亡上报成功后才执行**(上报失败 → Controller 不可达 → 不冻结,仲裁继续计数,容器重启兜底不被拖死);**freeze 受 enable_engine_relaunch 门控**(未开开关 → 不上报后不冻结,pod 快速自终止走 k8s 容器重启,不被 180s 冻结窗口拖延) 6. **FaultReporter 重拉编排**:重拉期间由 Daemon 暂停轮询(引擎被杀期间 FT 端口不可达,poll 失败会被误报死亡),重拉成功后 Daemon 恢复轮询并清空轮询状态(新引擎自然重新起算启动宽限);另修复首次 poll 失败即冻结自杀的时序竞态(自杀 ~15s vs DEAD 上报 15-30s) 7. **自杀裁决收敛 Daemon**:心跳模块收敛为状态源(has_abnormal_endpoints/endpoints_generation/is_within_grace_period),Daemon 独立 3s 仲裁线程统一裁决(5×3s≈15s,间隔跟随 heartbeat_interval_seconds 配置)+ 统一 freeze/should_suicide;**冷启动误判门槛**:只有「曾经 NORMAL」的 endpoint 异常才上报死亡(模型加载期不误报) 8. **引擎就绪等待下沉**:_engine_ready 握手事件 + Daemon 后台 wait_ready——heartbeat 不再感知引擎就绪、不再 import Daemon(循环依赖解除),状态线程在探测前等待握手 9. **改名**:EngineManager → RegisterManager(register_manager.py,职责=注册/元数据管理,与 Daemon 进程管理区分) 10. **结构**:StrategyBase 移至 strategy/base.py(消除循环 import) 11. **配置收敛(唯一开关)**:enable_engine_relaunch(默认开)为「容器不重启重拉引擎」唯一开关——Controller 侧 fault_tolerance_config(策略选择门控)+ NodeManager 侧 fault_tolerance_config(同名配置,freeze 门控);**删除 MOTOR_RESTART_ENGINE 环境变量及 health_check 的 SIGTERM 自杀快路径**,恢复路径统一由 Daemon 按开关决策 12. **重拉日志分隔标记**:旧引擎 stop 后、新引擎 pull 前,print 直达容器 stdout 打印 [ENGINE RELAUNCH #N] banner(容器生命周期内重拉计数 + 时间),多次重拉日志按次分区检索 13. **原生拉起适配(rebase #698/#717)**:重拉/就绪握手/死亡上报链路对接 native_engine;心跳经 RuntimeState probe 查询(STARTING/STOPPING 保留原状态);进程组清理(start_new_session + killpg + 退出等待 + 僵尸收割);bootstrap_port 字段;过时 EngineServer 提法清理 ### 3. 资料变更 涉及,同步更新: - docs/zh/developer_guide/components/node_manager.md:模块表(RegisterManager)、自杀裁决章节 - docs/zh/design/fault_tolerance/overview.md / fault_manager.md:策略/模块引用同步 - examples/features/config_sample.json:两侧 fault_tolerance_config 新字段 - skill refs(nodeman.md、controller.md、code-style.md)——nodeman.md 已随 #717 文档清理合并 ### 4. 接口变更 涉及(NodeManager 管理面接口 + 配置): - 新增 POST /node-manager/engine-restart:body {"action": "restart"|"abort", "instance_id": int?},200/400/409/500 - /node-manager/stop 语义补全为「自杀」:停引擎后延时 SIGTERM 自身(退出 -1 → k8s 重启 pod)。部分失联时 Controller 对存活 NM 复用该接口下发自杀(不再依赖引擎重拉路由) - 新增 Controller 配置 fault_tolerance_config.enable_engine_relaunch(默认 true)/ engine_relaunch_complete_timeout_sec / engine_relaunch_poll_interval_sec / engine_relaunch_dispatch_retries / engine_relaunch_nm_unreachable_threshold - 新增 NodeManager 配置 fault_tolerance_config.enable_engine_relaunch(默认 true,与 Controller 对齐)/ engine_restart_wait_timeout_sec(180s)/ engine_restart_freeze_sec(720s) - **删除** MOTOR_RESTART_ENGINE 环境变量(配置收敛,统一由 enable_engine_relaunch 决策) ### 5. 测试结果 **单测**(bash tests/run_tests.sh 全量):**2665 passed**(rebase #698/#717 后全量基线之上) 新增/重写测试覆盖: - EngineRelaunchStrategy:Phase 1 探活全部 NM / 探活失败直接 Phase 2 / 下发全部 NM / 轮询成功 / NM 连续不可达进 Phase 2 / 超时 abort / 实例消失提前结束 / event 打断 / Phase 2 只 abort 未派发 NM - 策略失败升级:StrategyBase 失败标记 / 策略中心 prev_strategy_failed 置位与清除 / 升级选择 EngineRelaunchStrategy - level2_strategy 挂钩:ENGINE_DEAD → EngineRelaunchStrategy、开关关闭 → None、UNHEALTHY → None - engine-restart 路由:restart/abort/400/409(并发、快照恢复中)/500(pull 失败且解冻) - /node-manager/stop 自杀语义:停引擎 + 延时 SIGTERM 自身(pod 重启) - 自杀冻结与裁决:freeze 清零复位 / 冻结期不计数 / 解冻恢复 / 冻结到期自动恢复 / generation 变化重置 / grace 期跳过 / 阈值触发 / **上报失败不冻结** / **未开 enable_engine_relaunch 不冻结** - 引擎死亡双信号源:PID 死亡事件(monitor health_check 返回死亡列表 + pid 去重)/ 心跳 ABNORMAL 上报(ep 去重 + 恢复清除 + 失败重试) - FaultReporter:回归纯 FT 状态上报 - 心跳状态源:has_abnormal_endpoints / abnormal_endpoint_ids / endpoints_generation / grace getter - daemon:restart_engine 只动引擎服务(KV/监控线程不动)/ 裁决(阈值、重置条件、冻结窗口与过期)/ PID 死亡上报去重与失败重试 / 冷启动不误报、曾 NORMAL 后异常上报 / 重拉分隔标记(计数 + banner 打印) - RegisterManager:master_dp_ip/role 持久化、get_restart_params - 配置:两侧新字段默认值与校验 **静态检查**:pylint / ruff 通过 **K8s e2e(infer195,A2 跨机 EP16 = 2 pod × 8 卡)**:已验证通过—— - 杀 vLLM 进程 → 双信号检测 → EngineRelaunchStrategy 协同两个 pod 一起重拉全部引擎 → 实例回 ACTIVE - 杀 NodeManager 进程 → 实例稳定 INACTIVE(无 ACTIVE↔INACTIVE 震荡)→ volcano podgroup 自动重启两个 pod(既有机制)→ 新实例组装恢复 - 冷启动加载期(30B 模型 > grace 120s)不再被误判死亡:新增「曾 NORMAL 门槛」——e2e 对比:修复前加载期多条误报触发重拉打断加载(恶性循环),修复后误报 0、实例平滑 ACTIVE e2e 定位并修复的问题: - engine-restart 路由快照恢复检查条件错误(非快照部署误拒 409) - EngineServer SIGKILL 后子进程孤儿残留(占旧 ZMQ 端口 → 重拉引擎握手 5 分钟超时)——进程组管理修复(start_new_session + killpg + 退出等待 + 僵尸收割) - 重拉与 FaultReporter 解耦:PID 级检测 + 心跳 ABNORMAL 双信号源,不依赖 FT 配置 - 部分节点失联防震荡:转 ACTIVE 加心跳新鲜度门槛 + 存活 NM 经既有 /node-manager/stop(自杀语义)协同退出(volcano 兜底) - daemon 职责收敛:engine 重启生命周期(stop/分隔标记/pull)下沉 EngineService.restart,daemon 回归 service 无关的编排者 ### 6. CheckList - [x] 代码注释完备 - [x] 正确记录维测日志(错误场景 error_window 防刷屏) See merge request: Ascend/MindIE-Motor!710 | 3 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 22 天前 | ||
| 3 天前 |