昇腾部署第 D5 课:可用性、健康与调优——起来之后怎么活下去
编制:观澜|日期:2026-10-05|级别:T2 标准级 层级:D 层 · L3 服务(D 层第 5 课,D 层收官;前接 D4《PD 分离与混部》) 素材来源:昇腾官方 MindIE 3.1.0 文档(《基于 K8s 多容器 PD 分离服务部署》含特性配置指导与附录·运维技巧、《虚推健康探测》特性说明、《主备倒换特性》、《就绪状态查询接口》、MindIE Motor CPP 开发指南《集群内通信接口》) 数据纪律:配置项、参数、官方报错文本、注意事项逐字出自官方文档;标注「归纳」的是便于记忆的整理,非官方口径。
第一段 · 章前定位
D2 讲的是"怎么让服务起来",D3 讲"由哪些角色组成",D4 讲"为什么这么拆"。
这一课解决最后一类问题:
服务起来了,然后呢?
真实生产里,"起来了"只是开始:
| 问题 | 谁关心 |
|---|---|
| 一台服务器挂了,服务还能用吗? | 可用性 |
| 某些进程卡死了,但服务"看起来正常"怎么办? | 健康探测 |
| 突然来十万个请求,服务会被压垮吗? | 过载保护 |
| 同样一批卡,怎么让吞吐更高? | 调优 |
这一课就是讲这四件事。
⭐ 而且好消息是:官方把答案都做好了,并且在示例配置里默认开了 —— 你要学的不是"怎么开",而是"它是什么、什么时候该调"。
这一课也把 A0 的判据 ④ 补完: 判据 ④ 是"端口 + curl 服务起得来"。 D2 讲了"起得来",这一课讲"起得来之后,怎么知道它还好着"。
第二段 · 学习目标
学完这一课,你应该能回答:
- 官方全量配置示例默认开启了哪六项能力
- 主备倒换解决什么、开启的前置条件是什么
- 虚推健康检查是什么、它针对的"静默故障"是什么样
- 服务限流怎么配、参数是什么意思
- KV Cache 亲和调度与 KV 池化分别在优化什么
- 服务"没就绪"时官方给的报错文本是什么
- 上线巡检要看哪几个指标(就绪码 /
GET /status/GET /health/ AI Cube 利用率) - 这几条官方命令:
kubectl get pods -o wide、/v1/readiness、bash show_log.sh、npu-smi info watch -s u
第三段 · 官方默认开了哪六项(总览)
官方原文(特性配置指导一节的开头):
上文
user_config.json与env.json全量示例已默认开启 主备倒换、异常实例重启、服务限流、虚推、KV 亲和性调度、KV 池化等能力。 若只需调整某项能力,可对照本节做最小配置修改。
六项能力分类(归纳)
| 分类 | 能力 | 解决什么 |
|---|---|---|
| 可用性与自愈 | 主备倒换 | 服务器级故障 |
| 异常实例重启 | 实例级故障 | |
| 保护与探测 | 服务限流 | 过载 |
| 虚推健康检查 | 静默故障 | |
| 性能调优 | KV Cache 亲和调度 | 重复前缀的重复计算 |
| KV 池化 | 显存压力 |
⭐ 注意官方那句话的语气:「全量示例已默认开启」 —— 这意味着你拿官方示例跑起来时,这六项已经在工作了。 所以你遇到的现象(例如"没请求时它也在动")可能正是虚推健康检查在干活。
这是一条很实用的排障提示: 在看日志发现"莫名其妙的行为"时,先想想这六项里有没有哪一项会表现成这样。
第四段 【如果我来做】一条真实的上线执行路径
这一节是本课的核心。 前三段讲的是"官方默认开了哪六项能力",这一节讲: 如果现在把"让服务活下去"这件事交给我,我从哪里开始、按什么顺序做、每一步凭什么判断。 本课本身就是讲可用性与调优,所以这条路径写得最实 —— 巡检 → 看指标 → 定位异常 → 调优。
4.0 先记住一句话:顺序错了会返工
"起来之后怎么活下去"是一条有先后顺序的链路,不是"把六项能力全开一遍就完了"。 最容易犯的错是顺序颠倒:
- 先调优(开 KV 亲和 / KV 池化),再发现服务根本没就绪 → 拿没对准的基线去调,变量全是乱的,白调;
- 为了排障把日志级别调成 INFO/DEBUG,结果虚推被引擎自动关掉(官方原文)→ 健康探测悄悄失效,你还不知道;
- 看到
kubectl get pods显示Running就以为万事大吉 → 官方原文: "Pod 状态为Running仅表示已成功调度并启动,是否业务就绪仍需结合日志进一步确认" —— 这是本课最容易踩的坑。
所以我的顺序是:摸底 → 建健康检查 → 定日志级别 → 认静默故障 → 跑通基线 → 再调优 → 收口 → 出证据。
4.1 我会按这个顺序做(八步)
① 先摸底:上线前先看官方默认开了哪六项能力,别自己乱改。
② 建健康检查:定清楚"健康"看哪几个指标(就绪码 / GET /status / GET /health / AI Cube 利用率)。
③ 定日志级别:把 ASCEND_GLOBAL_LOG_LEVEL 定在 ERROR,否则虚推会被引擎自动关掉。
④ 认"静默故障":记住它的长相 —— 进程卡死、服务看似无问题,并知道虚推就是为它准备的。
⑤ 先跑通最小链路:拉起服务 + 发一次真推理,作为调优前的基线。
⑥ 再做性能调优:基线没稳之前,不碰 KV 亲和 / KV 池化。
⑦ 收口出证据:把 Pod 状态、就绪码、一次成功推理的返回、日志片段留下来。
⑧ 诚实:官方没给答案的地方(例如日志的具体路径),明写"这里要自己查"。
4.2 第①步 先摸底:上线前先看官方默认开了哪六项能力
目的:在动手调之前,先把"服务在不在、官方默认开了什么"这两个事实搞清楚。
官方原文(特性配置指导开头):
上文
user_config.json与env.json全量示例已默认开启主备倒换、异常实例重启、 服务限流、虚推、KV 亲和性调度、KV 池化等能力。 若只需调整某项能力,可对照本节做最小配置修改。
为什么这一步放最前面:六项能力默认已开 —— 你多改一处,就多一个变量。 "最小配置修改"是官方原话,摸底的目的就是搞清楚"哪些其实不用我动"。
命令 kubectl get pods -n <namespace> -o wide
含义:列出这个命名空间下的所有 Pod,看它们起没起、被调度到哪台机器 拆解:
kubectl(K8s 命令行)+get pods(列出 Pod)+-n <namespace>(指定命名空间, 须与user_config.json里的job_id一致,默认mindie-motor)+-o wide(多显示 Pod IP 与所在节点) 用法:kubectl get pods -n mindie-motor -o wide期望:看到这几类 Pod 且状态为Running——mindie-motor-controller-xxxxx(服务管理 Pod)、mindie-motor-coordinator-xxxx(请求调度 Pod,业务入口)、vllm-px-xxxx(P 实例)、vllm-dx-xxxx(D 实例) 不过会怎样:Pod 卡在Pending/ContainerCreating→ 镜像拉不下来,或权重挂载路径不存在 (官方运维技巧:确保image_name在集群内可正常拉取、weight_mount_path在宿主机上存在); 报No resources found→ 命名空间填错了,或服务根本没拉起来(回 D2/D3) ⭐ 记住官方那句话:Running只表示"已成功调度并启动",不等于"业务就绪" —— 所以这一步只是摸底,真就绪不看这个,看下一步。
4.3 第②步 建健康检查:到底看哪几个指标
"健康"在本课里不是一个形容词,是一组可以查的指标。 官方给的口子是这几个:
| 层 | 指标 / 接口 | 判读 |
|---|---|---|
| 就绪面 | GET /v1/readiness |
200 = 已就绪;503 = 未就绪 |
| 就绪面(Triton) | GET /v2/health/ready |
同上(200 / 503) |
| 引擎管理面 | Node Manager 周期请求 Engine Server mgmt 面 GET /status |
综合推理面 /health 与虚推结果,返回 normal / abnormal |
| 推理面 | GET /health |
由 HealthCollector 探测,超时/重试由 health_collector_timeout 控制 |
| 算力面 | AI Cube 利用率 | 虚推的判据之一(见下) |
⭐ 一句话记住三层分工:就绪码证明"调度面活着",
/status证明"引擎面活着", AI Cube 利用率证明"算力面在真干活"。三样都看,才叫"建了健康检查"。
命令 curl -s -o /dev/null -w "%{http_code}\n" https://<ip>:<port>/v1/readiness
含义:只取就绪接口的 HTTP 状态码、不打印正文 —— 一把量出"就绪没就绪" 拆解:
curl(发请求)+-s(静默,不显示进度条)+-o /dev/null(丢掉响应正文) +-w "%{http_code}\n"(只打印状态码)+ URL 取自官方就绪接口https://{ip}:{port}/v1/readiness(官方说明:{ip}优先取环境变量POD_IP,取不到则取ms_coordinator.json的manage_ip;{port}优先取启动命令参数{status_port},取不到则取manage_port) 用法:curl -s -o /dev/null -w "%{http_code}\n" https://127.0.0.1:1025/v1/readiness期望:打印200—— 官方原文:"状态码 200,表示服务已就绪,消息体没有内容。" 不过会怎样:打印503—— 官方原文:"状态码 503,表示服务未就绪" → 回第 ① 步看 Pod,再结合日志确认;curl: (7) Failed to connect→ 地址或端口不对({ip}/{port}取值见上面的官方说明)
命令 npu-smi info watch -s u
含义:看 NPU 的 AI Cube 利用率(虚推判断"算力面在不在真干活"用的指标) 拆解:
npu-smi info watch(周期性刷新设备信息)+-s u(只采样u这一项,即 AI Cube Usage) 用法:npu-smi info watch -s u期望:能持续打印 AI Cube 利用率的数值 不过会怎样:报不支持 / 无输出 → 当前 HDK 版本太老。 官方原文:启动虚推前会通过npu-smi info watch -h检查 help 是否包含u - AI Cube Usage; 若当前 HDK 不支持该指标,Engine Server 会自动关闭虚推。(官方:虚推仅支持 HDK 26.0.RC1 及以后版本)
命令 npu-smi info watch -h
含义:先查
npu-smi info watch支持哪些采样项,确认清单里有没有u - AI Cube Usage拆解:-h(看帮助 / 支持的指标清单)—— 官方就是用它做虚推的前置检查 用法:npu-smi info watch -h期望:输出里能看到u - AI Cube Usage不过会怎样:清单里没有u→ 虚推会被自动关闭(官方原文),得换 HDK 版本
4.4 第③步 定日志级别:别把虚推调没了
这是本课最反直觉的一条 —— 官方原文:
虚推仅允许在 ERROR 日志级别下开启(
ASCEND_GLOBAL_LOG_LEVEL=3,未配置默认即为 ERROR)。 若引擎进程环境中显式配置为非 ERROR,Engine Server 启动虚推前会关闭虚推并打印 warning。
含义:你为了看得更细,把日志级别调成 INFO/DEBUG,虚推会被自动关掉 —— 而且是"打印一条 warning 就关",你多半注意不到。 结果就是:看起来一切都好,健康探测其实早没了。
命令 export ASCEND_GLOBAL_LOG_LEVEL=3
含义:把昇腾组件的日志级别设为 ERROR(3) —— 官方允许虚推开启的唯一日志级别 拆解:
ASCEND_GLOBAL_LOG_LEVEL取值 0=DEBUG、1=INFO、2=WARNING、3=ERROR(官方原文) 用法:export ASCEND_GLOBAL_LOG_LEVEL=3(未配置时默认即为 ERROR,所以"什么都不配"也是对的) 期望:echo $ASCEND_GLOBAL_LOG_LEVEL打印3不过会怎样:设成0/1/2→ 官方原文:"Engine Server 启动虚推前会关闭虚推并打印 warning", 于是配置里enable_virtual_inference: true形同虚设
命令 bash show_log.sh
含义:采集并持续跟踪各 Pod 的日志(官方部署指导给的日志入口) 拆解:脚本在
examples/deployer/下;运行前要先改log_collect/log_config.ini里的name_space, 改成与job_id一致的命名空间;输出目录是log_collect/log/用法:cd examples/deployer→vim log_collect/log_config.ini(把name_space换成你的命名空间)→bash show_log.sh期望:屏幕持续滚动各 Pod 的日志,同时在log_collect/log/下生成日志文件 不过会怎样:日志空空如也 →name_space没改对;要找关键报错, 按 Pod 名对号入座(Controller / Coordinator / vllm-px / vllm-dx)
4.5 第④步 认"静默故障":最危险的一类
官方原文:
静默故障表现为:部分进程卡死,服务看似无问题,但无法正常推理。
为什么它最危险(归纳):它会骗过"端口通不通"这一层。
| 普通故障 | 静默故障 | |
|---|---|---|
| 端口 | 不通 | 通 |
| 进程 | 不在 | 在 |
kubectl get pods |
不是 Running |
Running |
| 表面 | 明显异常 | "看似无问题" |
| 实际 | 起不来 | 无法正常推理 |
官方的对策就是"虚推"(第五段详述): 业务流量较小时发送轻量级推理请求;业务流量较大时查看 NPU 计算核心使用率。 不健康的 P/D 实例会被重启以消除静默故障。
⭐ 本课最该带走的一句验收话:
"端口通 / Pod Running"只能证明调度面活着;"能推理"必须主动发一个请求去证明。
4.6 第⑤步 先跑通最小链路(基线),再谈调优
没跑通基线就调优,等于没量尺子就裁衣服。 官方给的验证请求是这一条:
命令 curl -X POST http://<master_node_ip>:31015/v1/chat/completions -H "Content-Type: application/json" -d '{"model":"<served_model_name>","messages":[{"role":"user","content":"who are you?"}],"max_tokens":36,"stream":true}'
含义:向推理入口发一次真实对话请求 —— 这是"能推理"的直接证据 拆解:
-X POST(用 POST)+-H "Content-Type: application/json"(声明 JSON 体) +-d '{...}'(请求体:model填user_config.json里的served_model_name,messages是对话,max_tokens限长,stream: true走流式);端口31015是官方样例里的入口端口 用法:把<master_node_ip>换成 master 节点 IP、<served_model_name>换成配置里的值,照抄官方样例 期望:返回流式 JSON —— 官方原文:"返回流式 JSON:推理正常。" 不过会怎样:返回{"detail":"Service is not available"}→ 官方原文:"服务尚未就绪,稍后重试" (运维技巧补充:"多为 P/D 或 Coordinator 尚未完全就绪;等待后重试,并查看各 Pod 日志"); 一直curl: (7)/(28)→ 连不上,回第 ① 步
4.7 第⑥⑦⑧步 调优 / 收口 / 出证据 / 诚实
⑥ 再做性能调优(归纳):基线稳了才动手。
- 前缀重复多 → 开 KV Cache 亲和调度(第六段);
- 显存压力大 → 上 KV 池化(第六段);
- 官方运维技巧提醒:Prefix Cache 默认开启。要做基线性能测试,得先在 engine_config 里加
"no-enable-prefix-caching": true(vLLM)或 "disable_radix_cache": true(SGLang)——
否则你测出来的"性能"里混着缓存的功劳。
⑦ 收口 / 出证据(归纳):用完整流程跑一遍当验收。
| 留下什么 | 从哪来 |
|---|---|
Pod 清单与状态(kubectl get pods -n <ns> -o wide) |
第 ① 步 |
就绪码 200 |
第 ② 步 |
| 一次成功推理的流式返回 | 第 ⑤ 步 |
日志片段(log_collect/log/) |
第 ③ 步 |
终止服务(官方命令):cd examples/deployer && bash delete.sh <namespace> ——
停服务要走官方卸载脚本,不要手工 kill(D2 讲过 kill -2 是进程级的优雅停止;
集群场景该走 Deployment / 卸载脚本)。
⑧ 诚实(归纳):官方没给答案的地方,明写"这里要自己查":
- 日志的具体路径与收集方式 → 官方指向部署工具的 log_collect/,路径细节以官方文档与 E1 为准;
- KV 池化的完整参数 → 官方明说"参数较多,篇幅有限",指向《KV 池化部署指南》;
- 限流阈值设多少合适 → 官方只给了一个示例值(max_requests: 10000 / window_size: 60),
合理值要按你自己的 QPS 算。
4.8 附:本段涉及的官方原文(可用性与自愈)
下面这两项是"六项能力"里的可用性与自愈部分,官方在《特性配置指导》里给的就是这些: 它们默认已开,你主要是"知道它是什么、现象长什么样、什么时候该调"。
① 主备倒换
官方原文:
创建多个 Controller 和 Coordinator 并运行在不同的服务器上, 当某一台服务器出现异常(宕机、重启、维护升级), 部署于其他服务器的 Controller 和 Coordinator 组件将接管集群, 避免单个服务器异常导致业务请求无法调度。
原理:创建多个管理面和业务面的 Pod,分布于多台服务器。 当主 Pod 异常时,选择一套备份 Pod 承担当前业务。对外表现上整个服务集群没有任何异常。
开启方式(官方原文):
"motor_controller_config": {
"standby_config": { "enable_master_standby": true }
},
"motor_coordinator_config": {
"standby_config": { "enable_master_standby": true }
}
关闭:删除 standby_config 配置块,或把 enable_master_standby 设为 false。
⭐ 官方给的注意(前置条件):
须提前部署 etcd(建议 3 副本)。
官方《主备倒换特性》补的限制与约束(原文要点):
| 约束 | 官方原文 |
|---|---|
| 节点分布 | 主、备 Controller 节点不建议部署在同一台节点上 |
| etcd 版本 | ETCD 服务端需要使用 v3.6 版本 |
| etcd 副本 | 服务端至少需要 3 个副本,以保证 ETCD 集群的可靠性 |
| 复用 | Coordinator、Controller 主备倒换特性可以共用一套 ETCD;多套大 EP 集群也可共用,通过命名空间区分 |
归纳:主备倒换依赖 etcd 做协调 —— 所以"开了这个开关但没部署 etcd",它是起不来的。
⭐ 加速硬件故障恢复(官方可选步骤):硬件故障(如机器重启)时,K8s 可能无法迅速感知 Pod 状态, 官方给了加速恢复的可选步骤 —— 先查 K8s 节点心跳超时标记阈值:
命令 kubectl describe pod <kube-controller-manager-pod 名> -n kube-system | grep "node-monitor-grace-period"
含义:查 K8s 管理节点的
node-monitor-grace-period(节点心跳超时标记阈值) 拆解:kubectl describe pod <名> -n kube-system(看 kube-system 里那个 controller-manager Pod 的详情) +| grep "node-monitor-grace-period"(只挑出这一行) 用法:kubectl describe pod kube-controller-manager-node-97-10 -n kube-system | grep "node-monitor-grace-period"期望:打印出--node-monitor-grace-period=...(官方原文:"如果结果为空表示为默认值") 不过会怎样:查不到这个 Pod 名 → 先用kubectl get pods -n kube-system找到 kube-controller-manager 的 Pod ⭐ 官方给的可选做法是把该值改小(示例为20s)以加快业务恢复 —— 改法是编辑控制平面节点上的/etc/kubernetes/manifests/kube-controller-manager.yaml,再systemctl restart kubelet.service。这一步属集群管理动作,不确定就别在生产上动。
② 异常实例重启
官方原文:
P/D 实例出现异常时,重启推理实例,避免实例长时间处于异常状态,影响集群推理功能。
原理:推理主进程异常退出等情况时,Motor 自动重启对应实例。 重启期间吞吐能力下降,重启后恢复正常。
开启:默认开启,无需额外操作。 关闭:无需关闭。
归纳这条的实用价值: 如果你的服务"偶尔卡一下又好了",可能就是这个机制在工作 —— 某一侧实例异常退出 → 自动重启 → 期间吞吐下降 → 恢复。 这与"服务彻底挂了"是完全不同的现象,排查方向也不同。
与 D3 的一个呼应(归纳): D3 提到 Controller 和 Coordinator 官方方案里是"单 Pod" —— 单 Pod 就是单点。 主备倒换正是给这两个控制面组件加冗余 —— 这也解释了 D3 里为什么官方单独强调 Controller 的 "RAS 能力决策"。
第五段 · 保护与探测(服务限流 / 虚推健康检查)
① 服务限流
官方原文:
限制推理入口 Coordinator 在单位时间内的最大请求数,防止服务过载。
原理:Coordinator 记录一段时间内接收到的请求数量,达到阈值后停止接收外部请求。
配置(官方原文):
"motor_coordinator_config": {
"rate_limit_config": {
"enable_rate_limit": true,
"max_requests": 10000,
"window_size": 60
}
}
官方对这三个字段的解释:
上述配置表示 60 秒内最多处理 10000 条请求。
拆解(归纳):
| 字段 | 含义 |
|---|---|
enable_rate_limit |
是否开启 |
max_requests |
窗口内最大请求数 |
window_size |
窗口大小(秒) |
关闭:删除 rate_limit_config 配置块,或把 enable_rate_limit 设为 false。
⭐ 一个很实用的定位结论(归纳): 限流的动作发生在 Coordinator(官方:"限制推理入口 Coordinator")—— 所以如果你的请求被拒 / 变慢,而各实例都健康,先看 Coordinator 的限流配置。
⚠️ 官方没给的:阈值设多少没有标准答案 —— 官方只给了一个示例(
10000/60s)。 该设多少要按你自己的业务 QPS 和单实例吞吐算,这里没有官方公式。
② 虚推健康检查(本课最有价值的一项)
官方原文:
虚推健康检查(Virtual Inference Health Check):探测服务健康状态,避免静默故障带来业务损失。
静默故障表现为:部分进程卡死,服务看似无问题,但无法正常推理。
原理:业务流量较小时发送轻量级推理请求; 业务流量较大时查看 NPU 计算核心使用率。 不健康的 P/D 实例会被重启以消除静默故障。
⭐⭐ "静默故障"这个定义值得单独记住:
部分进程卡死,服务看似无问题,但无法正常推理。
归纳这类故障为什么最危险(归纳):
| 普通故障 | 静默故障 | |
|---|---|---|
| 端口 | 不通 | 通 |
| 进程 | 不在 | 在 |
| 表面 | 明显异常 | "看似无问题" |
| 实际 | 起不来 | 无法正常推理 |
A0 的判据 ④ 是"端口 + curl 服务起得来" —— 但静默故障恰好能骗过"端口通不通"这一层。 这正是为什么需要"虚推":主动发一个轻量推理请求去试。
⭐ 一条很实用的验收思路(归纳): "端口通"只能证明网络层活着;"能推理"要主动发一个请求去证明。 这与 D2 里官方那句"错误响应或空补全不能确认推理成功"是同一个道理。
配置(官方原文):
"motor_engine_prefill_config": {
"health_check_config": {
"enable_virtual_inference": true,
"npu_usage_threshold": 10
}
}
官方提醒:
P 和 D 实例需要单独开启虚推功能(P 的开启方式如上,D 实例的开启方式相同)。
⚠️ 一条很容易踩的官方注意:
虚推仅允许在 ERROR 日志级别下开启(
ASCEND_GLOBAL_LOG_LEVEL=3,未配置默认即为 ERROR)。 若引擎进程环境中显式配置为非 ERROR,Engine Server 启动虚推前会关闭虚推并打印 warning。
归纳这条的实用含义: 如果你为了排障把日志级别调细了(调成 INFO/DEBUG),虚推会被自动关掉 —— 而你多半不会注意到那条 warning。 所以"调完日志级别之后健康探测失效了"这件事,是有官方依据的。
虚推的"机关"(官方《虚推健康探测》特性说明,原文要点)
启用条件(须同时满足,官方原文):
engine_type为vllm—— SGLang 引擎即使配置enable_virtual_inference: true也会在运行时被自动关闭;health_check_config.enable_virtual_inference为true;- 进程环境变量
ASCEND_GLOBAL_LOG_LEVEL为 ERROR(3); 0 < health_check_config.npu_usage_threshold <= 100;- 推理面
GET /health返回正常(由HealthCollector探测); - 仅 DP rank 0 执行虚推(非 DP0 节点运行时自动关闭)。
虚推请求长什么样(官方原文):向推理面 POST /v1/completions,
请求体为 prompt: "1"、max_tokens: 1 —— 这就是"轻量级推理请求"的真身。
NPU 负载采样:使用 npu-smi info watch -s u 采集 AI Cube 利用率。
动态探测间隔(官方原文):
| AI Cube 利用率峰值(5 秒采样窗口) | 下一轮间隔 |
|---|---|
| ≥ 80% | 20 秒 |
< npu_usage_threshold |
5 秒(默认) |
[npu_usage_threshold, 80%) |
保持当前间隔不变 |
异常判定(官方原文):当 AI Cube 利用率峰值低于 npu_usage_threshold 且虚推请求失败时,
累计连续失败次数;达到 max_failure_count 后,GET /status 返回 abnormal 且虚推循环停止。
Node Manager 的 HeartbeatManager 连续 5 次收到 abnormal 后触发自杀重调度。
配置字段全表(官方原文):
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enable_virtual_inference |
bool | false |
虚推总开关。仅支持 vLLM;仅允许在 ERROR 日志级别下开启 |
npu_usage_threshold |
int | 3 |
AI Cube 利用率阈值(%) |
max_failure_count |
int | 6 |
连续虚推失败次数上限 |
health_collector_timeout |
int | 5 |
推理面 /health 探测超时(秒) |
health_collector_timeout_retry_attempts |
int | 3 |
推理面 /health 超时重试次数(含首次,仅超时触发) |
⚠️ 一处官方口径差异(如实标注): 《虚推健康探测》特性说明里
health_check_config的默认值是false(默认关闭); 而《PD 分离服务部署指导》说全量示例已默认开启虚推。 两处口径不同 —— 到底开没开,取决于你用的是哪份示例配置,上线前一定要自己看一眼配置文件。
官方未给出的:虚推那条 warning 的原文格式与具体日志路径本课未搬运, 以官方《虚推健康探测》特性说明为准。
第六段 · 性能调优(KV Cache 亲和调度 / KV 池化)
① KV Cache 亲和调度
官方原文:
将具有相同前缀的请求调度到同一实例,复用已有 KV Cache,减少 Prefill 耗时。
原理:MindIE Motor KV Cache 亲和性调度能力依赖 Mooncake 社区的 Mooncake Conductor 组件, 允许调度器根据 KV Cache 位置优先将请求调度到缓存了对应 KV 的实例, 从而减少 KV Cache 跨实例传输开销,提升推理吞吐与响应速度。
⭐ 用 D4 的知识解释它在优化什么(归纳):
D4 讲过:Prefill 是计算密集型的,且"每个新输入序列都需执行一次 Prefill"。
那么多轮对话会怎样?(归纳) 每轮对话的前面部分是一样的(相同前缀) —— 如果每次都重新 Prefill 一遍,就是重复计算。
KV Cache 亲和调度的做法:把前缀相同的请求送到"已经算过这段前缀"的那个实例上, 让它直接复用已有的 KV Cache,不用重算。
开启方式(官方原文,三处一起改):
"motor_coordinator_config": {
"scheduler_config": { "scheduler_type": "kv_cache_affinity" }
},
"motor_engine_prefill_config": {
"engine_config": {
"kv-events-config": {
"publisher": "zmq",
"enable_kv_cache_events": true,
"endpoint": "tcp://*:5557",
"topic": "kv-events",
"replay_endpoint": "tcp://*:6667"
},
"enable-prefix-caching": true
}
},
"kv_conductor_config": {
"kvevent_instance": { "mooncake_master": { "type": "Mooncake" } },
"http_server_port": 13333
}
归纳这三处分别在干什么:
1. motor_coordinator_config:换调度器 → scheduler_type: "kv_cache_affinity";
2. motor_engine_prefill_config:开启 KV 事件发布(enable_kv_cache_events)+ 开启 prefix cache 特性(enable-prefix-caching);
3. kv_conductor_config:KV Conductor 配置,与 motor_engine_prefill_config 同层级。
注意(官方原文):
需要确保镜像中已安装 KV Conductor 组件。
归纳:又是"先确认东西在不在,再谈配置" —— 和 B2(ops 包)、C1(版本对应)、D2(镜像标签)同一条纪律。
② KV 池化
官方原文:
通过
MultiConnector将 KV 缓存卸载到共享池,支持跨实例复用,降低显存压力。原理:允许 P/D 实例通过 KV 缓存池共享 KV Cache, P 实例将计算好的 KV Cache 推入缓存池,D 实例从缓存池拉取并复用, 从而在 PD 分离场景下提升显存利用率和推理吞吐。
⭐ 这一条正好把 D4 的"代价"接上了(归纳):
D4 说:PD 分离的代价是 KV Cache 必须在 P 和 D 之间传输。 KV 池化做的就是"把这件事做得更高效" —— 不是点对点传,而是放到一个共享池里,按需取用。
配置:官方说明"参数较多,篇幅有限",指向 《KV 池化部署指南》。
关闭:改用非 MultiConnector 的单一 connector,并删除根节点 kv_cache_pool_config。
⚠️ 数据纪律提示:官方明确指出该功能"参数较多",本课不搬运完整配置, 以官方《KV 池化部署指南》为准。
③ 调优前的一个官方前提:Prefix Cache 默认开着
官方运维技巧原文:
Prefix Cache 对性能测试的影响:Prefix Cache 默认开启。 若需基线性能,可在
engine_config中增加"no-enable-prefix-caching": true(vLLM) 或"disable_radix_cache": true(SGLang)。
归纳:"KV 亲和调度"和"Prefix Cache"是两件事 —— Prefix Cache 让同一实例自己复用前缀;KV 亲和调度让"该去哪台实例"也复用了前缀。 但两者都会让你的性能测试结果"更好看" —— 要做干净基线,就得先把它们关掉再测。
第七段 · 起停与"就绪"判断
官方给的运维技巧(原文,含一条真实报错)
服务未就绪:推理接口返回
{"detail":"Service is not available"}时, 多为 P/D 或 Coordinator 尚未完全就绪;等待后重试,并查看各 Pod 日志。
⭐⭐ 这是本课最实用的一条 —— 因为它给了一个"照抄就能用"的排查映射:
| 你看到 | 官方说多半是 | 该做什么 |
|---|---|---|
推理接口返回 {"detail":"Service is not available"} |
P/D 或 Coordinator 尚未完全就绪 | 等待后重试 + 查看各 Pod 日志 |
归纳:"服务未就绪""服务坏了""静默故障"是三件事:
- 未就绪 → 等一会儿可能就好了(实例在启动、在重启、在主备切换)
- 静默故障 → "看着是好的"(Running、端口通),其实推不出结果 —— 第三种状态,本课新增
- 坏了 → 等多久都不会好
先分清这三种,能省掉大量无效排查。
⭐ 三态怎么用命令认出来(把第四段的命令放这儿对照)
命令 curl -s -o /dev/null -w "%{http_code}\n" https://<ip>:<port>/v1/readiness
含义:用就绪码区分"未就绪"和"已就绪" 拆解:同第四段(
-w "%{http_code}"只取状态码);URL 是官方就绪接口 用法:curl -s -o /dev/null -w "%{http_code}\n" https://127.0.0.1:1025/v1/readiness期望:200= 已就绪;503= 未就绪(官方原文) 不过会怎样:拿到200但推理仍失败 → 说明"就绪"骗不了你,往"静默故障"查(第五段虚推); 连接失败 → 地址 / 端口错(回第四段)
命令 bash delete.sh <namespace>
含义:终止 / 卸载服务(官方部署指导给的收尾命令) 拆解:脚本在
examples/deployer/下;<namespace>与job_id一致 用法:cd examples/deployer && bash delete.sh mindie-motor期望:服务被卸载,kubectl get pods -n <namespace>下的 Pod 逐步消失 不过会怎样:名字填错 → 报找不到命名空间;停之前先确认这里跑的不是你在意的业务 ⭐ 官方运维技巧:部署失败时,"可先按终止服务小节的命令卸载,排查并修改配置后重新部署"
起停与"看什么日志"
结合 D3 的角色分工,可以整理出一张"看哪儿的日志"的表(归纳):
| 现象 | 看谁的日志 |
|---|---|
| 请求进不来 / 被限流 | Coordinator |
| 接口报未就绪 | P/D 实例 + Coordinator(官方原文) |
| 某节点上的进程反复重启 | NodeManager(D3:管节点级进程与健康) |
| 主备切换相关 | Controller(+ etcd) |
| 推理结果异常 / 疑似静默故障 | EngineServer / 引擎日志(往下走 D2/C 层) |
官方给的日志入口是 bash show_log.sh(第四段第 ③ 步),采集到 log_collect/log/。
⚠️ 说明:上表是归纳(基于 D3 的组件职责); 具体的日志路径与收集方式以官方文档为准(E1 讲过日志分三处)。
优雅停止(回顾 D2)
D2 讲过官方的 kill -2(等同于 Ctrl+C)——
本课补充一句(归纳):
在集群场景,停服务不要直接杀进程,
官方方案里 P/D 是 Pod、有 NodeManager 管进程 ——
该走 Deployment / 配置的启停方式(如官方 bash delete.sh),而不是手工 kill。
第八段 · 出问题怎么查
先按"六项能力"照镜子
因为官方默认全开了,很多现象是这些能力在正常工作(归纳):
| 你看到的现象 | 可能是 | 该做什么 |
|---|---|---|
| 服务偶尔"卡一下"又好了 | 异常实例重启在起作用 | 查日志确认,并查实例为何异常 |
| 没请求时日志里也有动作 | 虚推健康检查在探测 | 正常 |
| 请求被拒 / 变慢,但实例都健康 | 服务限流(Coordinator) | 调 max_requests / window_size |
| 调细日志级别后健康探测失效 | 虚推只在 ERROR 级别允许(官方原文) | 恢复日志级别 |
| 某台机器挂了但服务没断 | 主备倒换在工作 | 正常(但要修那台机器) |
| 想上 KV 亲和调度但起不来 | 镜像里有没有 KV Conductor 组件 | 换镜像 / 确认组件 |
返回 Service is not available |
未就绪(官方原文) | 等待重试 + 看 Pod 日志 |
Pod Running、端口通,但推理不回 |
静默故障(进程卡死) | 看虚推是否开启 / GET /status 是否 abnormal |
常见报错清单(官方原文 → 现象映射)
⚠️ 官方没有一份"本课专属的报错码表" —— 下面每一条都是官方原文 / 运维技巧里给的现象或说明, 按"现象 → 官方说明 → 先查什么"整理,不是编造的错误码。
| 现象 / 报错 | 官方说明 | 先查什么 |
|---|---|---|
推理接口返回 {"detail":"Service is not available"} |
服务未就绪;"多为 P/D 或 Coordinator 尚未完全就绪" | 等待重试 + bash show_log.sh 看各 Pod 日志 |
Pod Running 但推理无响应 |
可能是静默故障(官方:"部分进程卡死,服务看似无问题") | 虚推是否满足启用条件;GET /status 是否 abnormal |
| 配了虚推却不生效 | 官方:虚推仅允许在 ERROR 日志级别下开启 | echo $ASCEND_GLOBAL_LOG_LEVEL 应为 3 |
| 虚推被自动关闭 | 官方启用条件不满足即自动关闭(非 vLLM / 非 ERROR / HDK 不支持 AI Cube Usage / 非 DP rank 0) | 逐条对第五段的 6 个启用条件 |
npu-smi info watch -s u 不支持 |
官方:HDK 不支持该指标时虚推自动关闭 | 确认 HDK ≥ 26.0.RC1 |
权重加载报 timeout(超过约 10 分钟) |
官方:通常不影响程序运行;以所用镜像 / 引擎版本说明为准 | 先看服务是否最终就绪,别急着推倒重来 |
| 删掉一个 Pod 却没触发实例重调度 | 官方:实例重调度依赖 MindCluster;P/D 实例含多个 Pod 时,直接删除其中一个 Pod 不会触发实例重调度 | 走完整的实例重调度,而不是手删 Pod |
| 部署失败,想重来 | 官方:可先按终止服务小节的命令卸载,排查并修改配置后重新部署 | bash delete.sh <namespace> |
| Pod 起不来 / 找不到镜像或权重 | 官方:确保 image_name 在集群内可正常拉取、weight_mount_path 在宿主机上存在 |
查镜像仓库可达性与挂载路径 |
| 开了主备倒换但起不来 | 官方:依赖 ETCD v3.6、至少 3 个副本 | 查 etcd 部署与版本(4.8 第 ① 条) |
| 主备切了但没生效 / 只支持特定场景 | 官方(22RC1):该特性只支持在大规模专家并行场景下使用 | 确认你的场景是否在支持范围内 |
⭐ 检索技巧(E1 讲过,这里再用一次):去掉具体化信息。
不要搜"我们的服务莫名不动了",要搜报错原文里的稳定特征词
(Service is not available、abnormal、npu_usage_threshold 等)。
三层判断法(本课的框架)
归纳(结合 D3/D4 的成果):
① 是"没就绪"还是"静默故障"还是"坏了"?
没就绪 → 等待 + 看启动日志
静默故障 → 看虚推 / GET /status(本课新增)
坏了 → 往下走第 ② 步
② 是"哪个角色"的问题?(D3)
Coordinator / Controller / EngineServer / NodeManager / Deployer
③ 是"算的环节"还是"搬的环节"?(D4)
算的 → 回 D2 / C 层
搬的 → KV 传输 / KV Cache Store / 池化
⭐ 这个三层法,是把 D2–D5 串起来用的方法 —— 本课是它最上层的那一问(就绪 / 静默故障 / 坏了)。
第九段 · 小结与下一课
三句话带走
- 官方全量配置示例已默认开启六项能力:主备倒换、异常实例重启、服务限流、 虚推健康检查、KV Cache 亲和调度、KV 池化。 你要学的不是"怎么开",而是"它是什么、现象长什么样、什么时候该调"。
- 最该记住的一个概念是"静默故障"(官方原文): "部分进程卡死,服务看似无问题,但无法正常推理" —— 虚推健康检查就是为它准备的;而它恰好能骗过"端口通不通"这一层。
- 官方给的万能报错映射:接口返回
{"detail":"Service is not available"}→ "多为 P/D 或 Coordinator 尚未完全就绪" → 等待重试 + 查看各 Pod 日志。
D 层到这里收官
回头看这一层讲了什么:
D1 L3 服务层在干什么、MindIE 是什么、官方推荐哪条路
D2 把服务跑起来(vLLM Ascend 三步走 + 三重验证)
D3 服务由哪些角色组成(Motor 五组件 + 端口规划)
D4 为什么要拆 P 和 D(计算密集 vs 访存密集 + KV 传输的代价)
D5 起来之后怎么活下去(可用性 / 健康 / 保护 / 调优)
D 层的一句话总结:
L1/L2 解决"一个请求怎么算对",L3 解决"一万个请求怎么算得完"。 这一层的所有设计(拆 P/D、限流、健康检查、KV 复用), 都是在"资源有限"这个前提下做的取舍。
本课在体系里的位置
─── L0 硬件与环境(A 层,已完成)─── A0 / A1 / A2 / A3
─── L1 基础软件(B 层,已完成)─── B1 / B2 / B3 / B4 / B5
─── L2 框架(C 层,已完成)─── C1 / C2 / C3
─── L3 服务(D 层,本课收官)───
D1 L3 服务层 ✅
D2 vLLM Ascend ✅
D3 MindIE Motor ✅
D4 PD 分离与混部 ✅
D5 可用性、健康与调优 ★ 本课
─── 排障(E 层)─── E1
本课留下的"钩子"
| 钩子 | 在哪一课填 |
|---|---|
| 服务侧的日志在哪、怎么收集与定位 | E 层(E1 讲了日志三处) |
| KV 池化 / KV 亲和的完整配置 | 官方《KV 池化部署指南》《KV Cache 亲和性调度》 |
| 量化(msModelSlim)——A0 地图里属于 L4 应用层 | D 层之后 |
| 950 系列服务器的预检查(官方文档有专门一节) | 官方《PD 分离服务部署指导》 |
课后自测(3 题)
- "静默故障"是什么样?为什么它特别危险?虚推健康检查用哪两种方式探测?
- 虚推有一个容易踩的限制条件——调日志级别会让你悄悄失去它。是什么条件?
- 接口返回
{"detail":"Service is not available"},官方说多半是什么原因?你该做什么?
附录 A · 本课引用的官方出处
| 内容 | 出处 |
|---|---|
| 「全量示例已默认开启主备倒换、异常实例重启、服务限流、虚推、KV 亲和性调度、KV 池化」 | 官方 MindIE《基于 K8s 多容器 PD 分离服务部署 · 特性配置指导》zh/mindie/310/mindiemotor/motordev/docs/zh/user_guide/deployment/k8s/pd_disaggregation_deployment.md |
| 主备倒换的原理、开启配置、关闭方式、「须提前部署 etcd(建议 3 副本)」 | 同上 |
| 异常实例重启的原理、「默认开启,无需额外操作」「无需关闭」 | 同上 |
服务限流的原理、rate_limit_config 三个字段与「60 秒内最多处理 10000 条请求」 |
同上 |
虚推健康检查的定义(含"静默故障表现为:部分进程卡死,服务看似无问题,但无法正常推理")、原理、配置、「仅允许在 ERROR 日志级别下开启」ASCEND_GLOBAL_LOG_LEVEL=3 与自动关闭并打印 warning |
同上 |
| KV Cache 亲和调度的目的、原理(依赖 Mooncake Conductor)、配置要点、「需要确保镜像中已安装 KV Conductor 组件」 | 同上 |
KV 池化的目的、原理(MultiConnector、P 推入 / D 拉取)、关闭方式、指向《KV 池化部署指南》 |
同上 |
运维技巧 · 服务未就绪:{"detail":"Service is not available"} →「多为 P/D 或 Coordinator 尚未完全就绪;等待后重试,并查看各 Pod 日志」 |
同上(附录 · 运维技巧) |
| 主备倒换的限制与约束(主备不支持同节点、依赖 ETCD 3 副本、ETCD v3.6、明文传输风险) | 官方 MindIE《集群管理组件 · Controller 主备倒换 · 使用样例》(22RC1)https://www.hiascend.com/document/detail/zh/mindie/22RC1/mindieservice/servicedev/mindie_service0371.html |
⚠️ 数据纪律提示: ① 上表主备倒换的限制条款出自 22RC1 版本文档(官方 3.1.0 文档指向该特性说明)—— 不同版本的具体约束可能不同,以对应版本文档为准; ② KV 池化的完整参数本课未搬运(官方明说"参数较多"),以官方指南为准; ③ 日志路径与收集方式本课未给出,以官方文档与 E1 为准; ④ 标注「归纳」的内容(六项能力分类、三层判断法、日志归属表)非官方口径。
附录 B · 本课出现的配置项与名词详解
配置块速查
| 配置块 | 位置 | 作用 |
|---|---|---|
standby_config.enable_master_standby |
motor_controller_config / motor_coordinator_config |
主备倒换开关 |
rate_limit_config |
motor_coordinator_config |
服务限流(enable_rate_limit / max_requests / window_size) |
health_check_config |
motor_engine_prefill_config(P 与 D 各自) |
虚推健康检查(enable_virtual_inference / npu_usage_threshold) |
scheduler_config.scheduler_type |
motor_coordinator_config |
设为 "kv_cache_affinity" 开启 KV 亲和调度 |
kv_conductor_config |
与 motor_engine_prefill_config 同层级 |
KV Conductor 配置 |
kv_cache_pool_config |
根节点 | KV 池化(删除即关闭) |
名词表
| 名词 | 一句话 |
|---|---|
| 主备倒换 | 多套 Controller/Coordinator 分布在不同服务器,主挂备接管 |
| 异常实例重启 | 实例异常退出时 Motor 自动重启(默认开启、无需关闭) |
| 服务限流 | Coordinator 限制单位时间最大请求数 |
| 虚推健康检查 | 小型推理请求 / 看 NPU 使用率,专治"静默故障" |
| 静默故障 | 部分进程卡死,服务看似无问题,但无法正常推理 |
| KV Cache 亲和调度 | 前缀相同的请求送到同一实例,复用 KV,省 Prefill |
| KV 池化 | KV Cache 卸载到共享池,跨实例复用,降显存压力 |
| Mooncake Conductor | KV 亲和调度依赖的社区组件 |
MultiConnector |
KV 池化用到的连接器 |
| etcd | 主备倒换依赖的协调服务(官方:建议 3 副本) |
ASCEND_GLOBAL_LOG_LEVEL=3 |
ERROR 级别;虚推只允许在此级别下开启(0=DEBUG/1=INFO/2=WARNING/3=ERROR) |
| AI Cube 利用率 | 虚推在业务繁忙时的探测判据(npu-smi info watch -s u 采集) |
| 虚推启用条件 | 须同时满足 6 条(vLLM / 开关 / ERROR 级别 / 阈值 / /health 正常 / DP rank 0) |
命令与现象
| 项 | 说明 |
|---|---|
{"detail":"Service is not available"} |
推理接口返回此内容 → 官方:多为 P/D 或 Coordinator 尚未完全就绪 |
kill -2 <PID> |
优雅停止(D2 讲过,相当于 Ctrl+C) |
npu_usage_threshold |
虚推判据之一:NPU 计算核心使用率阈值 |
kubectl get pods -n <namespace> -o wide |
看 Pod 状态;官方:Running ≠ 业务就绪 |
GET /v1/readiness(及 Triton 版 GET /v2/health/ready) |
官方就绪接口:200 = 已就绪,503 = 未就绪 |
npu-smi info watch -s u / -h |
看 AI Cube 利用率 / 查是否支持该指标(虚推前置) |
bash show_log.sh |
官方日志采集入口,输出到 log_collect/log/ |
bash delete.sh <namespace> |
官方终止 / 卸载服务 |
python3 deploy.py --config_dir ../infer_engines/vllm |
官方拉起 PD 分离服务 |
附:D3 / D4 / D5 的分工
| 问题 | 在哪一课 |
|---|---|
| Motor 五个子组件、部署形态、端口规划 | D3 |
| Prefill/Decode 的差别、PD 分离的动机与代价 | D4 |
| 六项能力(主备倒换/异常重启/限流/虚推/亲和/池化)、就绪判断、静默故障 | 本课(D5) |
| 日志收集与按层排错 | E 层 |