已合并
【docs】设计文档优化:扩缩容文档合一为 scaling.md、主备倒换设计文档归位 #679
【docs】设计文档优化:扩缩容文档合一为 scaling.md、主备倒换设计文档归位 #679
已合并
jason lyu创建于 8月6日
5 个文件变更+150-237
@@ -1,13 +1,13 @@
1nav:1nav:
2 - 首页: index.md2 - 首页: index.md
3 - 架构: architecture.md3 - 架构: architecture.md
4- - 版本说明: release_note.md4+ - 版本说明: release_note_motor.md
5 - 用户指南:5 - 用户指南:
6 - 简介: user_guide/README.md6 - 简介: user_guide/README.md
7 - 用户手册目录: user_guide/menu_user_manual.md7 - 用户手册目录: user_guide/menu_user_manual.md
8 - 快速开始: user_guide/quick_start_motor.md8 - 快速开始: user_guide/quick_start_motor.md
9 - 环境准备: user_guide/environment_preparation.md9 - 环境准备: user_guide/environment_preparation.md
10- - 通信矩阵: user_guide/communication_matrix.md10+ - 通信矩阵: user_guide/communication_matrix_motor.md
11 - 服务部署:11 - 服务部署:
12 - 部署概述: user_guide/deployment/README.md12 - 部署概述: user_guide/deployment/README.md
13 - K8s 部署:13 - K8s 部署:
@@ -34,7 +34,6 @@ nav:
34 - MemCache 后端: user_guide/features/kv_cache_store/backend/memcache.md34 - MemCache 后端: user_guide/features/kv_cache_store/backend/memcache.md
35 - Mooncake 后端: user_guide/features/kv_cache_store/backend/mooncake.md35 - Mooncake 后端: user_guide/features/kv_cache_store/backend/mooncake.md
36 - Yuanrong 后端: user_guide/features/kv_cache_store/backend/yuanrong.md36 - Yuanrong 后端: user_guide/features/kv_cache_store/backend/yuanrong.md
37- - 自动弹性扩缩容: user_guide/features/auto_scaling.md
38 - 手动扩缩容: user_guide/features/manual_scaling.md37 - 手动扩缩容: user_guide/features/manual_scaling.md
39 - 主备倒换: user_guide/features/fault_tolerance/standby.md38 - 主备倒换: user_guide/features/fault_tolerance/standby.md
40 - 链路追踪: user_guide/features/tracing.md39 - 链路追踪: user_guide/features/tracing.md
@@ -62,10 +61,10 @@ nav:
62 - 能力总览: design/fault_tolerance/overview.md61 - 能力总览: design/fault_tolerance/overview.md
63 - ScaleP2D 故障恢复: design/fault_tolerance/scale_p2d.md62 - ScaleP2D 故障恢复: design/fault_tolerance/scale_p2d.md
64 - FaultManager 设计: design/fault_tolerance/fault_manager.md63 - FaultManager 设计: design/fault_tolerance/fault_manager.md
65- - 主备倒换: design/standby.md64+ - 主备倒换: design/fault_tolerance/standby.md
66 - 精度检测: design/fault_tolerance/precision_detection.md65 - 精度检测: design/fault_tolerance/precision_detection.md
67 - KV Conductor: design/kv_conductor.md66 - KV Conductor: design/kv_conductor.md
68- - 手动扩缩容: design/manual_scaling.md67+ - 扩缩容: design/scaling.md
69 - CRD 部署设计: design/crd_deployment.md68 - CRD 部署设计: design/crd_deployment.md
70 - Metrics 可观测性: design/metrics.md69 - Metrics 可观测性: design/metrics.md
71 - 熔断设计: design/circuit_breaker_design.md70 - 熔断设计: design/circuit_breaker_design.md
Rdocs/zh/design/standby.mddocs/zh/design/fault_tolerance/standby.md+2-2
@@ -28,7 +28,7 @@ Controller 和 Coordinator 使用独立的 ETCD 锁键(自动加上 `/controll
28**选举流程:**28**选举流程:**
29 29 
30<p align="center">30<p align="center">
31- <img alt="主备倒换架构" src="../imgs/standby.png" />31+ <img alt="主备倒换架构" src="../../imgs/standby.png" />
32</p>32</p>
33 33 
34<p align="center"><b>图1. 主备倒换架构图</b></p>34<p align="center"><b>图1. 主备倒换架构图</b></p>
@@ -108,7 +108,7 @@ Coordinator 的 `/readiness` 不只是"我是 master 吗",它综合判断以
108## 3. 主备倒换时序108## 3. 主备倒换时序
109 109 
110<p align="center">110<p align="center">
111- <img alt="主备倒换时序图" src="../imgs/standby-seq.jpg" />111+ <img alt="主备倒换时序图" src="../../imgs/standby-seq.jpg" />
112</p>112</p>
113 113 
114<p align="center"><b>图2. 主备倒换时序图</b></p>114<p align="center"><b>图2. 主备倒换时序图</b></p>
@@ -1,100 +0,0 @@
1-# 实例级手动扩缩容设计说明(MindIE Motor)
2- 
3-本文档描述 **`examples/deployer/deploy.py` 在传入 `--update_instance_num` 时的行为**,以及与集群 ConfigMap、YAML 产物的对应关系。表述均来自仓库内上述脚本及其依赖模块的实现,不包含脚本未实现的保证。
4- 
5-## 1. 入口与前置校验
6- 
7-- **入口**`deploy.py``main()` 在解析到 `--update_instance_num` 时调用 `handle_update_instance_num(user_config)`,随后 `return`,不会走全量 `deploy_services()`
8-- **通用前置**:在分支判断之前,`main()` 会执行 `validate_instance_nums(user_config)``lib/generator/engine.py`)。`motor_deploy_config` 中的 `p_instances_num``d_instances_num`(PD 分离)或 `hybrid_instances_num`(PD 混部)校验链条为:
9- - 字段缺失:内部调用的 `obtain_engine_instance_total``lib/utils.py`)抛 `KeyError`,文案 `p_instances_num is required in motor_deploy_config` / `d_instances_num is required ...`
10- - 非整数:`obtain_engine_instance_total``int(...)` 失败时抛 `ValueError`,文案 `p_instances_num and d_instances_num must be integers`
11- - 越界:`validate_instance_nums` 自身抛 `ValueError`,要求 `> INSTANCE_NUM_ZERO`(0)且 `<= INSTANCE_NUM_MAX`(16),常量定义在 `lib/constant.py`,文案形如 `p_instances_num must be greater than 0` / `must not exceed 16`
12- 
13-## 2. 基线:集群 ConfigMap `motor-config`
14- 
15-- **读取**`handle_update_instance_num` 调用 `get_baseline_config_from_configmap(deploy_config["job_id"])``lib/generator/k8s_utils.py`)。
16-- **命令**`kubectl get configmap motor-config -n <job_id> -o json``MOTOR_CONFIG_CONFIGMAP_NAME``motor-config``job_id` 来自当前输入 `user_config``motor_deploy_config.job_id`,用作 namespace)。
17-- **解析**:从返回 JSON 的 `data["user_config.json"]` 取出字符串,再 `json.loads` 为 dict;若命令失败、JSON 非法、缺 `data` 或缺 `user_config.json` 键,函数返回 `None`
18-- **缺失时**:若基线为 `None`,抛出 `FileNotFoundError``ConfigMap motor-config not found. Please deploy once before scaling.`
19- 
20-## 3. 仅允许修改实例数:`validate_only_instance_changed`
21- 
22-- **实现**`lib/config_validator.py``validate_only_instance_changed(current_config, baseline_config)`
23-- **逻辑**:对两份配置各做一次深拷贝,并从 `motor_deploy_config` 中移除 `p_instances_num``d_instances_num` 后比较整份 dict;若不等则抛出 `ValueError``user_config changes detected beyond instance numbers. Only p_instances_num/d_instances_num can be modified for scaling.`
24- 
25-## 4. 部署模式以集群基线为准
26- 
27-- **取值**`deploy_mode_arg = baseline_deploy.get("deploy_mode", DEPLOY_MODE_INFER_SERVICE_SET)``lib/constant.py``DEPLOY_MODE_INFER_SERVICE_SET``"infer_service_set"`)。
28-- **校验**`validate_deploy_mode_value(deploy_mode_arg)` 要求该值属于 `VALID_DEPLOY_MODES``infer_service_set``multi_deployment``single_container`;非法则 `ValueError`,文案含 `Baseline config has invalid deploy_mode`
29- 
30-后续 YAML 生成与 `kubectl` 行为按 `deploy_mode_arg` 分支(见下节)。**注意**:扩缩容分支里读取的是基线里的 `deploy_mode`,不是仅凭当前本地 `user_config` 决定。
31- 
32-## 5. 刷新 ConfigMap 与 `kubectl` 总入口
33- 
34-- **统一行为**`handle_update_instance_num` 末尾调用 `exec_all_kubectl_multi(deploy_config, baseline_config, deploy_mode_arg)``lib/generator/k8s_utils.py`)。
35-- **ConfigMap**`exec_all_kubectl_multi` **首先**调用 `create_motor_config_configmap(job_id)`:用当前进程内已设置的 `g_user_config_path`(由 `main()``set_user_config_path(user_config_path)` 设置,对应本次命令行解析出的 `user_config.json` 路径)与 `startup/``probe/` 等文件组装 `kubectl create configmap motor-config ... --from-file=user_config.json=<路径> -n <job_id>`,经 `apply_configmap` 以 client dry-run 管道到 `kubectl apply`
36-- **因此**:每次成功的扩缩容执行都会用**本次输入的** `user_config.json` 文件内容更新集群中的 `motor-config`(与是否走 engine 逐文件扩缩无关)。
37- 
38-## 6. 两种部署模式下的扩缩容行为
39- 
40-### 6.1 `infer_service_set`
41- 
42-- **YAML 路径**`get_deploy_paths()` 将 InferServiceSet 输出定为 `os.path.join(OUTPUT_ROOT_PATH, "infer_service.yaml")`,其中 `OUTPUT_ROOT_PATH``./output_yamls``lib/constant.py`),路径相对于在 `examples/deployer` 下执行脚本时的当前工作目录。
43-- **若 `./output_yamls/infer_service.yaml` 已存在**:调用 `update_infer_service_replicas_only(infer_output, deploy_config)``lib/generator/infer_service.py`):加载该 YAML,定位 `kind: InferServiceSet` 文档,将角色名为 `prefill` / `decode` 的 role 的顶层 `replicas` 分别设为当前 `deploy_config``p_instances_num``d_instances_num`(由 `obtain_engine_instance_total` 读出),写回同一文件,并把该路径追加到 `g_generate_yaml_list`
44-- **若不存在**:先 `init_service_domain_name(paths, deploy_config)` 初始化服务域名;再校验模板文件 `infer_service_input_yaml`(即 `./yaml_template/infer_service_template.yaml`)是否存在,**不存在则抛 `FileNotFoundError`:`InferServiceSet template yaml not found: <path>.`**;通过后调用 `init_infer_service_domain_name(infer_input, deploy_config)``generate_yaml_infer_service_set(infer_input, infer_output, user_config)` 全量生成该文件并加入 `g_generate_yaml_list`(与首次生成 InferServiceSet 流程一致)。
45-- **`kubectl`**`exec_all_kubectl_multi``baseline_config is not None``deploy_mode_arg == infer_service_set` 时,对 `g_generate_yaml_list`**每一个**文件执行 `kubectl apply -f <file> -n <job_id>`。扩缩容场景下列表通常仅含 `infer_service.yaml` 一项。
46- 
47-### 6.2 `multi_deployment`(及非 `infer_service_set` 时进入的 else 分支)
48- 
49-- **YAML 生成**:调用 `generate_yaml_engine(engine_input_yaml, engine_output_yaml, user_config)``lib/generator/engine.py`)。`engine_output_yaml``os.path.join(OUTPUT_ROOT_PATH, g_engine_base_name)`,对每个 `p_index in range(p_total)``d_index in range(d_total)` 写出 `{engine_output_yaml}_p{index}.yaml` / `_d{index}.yaml`,并全部追加到 `g_generate_yaml_list``g_engine_base_name``update_engine_base_name` 按引擎类型设置(如 vLLM 为 `vllm`,见 `SERVER_BASE_NAME_MAP` 等)。
50-- **`kubectl`**`exec_all_kubectl_multi` 在存在 `baseline_config` 且模式非 `infer_service_set` 时,调用 `elastic_distributed_engine_deploy(deploy_config, baseline_deploy_config, OUTPUT_ROOT_PATH)`(实现位于 **`lib/generator/k8s_utils.py`**,与 `engine.py` 中同名函数逻辑一致,实际执行以 `k8s_utils` 为准)。
51-- **缩容**:对 P 或 D,若目标实例数 `<` 基线,从 `index = base-1` 递减到 `total`,对文件 `{OUTPUT_ROOT_PATH}/{g_engine_base_name}_{p|d}{index}.yaml` 执行 `kubectl delete -f`;若文件仍存在则 `os.remove`
52-- **扩容**:若目标 `>` 基线,对 `index in range(base, total)` 的上述路径执行 `kubectl apply -f`
53-- **顺序**:先 `scale_engine_by_type(..., NODE_TYPE_P)`,再 `scale_engine_by_type(..., NODE_TYPE_D)`
54- 
55-**说明**`handle_update_instance_num``deploy_mode``infer_service_set`**不会**对 controller/coordinator 等调用 `generate_yaml_*`;仅生成 engine 多文件并由 `elastic_distributed_engine_deploy` 做增量 apply/delete。与全量部署路径不同。
56- 
57-## 7. 与 `--update_config` 的差异(便于对照)
58- 
59-- **`handle_update_config`**`--update_config`):同样读取 `motor-config` 基线;若当前 `p_instances_num`/`d_instances_num` 与基线不一致,抛出 `ValueError`,提示使用 `--update_instance_num` 做实例扩缩;否则校验 `deploy_mode` 一致及白名单字段,再 `create_motor_config_configmap`**不**执行 `elastic_distributed_engine_deploy` 或 InferServiceSet 的 apply 列表扩缩。
60-- **基线缺失文案**`--update_config` 下为 `ConfigMap motor-config not found or has no user_config in cluster. Please deploy once before updating configmap.`
61- 
62-## 8. 关键符号与辅助函数(源码位置)
63- 
64-| 名称 | 文件 |
65-|------|------|
66-| `handle_update_instance_num` | `examples/deployer/deploy.py` |
67-| `get_baseline_config_from_configmap``run_cmd_get_output``exec_all_kubectl_multi``create_motor_config_configmap``elastic_distributed_engine_deploy``scale_engine_by_type` | `examples/deployer/lib/generator/k8s_utils.py` |
68-| `validate_only_instance_changed``strip_instance_nums``validate_deploy_mode_value` | `examples/deployer/lib/config_validator.py` |
69-| `generate_yaml_engine``validate_instance_nums``update_engine_base_name` | `examples/deployer/lib/generator/engine.py` |
70-| `generate_yaml_infer_service_set``update_infer_service_replicas_only` | `examples/deployer/lib/generator/infer_service.py` |
71-| `obtain_engine_instance_total` | `examples/deployer/lib/utils.py` |
72-| `MOTOR_CONFIG_CONFIGMAP_NAME``OUTPUT_ROOT_PATH``INSTANCE_NUM_MAX` 等 | `examples/deployer/lib/constant.py` |
73- 
74-## 9. 流程图(与代码分支一致)
75- 
76-### 9.1 `--update_instance_num` 主流程
77- 
78-```mermaid
79-flowchart TD
80- A[main: validate_instance_nums] --> B[handle_update_instance_num]
81- B --> C[get_baseline_config_from_configmap]
82- C -->|None| E[FileNotFoundError: motor-config not found]
83- C -->|dict| D[validate_only_instance_changed]
84- D -->|失败| F[ValueError: beyond instance numbers]
85- D -->|通过| G[deploy_mode 来自基线 + validate_deploy_mode_value]
86- G --> H{deploy_mode == infer_service_set?}
87- H -->|是| I[更新或生成 infer_service.yaml]
88- H -->|否| J[generate_yaml_engine]
89- I --> K[exec_all_kubectl_multi]
90- J --> K
91- K --> L[create_motor_config_configmap 后 apply 或 elastic_distributed_engine_deploy]
92-```
93- 
94-## 10. 约束小结(均可在上述函数中逐项核对)
95- 
96-1. 扩缩容前集群中需已有含 `user_config.json``motor-config`(否则 `get_baseline_config_from_configmap` 返回 `None` 并报错)。
97-2.`motor_deploy_config.p_instances_num` / `d_instances_num` 外,整份 `user_config` 与基线须一致(`validate_only_instance_changed`)。
98-3. 实例数合法范围由 `validate_instance_nums` 与常量 `INSTANCE_NUM_ZERO`/`INSTANCE_NUM_MAX` 定义。
99-4. 每次 `exec_all_kubectl_multi` 都会刷新 `motor-config` 为当前命令使用的 `user_config.json` 文件内容。
100-5. 多 Deployment 模式下 engine 产物与扩缩容操作文件位于 **`./output_yamls/`** 下,文件名形如 `{engine_base_name}_p0.yaml`;InferServiceSet 模式下主要产物为 **`./output_yamls/infer_service.yaml`**。
Rdocs/zh/user_guide/features/auto_scaling.mddocs/zh/design/scaling.md+143-129
@@ -1,12 +1,23 @@
1-# 自动弹性扩缩容1+# 实例扩缩容设计文档
yuzechen
yuzechenyuzechen8月7日

删/移了文档但 docs/zh/.nav.yaml 没同步,侧栏会 404:第 37 行还指着已删的 user_guide/features/auto_scaling.md,第 64 行还是 design/standby.md(已移到 fault_tolerance/),第 67 行还是 design/manual_scaling.md(已合并为 scaling.md)。PR 正文写清理与链接同步,nav 漏改的话读者从文档站进不去。

likedislike
2 2 
3-## 特性介绍3+MindIE Motor 提供两种实例扩缩容方式,满足不同运维场景的需求:
4+ 
5+- **自动扩缩容**:依据推理实例的实时负载自动调整 Prefill / Decode 实例数量,适用于负载波动大、追求资源利用率的场景。
6+- **手动扩缩容**:人工修改实例数并执行增量调整,适用于需要确定性变更行为、或集群不具备自动扩缩容条件的场景。
7+ 
8+两种方式互补,分别对应"负载自适应"与"人工决策"两种扩缩容语义。本文档分别介绍两者的设计思路。
9+ 
10+## 1. 自动扩缩容
11+ 
12+### 1.1 特性概述
4 13 
5自动弹性扩缩容功能支持根据推理实例的实时负载自动调整 Prefill 和 Decode 实例数量。当请求量上升时自动扩容,当负载回落时自动缩容,在保障服务 SLA 的同时提升资源利用率。14自动弹性扩缩容功能支持根据推理实例的实时负载自动调整 Prefill 和 Decode 实例数量。当请求量上升时自动扩容,当负载回落时自动缩容,在保障服务 SLA 的同时提升资源利用率。
6 15 
7核心机制:Infer Operator 为推理实例创建 HPA(Horizontal Pod Autoscaler)资源,HPA 通过 External Metrics Adaptor 获取 MindIE Motor 汇聚的引擎级负载指标(如排队请求数、TPS、KV Cache 使用率等),按用户配置的扩缩容阈值自动调整实例副本数。16核心机制:Infer Operator 为推理实例创建 HPA(Horizontal Pod Autoscaler)资源,HPA 通过 External Metrics Adaptor 获取 MindIE Motor 汇聚的引擎级负载指标(如排队请求数、TPS、KV Cache 使用率等),按用户配置的扩缩容阈值自动调整实例副本数。
8 17 
9-## 说明18+支持 Atlas 800I A2 / A3 系列推服务器,前置依赖 Infer Operator 部署与 External Metrics Adaptor(用于将 Motor 指标转换为 Kubernetes External Metrics)。
19+ 
20+### 1.2 工作原理
10 21 
11```text22```text
12┌──────────────────────────────────────────────────────────────────┐23┌──────────────────────────────────────────────────────────────────┐
@@ -32,22 +43,13 @@
323. HPA 从 External Metrics API 获取负载数据,与用户配置的目标阈值对比。433. HPA 从 External Metrics API 获取负载数据,与用户配置的目标阈值对比。
334. 当指标持续超出阈值时,HPA 通知 Infer Operator 增加副本;低于阈值时减少副本。444. 当指标持续超出阈值时,HPA 通知 Infer Operator 增加副本;低于阈值时减少副本。
34 45 
35-## 支持产品型号46+设计要点:**指标聚合与扩缩容决策解耦**。Motor 只负责指标采集、聚合与暴露,扩缩容决策完全交由 HPA 标准机制完成,Motor 不感知扩缩容过程。这使得自动扩缩容能力可以复用 K8s 生态成熟的 HPA 语义(稳定窗口、冷却、多指标最保守决策等),无需自研控制器。
36 47 
37-- Atlas 800I A2 推理服务器48+### 1.3 指标设计
38-- Atlas 800I A3 超节点服务器
39 49 
40-## 前置条件50+#### 1.3.1 指标聚合视图
41 51 
42-- 已完成 Infer Operator 的安装部署。52+Coordinator `/metrics` 端点提供多种聚合视图,通过 `type` 参数切换,满足不同扩缩容粒度:
43-- 已完成 MindIE Motor 推理服务的部署(PD 分离或 PD 混部模式)。
44-- 已部署 External Metrics Adaptor,用于将 MindIE Motor 指标转换为 Kubernetes External Metrics。可直接使用 [mindcluster-deploy 提供的 Metrics Adaptor 示例](https://gitcode.com/Ascend/mindcluster-deploy/tree/master/infer-operator-metrics-adaptor)进行部署。
45- 
46-## 配置 MindIE Motor 暴露 Metrics
47- 
48-MindIE Motor Coordinator 默认通过 `/metrics` 端点暴露聚合后的引擎指标,无需额外配置即可使用。
49- 
50-Coordinator `/metrics` 端点提供多种聚合视图,通过 `type` 参数切换:
51 53 
52| type 值 | 说明 | 适用场景 |54| type 值 | 说明 | 适用场景 |
53|---------|------|---------|55|---------|------|---------|
@@ -64,23 +66,66 @@ curl http://{coordinator-ip}:1027/metrics?type=role&role=prefill
64curl http://{coordinator-ip}:1027/metrics?type=role&role=decode66curl http://{coordinator-ip}:1027/metrics?type=role&role=decode
65```67```
66 68 
67-## 部署 External Metrics Adaptor69+#### 1.3.2 推荐的扩缩容指标
68 70 
69-External Metrics Adaptor 负责将 Coordinator Prometheus 格式指标转换为 Kubernetes External Metrics API,供 HPA 消费71+Prefill 为计算密集型、Decode 为访存密集型,建议分别配置不同扩缩容指标以匹配各自的计算特征
70 72 
71-可使用 [mindcluster-deploy 提供的适配器示例](https://gitcode.com/Ascend/mindcluster-deploy/tree/master/infer-operator-metrics-adaptor) 直接部署,也可按需自行实现。73+**Prefill 扩缩容推荐指标:**
72 74 
73-部署前需确认 Adaptor 配置了正确的 Coordinator metrics 端点地址和抓取间隔。部署完成后,执行以下命令验证指标可用:75+| 指标名 | 类型 | 说明 | 推荐阈值建议 |
76+|--------|------|------|-------------|
77+| `vllm:num_requests_waiting` | Gauge | 等待调度的请求数 | > 5 触发扩容,< 2 触发缩容 |
78+| `vllm:num_requests_running` | Gauge | 当前运行中的请求数 | 视 NPU 规格和模型而定 |
79+| `vllm:kv_cache_usage_perc` | Gauge | KV Cache 使用率(0-1) | > 0.8 触发扩容 |
80+| `motor:prompt_tokens_per_second` | Gauge | Prompt token 处理速率(Motor 计算) | 按 SLA 目标设定 |
81+| `vllm:time_to_first_token_seconds` | Histogram | 首 token 延迟(TTFT) | 按 SLA 目标(如 p95 < 500ms) |
74 82 
75-```bash83+**Decode 扩缩容推荐指标:**
76-kubectl get --raw /apis/external.metrics.k8s.io/v1beta1 | grep -E "num_requests_waiting|motor:generation_tokens_per_second"84+ 
85+| 指标名 | 类型 | 说明 | 推荐阈值建议 |
86+|--------|------|------|-------------|
87+| `vllm:num_requests_waiting` | Gauge | 等待调度的请求数 | > 5 触发扩容 |
88+| `vllm:num_requests_running` | Gauge | 当前运行中的请求数 | 视 NPU 规格和模型而定 |
89+| `motor:generation_tokens_per_second` | Gauge | 生成 token 速率(Motor 计算) | 按 SLA 目标设定 |
90+| `vllm:e2e_request_latency_seconds` | Histogram | 端到端请求延迟 | 按 SLA 目标(如 p95 < 2s) |
91+| `vllm:time_per_output_token_seconds` | Histogram | 跨 token 延迟(TPOT) | 按 SLA 目标(如 p95 < 50ms) |
92+ 
93+> [!NOTE]说明
94+>
95+> - `motor:prompt_tokens_per_second` 和 `motor:generation_tokens_per_second` 是 MindIE Motor Coordinator 计算的服务级指标,基于 vLLM 原始 counter 计算 delta rate 得到,更准确反映实时吞吐。
96+> - Histogram 类型指标(如 `vllm:e2e_request_latency_seconds`)需要在 Adaptor 侧计算分位数(p50/p95/p99)后作为独立指标暴露。
97+> - Gauge 指标直接反映当前负载水平,优先推荐;Counter 类型指标(如 token 总数)不会因 `/metrics` 请求而重置,直接比较数值无法反映速率变化,不建议作为扩缩容依据。
98+ 
99+#### 1.3.3 多指标组合策略
100+ 
101+HPA 支持在同一策略中配置多个指标,并选择**最保守的扩缩容决策**(即当前副本数最接近触发扩容或缩容的指标)——这保证了任一指标触及阈值时都不会被其他指标"掩盖":
102+ 
103+```yaml
104+scalingPolicy:
105+ type: HPA
106+ spec:
107+ minReplicas: 1
108+ maxReplicas: 4
109+ metrics:
110+ - type: External
111+ external:
112+ metric:
113+ name: num_requests_waiting
114+ target:
115+ type: AverageValue
116+ averageValue: "5"
117+ - type: External
118+ external:
119+ metric:
120+ name: kv_cache_usage_perc
121+ target:
122+ type: AverageValue
123+ averageValue: "0.8"
77```124```
78 125 
79-## 配置弹性扩缩容策略126+### 1.4 扩缩容策略配置
80 127 
81-在 `examples/deployer/yaml_template/infer_service_template.yaml` 中,为 Prefill 和 Decode 角色的配置块下添加 `scalingPolicy`128+在 `examples/deployer/yaml_template/infer_service_template.yaml` 中,为 Prefill 和 Decode 角色的配置块下添加 `scalingPolicy`
82- 
83-以下示例为 Prefill 按排队请求数扩缩容,Decode 按生成 token 速率扩缩容:
84 129 
85```yaml130```yaml
86roles:131roles:
@@ -135,7 +180,7 @@ roles:
135 # ... 其余配置保持不变 ...180 # ... 其余配置保持不变 ...
136```181```
137 182 
138-### scalingPolicy 参数说明183+**scalingPolicy 参数说明:**
139 184 
140| 参数 | 说明 | 取值 |185| 参数 | 说明 | 取值 |
141|------|------|------|186|------|------|------|
@@ -158,122 +203,91 @@ roles:
158>203>
159> 本特性依赖 MindCluster Infer Operator 版本 ≥ 26.1.0。用户需按上文示例在 `infer_service_template.yaml` 中手动添加 `scalingPolicy` 配置。204> 本特性依赖 MindCluster Infer Operator 版本 ≥ 26.1.0。用户需按上文示例在 `infer_service_template.yaml` 中手动添加 `scalingPolicy` 配置。
160 205 
161-## 推荐的扩缩容指标206+### 1.5 关键设计考虑
yuzechen
yuzechenyuzechen8月7日

原 auto_scaling.md 用户手册删掉后,部署 External Metrics Adaptor 和验证扩缩容效果(查 HPA、压测触发扩容、验证缩容)整段没了;scaling.md 只保留了设计向内容。如果 nav 还保留自动弹性扩缩容入口,用户会找不到操作步骤。建议要么保留 user_guide 操作篇,要么 nav 改指 design/scaling.md 并补回验证章节。

likedislike
162 207 
163-MindIE Motor `/metrics` 端点提供了丰富的引擎级指标,下表列出推荐用于自动扩缩容的关键指标:208+- **扩缩容范围**:自动扩缩容仅作用于 Prefill 和 Decode 实例;Router 不参与扩缩容。
209+- **缩容稳定窗口**:缩容存在稳定窗口(HPA 默认 5 分钟),避免负载短暂波动导致频繁扩缩。
210+- **新实例性能预热**:扩容的新实例没有 KV Cache 缓存,Prefix Cache 特性会逐步重建缓存,因此新实例的推理性能可能出现小幅度劣化并在一段时间后恢复。
211+- **依赖链路**:External Metrics Adaptor 需持续运行并正确配置 Coordinator 地址。若 Adaptor 异常,HPA 将无法获取指标,可能导致扩缩容失效。
212+- **缩容下限**:建议 `minReplicas` 至少设为 1,避免缩容到 0 导致服务完全不可用。
213+- **角色独立扩缩**:若使用不带 `type` 参数的 `/metrics` 端点(默认 `full`),HPA 获取到的是全局聚合值。如需按 Prefill/Decode 角色独立扩缩容,Adaptor 需分别请求 `/metrics?type=role&role=prefill``/metrics?type=role&role=decode`
164 214 
165-### Prefill 扩缩容推荐指标215+## 2. 手动扩缩容设计
166 216 
167-| 指标名 | 类型 | 说明 | 推荐阈值建议 |217+### 2.1 设计目标与适用场景
168-|--------|------|------|-------------|
169-| `vllm:num_requests_waiting` | Gauge | 等待调度的请求数 | > 5 触发扩容,< 2 触发缩容 |
170-| `vllm:num_requests_running` | Gauge | 当前运行中的请求数 | 视 NPU 规格和模型而定 |
171-| `vllm:kv_cache_usage_perc` | Gauge | KV Cache 使用率(0-1) | > 0.8 触发扩容 |
172-| `motor:prompt_tokens_per_second` | Gauge | Prompt token 处理速率(Motor 计算) | 按 SLA 目标设定 |
173-| `vllm:time_to_first_token_seconds` | Histogram | 首 token 延迟(TTFT) | 按 SLA 目标(如 p95 < 500ms) |
174 218 
175-### Decode 扩缩容推荐指219+手动扩缩容提供**确定性、人工可控**的实例数调整手段:用户修改实例数后执行一次扩缩容操作,集群实例数精确收敛到目值,变更行为可预期。
176 220 
177-| 指标名 | 类型 | 说明 | 推荐阈值建议 |221+适用场景:
178-|--------|------|------|-------------|
179-| `vllm:num_requests_waiting` | Gauge | 等待调度的请求数 | > 5 触发扩容 |
180-| `vllm:num_requests_running` | Gauge | 当前运行中的请求数 | 视 NPU 规格和模型而定 |
181-| `motor:generation_tokens_per_second` | Gauge | 生成 token 速率(Motor 计算) | 按 SLA 目标设定 |
182-| `vllm:e2e_request_latency_seconds` | Histogram | 端到端请求延迟 | 按 SLA 目标(如 p95 < 2s) |
183-| `vllm:time_per_output_token_seconds` | Histogram | 跨 token 延迟(TPOT) | 按 SLA 目标(如 p95 < 50ms) |
184 222 
185-> [!NOTE]说明223+- 集群未部署 HPA / External Metrics Adaptor,不具备自动扩缩容条件;
186->224+- 需要精确预期变更行为(如按计划扩容、临时缩容、演练);
187->- `motor:prompt_tokens_per_second` 和 `motor:generation_tokens_per_second` 是 MindIE Motor Coordinator 计算的服务级指标,基于 vLLM 原始 counter 计算 delta rate 得到,更准确反映实吞吐225+- 作为自动扩缩容不可用的兜底手段
188->- Histogram 类型指标(如 `vllm:e2e_request_latency_seconds`)需要在 Adaptor 侧计算分位数(p50/p95/p99)后作为独立指标暴露。
189->- 建议为 Prefill 和 Decode 分别配置不同的扩缩容指标,以匹配各自的计算特征(Prefill 为计算密集型,Decode 为访存密集型)。
190 226 
191-### 多指标合策略227+与自动扩缩容的边界:手动扩缩容只调整 **Prefill / Decode 实例数**,Controller、Coordinator 等控制面件不在扩缩容路径中。
192 228 
193-可在 HPA 中配置多个指标,HPA 会选择**最保守的扩缩容决策**(即当前副本数最接近触发扩容或缩容的指标):229+### 2.2 核心设计思路
194 230 
195-```yaml231+手动扩缩容的设计围绕"**以集群内基线为锚、增量调整、成功即收敛**"展开,核心要点如下:
196-scalingPolicy:232+ 
197- type: HPA233+**1. 集群 ConfigMap 作为唯一基线(motor-config)**
198- spec:234+ 
199- minReplicas: 1235+每次部署会把 user_config 持久化到集群中的 ConfigMap `motor-config`。扩缩容以集群内基线与当前输入做对比,而非以本地文件为基准——集群状态即真相,避免多客户端/多机器本地配置漂移导致误扩缩。集群中不存在基线(未部署过)时拒绝扩缩容。
200- maxReplicas: 4236+ 
201- metrics:237+**2. 单一变更维度约束**
202- - type: External238+ 
203- external:239+扩缩容仅允许修改实例数字段(`p_instances_num`、`d_instances_num` 或 `hybrid_instances_num`),其余配置必须与基线完全一致。该约束保证了"扩缩容 = 只改实例数"这一语义清晰、可校验,防止配置变更与实例变更混入同一次操作。其他配置的修改走全量重新部署路径,职责分离。
204- metric:240+ 
205- name: num_requests_waiting241+**3. 增量式调整,不动存量实例**
206- target:242+ 
207- type: AverageValue243+- **扩容**:仅对新增的实例(更高 index)执行创建,已运行实例不重拉、不滚动重启,存量流量不受影响;
208- averageValue: "5"244+- **缩容**:从最大 index 开始逆序删除,并同步清理对应部署产物。实例对等(无状态),逆序删除不会产生 index 空洞。
209- - type: External245+ 
210- external:246+增量语义保证操作耗时与变更量成正比,大规模集群下避免全量重建。
211- metric:247+ 
212- name: kv_cache_usage_perc248+**4. 部署模式自适应**
213- target:249+ 
214- type: AverageValue250+扩缩容路径的部署模式取自集群基线(而非本地输入),保证与集群实际拓扑一致:
215- averageValue: "0.8"251+ 
252+- **InferServiceSet 模式**:直接更新角色 replicas,与自动扩缩容共享同一份 YAML 形态;
253+- **multi_deployment 模式**:按引擎类型逐个增量 apply / delete 对应实例的 YAML。
254+ 
255+**5. 成功即刷新基线**
256+ 
257+每次成功的扩缩容都会把本次 user_config 写回 ConfigMap,成为下一次操作的基线。集群状态始终收敛于最后一次成功操作,不会出现"执行了但集群配置未记录"的漂移。
258+ 
259+**6. 前置校验先行**
260+ 
261+实例数的整数性、范围(大于 0 且不超过 16)在触碰集群之前完成校验,快速失败,避免无效操作。
262+ 
263+```mermaid
264+flowchart TD
265+ A[输入 user_config + 扩缩容命令] --> B[校验实例数合法]
266+ B -->|非法| X[快速失败]
267+ B -->|合法| C[读取集群基线 motor-config]
268+ C -->|无基线| X
269+ C -->|有基线| D[校验仅实例数变更]
270+ D -->|有其他变更| X
271+ D -->|通过| E[按部署模式增量调整实例]
272+ E --> F[写回 ConfigMap 刷新基线]
216```273```
217 274 
218-## 验证扩缩容效果275+### 2.3 与自动扩缩容的关系
219 276 
220-### 查看 HPA 状态277+两种方式互补而非互斥:
221 278 
222-```bash279+| 维度 | 自动扩缩容 | 手动扩缩容 |
223-kubectl get hpa -n {namespace}280+|------|-----------|-----------|
224-```281+| 决策主体 | HPA(负载自适应) | 人工 |
282+| 变更粒度 | min/max 范围内连续调整 | 一次性精确设定实例数 |
283+| 适用场景 | 负载波动大、追求资源利用率 | 无自动扩缩容环境、需确定性变更 |
284+| 依赖 | Infer Operator + HPA + External Metrics Adaptor | 仅需集群 ConfigMap 基线 |
225 285 
226-回显示例如下286+两者可组合使用手动扩缩容设定部署层实例数基数,自动扩缩容在 HPA 的 min/max 范围内进一步动态调整副本数。
227 287 
228-```text288+## 3. 参考文档
229-NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
230-prefill-my-test StatefulSet/prefill-my-test 3/5 1 4 2 10m
231-decode-my-test StatefulSet/decode-my-test 8/10 1 4 2 10m
232-```
233- 
234-- `TARGETS` 列显示 `当前值/目标值`,当前值超过目标值时触发扩容。
235-- `REPLICAS` 列显示当前实际副本数。
236- 
237-### 模拟负载触发扩容
238- 
239-发送大量并发推理请求,观察 HPA 是否自动扩容:
240- 
241-```bash
242-# 并发发送请求
243-for i in {1..100}; do
244- curl -X POST "http://{service-ip}:31015/v1/chat/completions" \
245- -H "Content-Type: application/json" \
246- -d '{"model": "your-model", "max_tokens": 100, "messages": [{"role": "user", "content": "Hello"}]}' &
247-done
248-```
249- 
250-随后查看 HPA 状态,确认 `REPLICAS` 是否增加,以及新增引擎 Pod 是否正常运行:
251- 
252-```bash
253-kubectl get hpa -n {namespace} --watch
254-kubectl get pod -n {namespace} | grep -E "prefill|decode"
255-```
256- 
257-### 验证缩容
258- 
259-停止负载后,观察数分钟(由 HPA `--horizontal-pod-autoscaler-downscale-stabilization` 默认 5 分钟),确认实例数回落至 `minReplicas`
260- 
261-```bash
262-kubectl get hpa -n {namespace} --watch
263-```
264- 
265-## 注意事项
266- 
267-- HPA 弹性扩缩容当前仅支持 Prefill 和 Decode 实例;Router 不参与扩缩容。
268-- 缩容存在稳定窗口(默认 5 分钟),避免负载短暂波动导致频繁扩缩。
269-- 扩容的新实例没有 KV Cache 缓存,Prefix Cache 特性会逐步重建缓存,因此新实例的推理性能可能出现小幅度劣化并在一段时间后恢复。
270-- External Metrics Adaptor 需持续运行并正确配置 Coordinator 地址。若 Adaptor 异常,HPA 将无法获取指标,可能导致扩缩容失效。
271-- 建议 `minReplicas` 至少设为 1,避免缩容到 0 导致服务完全不可用。
272-- Counter 类型指标(如 token 总数)不会因 `/metrics` 请求而重置,建议优先使用 Gauge 类型指标或 MindIE Motor 计算的 TPS 指标作为扩缩容依据。
273-- 若使用不带 `type` 参数的 `/metrics` 端点(默认 `full`),HPA 获取到的是全局聚合值。如需按 Prefill/Decode 角色独立扩缩容,Adaptor 需分别请求 `/metrics?type=role&role=prefill``/metrics?type=role&role=decode`
274- 
275-## 参考文档
276 289 
277- [配置基于负载的弹性扩缩容](https://gitcode.com/Ascend/mind-cluster/blob/master/docs/zh/scheduling/04_usage/09_infer_operator_best_practice/05_configuring_elastic_scaling.md) — Infer Operator 弹性扩缩容策略配置指南290- [配置基于负载的弹性扩缩容](https://gitcode.com/Ascend/mind-cluster/blob/master/docs/zh/scheduling/04_usage/09_infer_operator_best_practice/05_configuring_elastic_scaling.md) — Infer Operator 弹性扩缩容策略配置指南
278-- [Metrics 可观测性指标设计文档](../../design/metrics.md) — MindIE Motor Metrics 子系统架构与指标说明291+- [Metrics 可观测性指标设计文档](./metrics.md) — MindIE Motor Metrics 子系统架构与指标说明
279-- [监控接口](../api/monitoring_interfaces.md) — MindIE Motor `/metrics` 端点使用说明292+- [监控接口](../user_guide/api/monitoring_interfaces.md) — MindIE Motor `/metrics` 端点使用说明
293+- [手动扩缩容用户手册](../user_guide/features/manual_scaling.md) — 手动扩缩容操作步骤与常见问题
@@ -8,7 +8,7 @@
8 8 
9本特性通过ETCD分布式锁机制实现Kubernetes集群中Controller的主备倒换功能,确保系统高可用性。开启Controller主备倒换特性开关后,系统会在初始化阶段拉起两个Controller实例,通过ETCD分布式锁竞争来实现主备身份选举,当主Controller发生故障时,备用Controller能在设定时间间隔后自动接管工作。9本特性通过ETCD分布式锁机制实现Kubernetes集群中Controller的主备倒换功能,确保系统高可用性。开启Controller主备倒换特性开关后,系统会在初始化阶段拉起两个Controller实例,通过ETCD分布式锁竞争来实现主备身份选举,当主Controller发生故障时,备用Controller能在设定时间间隔后自动接管工作。
10 10 
11-了解主备详细设计请参考[主备倒换特性设计文档](../../../design/standby.md)。11+了解主备详细设计请参考[主备倒换特性设计文档](../../../design/fault_tolerance/standby.md)。
12 12 
13**限制与约束**13**限制与约束**
14 14