本文面向希望在国产 AI 加速卡上部署大模型服务的工程师,记录一次从“接口能访问但真实推理不可用”到“四卡多副本稳定运行”的排障和部署过程。
一、卡信息与问题背景
1. 硬件和软件环境
本次环境使用海光 K100AI,单卡显存约 64GB,四卡服务器使用 gfx928 架构;软件环境包括 DTK 26.04、对应驱动,以及基于 das 分支的 vLLM 0.18.1 运行时。当前部署模型为 Qwen3.6-35B-A3B,采用 MoE 架构,本文的结论针对“这类 K100AI 硬件、当时的 DTK 用户态和 das vLLM 组合”得出,不等于 K100AI 永远不支持 TP
tip: 旧服务采用两卡 Tensor Parallel,也就是把同一个模型层拆到两张卡上共同计算。容器能够启动,模型列表接口也能快速返回,但真正发送聊天请求时,每生成一个 token 大约需要 17 秒,生成两个 token 就要 35.6 秒,客户端基本必然超时。因此,模型列表接口只能证明 HTTP 服务还活着,不能证明推理链路正常。生产验收必须发送固定长度的真实请求,同时记录首 token 延迟、完整响应时间、生成速度和加速卡计算利用率。
2.问题确认与优化
当时的现象是:两张卡的显存已经装入模型权重,但 hy-smi 观察到 HCU 利用率长期接近 0;日志没有 OOM、进程崩溃或明显的 CUDA 错误,只有启动阶段的通信和 cudagraph 警告。这更像是任务在多卡同步或执行阶段等待,而不是模型没有加载。
为了避免把问题归因到单一模型或单一版本,做了多组对照:更换 vLLM 版本、尝试 SGLang、调整 all-reduce 选项,并用更小的 Qwen2.5-0.5B 做模型架构对照。只要使用 TP2,真实推理仍然极慢;改为 TP1 后同一台服务器立即恢复正常。PP4 也测试过,但只有约 0.6 tok/s,仍达不到在线服务要求。最终能够确认的是:**在本次 gfx928、DTK 26.04 和 das vLLM 组合下,TP≥2 的多卡执行链路不可用,TP1 是已经通过实测的稳定路径。**如果软件栈发生变化,仍然要重跑同样的矩阵,而不是只凭版本号判断。
3. TP、PP 和多副本的区别
Tensor Parallel(TP)是把同一层的大矩阵拆到多张卡,同一个请求每生成一步都需要卡间同步。它适合单卡放不下模型的情况,但非常依赖卡间通信和运行时实现。
Pipeline Parallel(PP)是把模型的不同层放到不同卡,数据依次经过各阶段。它能分摊权重,但会有流水线空泡和阶段传输,单请求速度未必理想。
多副本则是每张卡各放一份完整模型。请求先到网关,再分给不同副本。它主要提高并发处理能力,一个请求仍然只使用一个副本,不会自动把单请求速度变成四倍。
二、最终解决方案
1. 先量化,让模型能够单卡运行
原始 BF16 权重约 67GB,单卡约 64GB,即使勉强放入也没有空间留给 KV Cache、激活值和运行时开销,因此不能直接把 TP2 改成 TP1。
本方案采用 W8A8 量化,让完整模型能够放入单张 64GB 卡,并为 KV Cache、激活值和运行时留出空间。W8A8 可以简单理解为:权重和激活使用 8 位整数表示和计算,在显著减少显存占用的同时保留可接受的推理效果。具体权重大小以实际下载的 Qwen3.6 量化产物为准。
量化格式必须和推理运行时匹配。使用 llmcompressor 自行生成的混合量化权重虽然能够加载,但输出出现重复字符和乱码;换成已经针对 K100AI 运行时验证过的 metax W8A8 权重后,输出恢复正常。
“权重能加载”不等于“量化格式可用”。必须用固定问题验证输出内容,再观察速度和计算利用率。对于 GDN/linear attention 层,还要确认权重目录包含对应的 scale 文件,不能只检查文件是否存在。
metax 权重还需要在模型配置中补充 vLLM 识别所需的 compressed-tensors 描述。修改配置前应备份原文件,修改后重新解析 JSON;ignore 列表必须来自已验证的同结构配置,不能凭经验随意排除 linear_attn,因为本次权重已经对该层进行了量化。
2. 用四个 TP1 副本替代 TP2
量化后,每张卡可以独立运行一份完整模型。最终形态是四个 TP1 副本,分别绑定四张卡;每个副本采用独立容器、独立编译缓存和只读模型挂载。
flowchart LR
U[调用方] --> H[HAProxy 统一入口]
H --> R0[卡 0:TP1 副本]
H --> R1[卡 1:TP1 副本]
H --> R2[卡 2:TP1 副本]
H --> R3[卡 3:TP1 副本]
R0 --> M[(只读 W8A8 权重)]
R1 --> M
R2 --> M
R3 --> M
HAProxy 使用最少连接策略,把新请求发给当前连接数较少的副本,并每隔几秒访问副本的健康检查接口。副本连续失败会被自动摘除,恢复后再挂回。这样可以绕开 TP 多卡同步问题,同时让四张卡都参与服务。
3. 在单卡基线之上叠加优化
单卡副本先以不带高级优化的配置跑通,再逐项增加能力,避免多个变量同时变化导致无法定位问题。
- MTP 投机解码:先由投机头提出候选 token,再由主模型验证。本次投机 token 数量设为 2,单副本从约 51 tok/s 提升到约 65 tok/s;高并发出现输出退化时可以删除该参数回退。
- 128K 上下文:提高长文档处理能力,但会占用更多 KV Cache,需要按显存和并发重新评估。
- 前缀缓存:长文档前缀不变、只改变末尾问题时,可以复用前面已经计算过的缓存。本次 80K 前缀首轮约 187 秒,命中后约 7.3 秒;GDN 结构需要使用 align 模式。
三、脱敏后的部署流程
1. 准备硬件和运行时
先在目标服务器确认驱动、运行时和四张卡状态:
hy-smi
docker version
确认设备数量、显存和运行时版本后,再确定使用哪个推理镜像。镜像应锁定内容摘要,避免同名标签在仓库中漂移。不同厂商的卡需要使用对应的设备节点、环境变量和官方适配镜像。
2. 准备模型权重
以下命令仅为公开示例,目录和模型来源应替换为实际可访问的位置:
pip install modelscope
modelscope download --model <已验证的-Qwen3.6-35B-A3B-W8A8-仓库> \
--local_dir /srv/models/Qwen3.6-35B-A3B-W8A8
下载后检查权重文件、配置文件和 scale 文件,并计算校验值。不要把下载凭据或内部仓库地址写进公开文档。
3. 准备注入后的量化配置
在容器内或具备写权限的临时目录修改模型 config.json,注入 compressed-tensors 的量化说明。配置的含义是:模型采用 W8A8,权重按通道保存,激活按 token 动态量化;需要排除的模块必须与当前模型结构匹配。
修改完成后至少做三项检查:JSON 可以正常解析;模型配置仍然保留原始架构字段;linear_attn 没有被错误地加入忽略列表。原始配置和修改后的配置都要保留备份。
4. 启动单个 TP1 副本
下面是参数含义示例,不是绑定某一台服务器的完整命令:
vllm serve /model \
--served-model-name qwen3.6-35b-a3b \
--tensor-parallel-size 1 \
--quantization compressed-tensors \
--max-model-len 131072 \
--max-num-seqs 4 \
--enable-prefix-caching \
--mamba-cache-mode align \
--speculative-config '{"method":"mtp","num_speculative_tokens":2}'
真实部署还需要按目标卡补充 dtype、设备可见性、显存比例、模型模板、工具调用解析器、编译模式和运行时库路径。K100AI 的补丁、MoE 调优文件、设备节点和环境变量不能直接复制给 H100 或其他卡。
第一次启动通常会执行权重加载、编译和 cudagraph 捕获,耗时较长属于正常现象。上线前应单独发送 warmup 请求,让剩余 kernel 编译在接入正式流量前完成。
5. 为每张卡建立独立副本
将单副本启动命令复制成四份,只修改卡号、端口、容器名和缓存目录。每个副本的编译缓存必须独立,否则并发编译可能互相覆盖。
export VLLM_API_KEY='替换为本环境自己的密钥'
# 分别以 HIP_VISIBLE_DEVICES=0、1、2、3 启动四个副本
# 端口示例:8001、8002、8003、8004
公开文档中只展示环境变量占位符,密钥应由密钥管理系统、受限环境文件或运行平台注入,不能写入 shell 脚本和 Git 仓库。
6. 配置 HAProxy 统一入口
HAProxy 的核心职责是统一入口、最少连接分发和健康检查。配置示例:
frontend model_api
bind :8000
default_backend model_replicas
backend model_replicas
balance leastconn
option httpchk GET /health
server replica0 127.0.0.1:8001 check
server replica1 127.0.0.1:8002 check
server replica2 127.0.0.1:8003 check
server replica3 127.0.0.1:8004 check
实际环境还应设置合理的连接、队列、客户端和服务端超时,并将统计页限制在管理网或本机地址。修改后先检查配置,再热加载服务:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
四、验收和故障回退
1. 功能验收
至少验证模型列表、基础对话、多轮对话、工具调用闭环、40K 以上长上下文和四个副本健康检查。工具调用不能只看模型返回了工具名,还要确认“工具请求 → 工具执行 → tool-result 回填 → 最终回答”完整闭环。
2. 性能验收
固定 prompt、输出长度、并发数和缓存状态,分别记录 TTFT、完整耗时、完成 token 数、decode tok/s、显存和 HCU。不能用一次偶然的最快结果代替稳定基线。
历史同类部署的参考结果是:TP2 异常状态约 0.06 tok/s;W8A8 TP1 单副本约 47 至 51 tok/s;叠加 MTP 后约 62 tok/s;四副本聚合约 240 tok/s,短请求 TTFT 约 0.21 秒。**这些数字用于说明优化量级,不直接作为 Qwen3.6 的交付结果。**Qwen3.6 上线时必须使用相同压测口径重新记录结果;迁移到其他卡时也应建立目标卡自己的基线。
3. 常见问题和处理顺序
| 现象 | 先检查什么 | 处理方向 |
|---|---|---|
| 模型列表正常但聊天极慢 | 真实请求耗时、HCU、日志 | 优先做 TP1/TP2 对照,不要只重启容器 |
| 输出乱码或重复 | 量化格式、scale 文件、补丁和配置 | 换用已验证权重,回退最近新增优化 |
| 首次请求很慢 | 编译日志、缓存目录 | warmup;确认每个副本缓存独立 |
| 上下文超限 | prompt、max_tokens、KV Cache | 降低上下文或并发,重新测量 |
| 个别副本异常 | /health、容器日志、对应卡状态 | 先让 HAProxy 摘除,再单独重启副本 |
| MTP 高并发输出退化 | 并发数和输出一致性 | 删除 MTP 参数,保留基础 TP1 服务 |
| 前缀缓存异常 | GDN 缓存模式和命中条件 | 使用 align;仍异常时关闭前缀缓存 |
4. 回退原则
优化必须逐项叠加、逐项可撤销。MTP、前缀缓存和 128K 上下文都属于增强项,出现问题时可以先回到已经验证过的 W8A8 TP1 基线。无论发生什么故障,都不能把请求降级到未经隔离的宿主机执行,也不能以“接口成功”掩盖真实推理失败。
五、可迁移的方法与不可直接迁移的内容
可以迁移的是排障思路:把接口可达和真实推理分开;用固定请求测量速度;同时观察计算卡利用率;通过模型、引擎、并行方式和版本矩阵做对照;先跑单卡基线,再增加量化、投机解码、缓存和负载均衡;最后补齐健康检查、监控、回滚和故障演练。
不能直接迁移的是 K100AI 的具体运行材料:DTK 镜像、/dev/kfd 等设备节点、HIP/DTK 环境变量、K100AI 运行补丁、fused MoE 调优配置和本次验证过的量化格式。迁移到 NVIDIA H100 时,应重新选择 CUDA、驱动和 vLLM 镜像,验证 BF16、FP8 或其他权重格式,并重新评估 NVLink/NCCL 下的 TP 能力。
六、结论
这次部署的关键不是盲目增加启动参数,而是先用真实推理数据定位问题,再根据硬件和运行时约束调整架构:TP2 在当前软件栈下不可用,就先量化让模型落到单卡,再用四个 TP1 副本把四张卡利用起来;在稳定基线之上,按实测结果增加 MTP、128K 上下文和前缀缓存。任何优化都必须以真实输出、速度、资源占用和可回退为验收条件。