1. 为什么要在昇腾 910B 上折腾 DeepSeek 多机分布式推理
先把结论摆在前面:单卡 910B 跑 DeepSeek 这类 MoE 大模型,能跑,但跑不快,也跑不大。DeepSeek 系列模型动辄几百 GB 的权重,加上 MoE 架构里专家并行的特性,单机 8 卡往往只能勉强塞下一个中等规模版本,一旦上到满血版或者长上下文场景,显存直接爆掉。这时候多机分布式推理就不是"要不要做"的问题,而是"必须做"的问题。
我这次实操的目标很明确:用两台昇腾 910B 服务器(每台 8 卡,共 16 卡),通过 CubeStudio 平台配合 MindIE 推理引擎,把 DeepSeek 模型以多机分布式的方式跑起来。中间踩的坑主要集中在三块:hccn_tool 网卡配置、ranktable 生成、HCCL 通信参数调优。这三块任何一块出问题,表现都是"卡住不动"或者"通信超时",日志还特别不友好,排查起来相当费劲。
这篇文章适合谁看?如果你手上已经有昇腾 910B 的机器,想跑 DeepSeek 但卡在单机显存不够;或者你已经在尝试多机但被 HCCL 通信问题折磨得不行;再或者你是运维/算法工程师,需要把大模型推理服务化落地——那这篇内容应该能帮你省下不少时间。我会把每一步的操作、参数含义、为什么这么设,以及我实际踩过的坑都写清楚,尽量做到你照着抄就能跑通。
需要提前说明的是,下面涉及的具体 IP、设备号、路径都是我在测试环境里的实际值,你替换成自己环境的值即可。参数部分我会解释计算逻辑,不是让你死记硬背。
2. 整体方案设计与选型思路拆解
2.1 为什么选 MindIE 而不是自己搭推理框架
昇腾生态里跑大模型推理,绕不开 MindIE(Mind Inference Engine)。它是华为官方推出的推理引擎,底层对接 CANN,上层提供模型加载、KV Cache 管理、连续批处理(continuous batching)、PagedAttention 等能力。自己基于 PyTorch + torch_npu 手搓推理服务不是不行,但你要自己处理算子适配、显存管理、多机通信,工作量巨大且容易出隐性 bug。
MindIE 的核心优势在于它对昇腾硬件的亲和性。比如它内置了针对 910B 的 FlashAttention 实现、量化算子(W8A8、W4A16 等)、以及 HCCL 通信的封装。你只需要通过配置文件告诉它"模型在哪、几台机器、每台几张卡、用什么并行策略",剩下的它来搞定。
CubeStudio 在这里扮演的角色是"编排层"。它本身是一个面向 AI 全流程的平台,提供了推理服务的部署模板、资源调度、服务暴露等能力。用 CubeStudio 部署 MindIE,好处是你不用手动去每台机器上敲命令、传文件、起进程,平台会帮你把 ranktable、环境变量、启动脚本都分发下去。当然,前提是你得把配置写对。
2.2 并行策略怎么选:TP、PP 还是 EP
DeepSeek 是 MoE 架构,这决定了它的并行策略和稠密模型不太一样。常见的三种并行方式:
- TP(Tensor Parallel,张量并行):把单个权重矩阵切到多卡上,每张卡算一部分,然后 all-reduce 汇总。适合单层参数量大、卡间带宽高的场景。缺点是通信量大,跨机 TP 性能衰减明显。
- PP(Pipeline Parallel,流水线并行):把模型按层切分,不同卡负责不同层,数据像流水线一样流过。通信量比 TP 小,但会有流水线气泡(bubble),需要足够的 batch 来填满。
- EP(Expert Parallel,专家并行):MoE 专属,把不同的专家(expert)分配到不同卡上,token 根据路由结果发送到对应专家所在的卡。这是 DeepSeek 这类模型最关键的并行方式。
我的实际配置是:单机内用 TP=8,跨机用 PP=2 或者 EP。为什么这么选?因为 910B 的 HCCS(卡间高速互联)带宽远高于跨机的 RoCE 网络,所以把通信密集的 TP 放在机内,把通信相对稀疏的 PP/EP 放在跨机,是性价比最高的做法。
具体到 DeepSeek 的 MoE 层,如果专家数量多(比如 256 个专家),EP 的收益会非常明显,因为每个 token 只激活少数专家,通信量可控。但如果专家数少,EP 的负载均衡会成问题,这时候可能还是 TP+PP 更稳。
2.3 多机通信的物理层:RoCE 还是 HCCS
多机之间通信走什么网络,直接决定了你的推理吞吐。昇腾 910B 服务器通常配备多张 RoCE 网卡(比如 200Gbps 或 400Gbps),跨机通信就走这些网卡。这里的关键是:你必须确保所有参与通信的网卡都配置正确,且 HCCL 能识别到它们。
这就是 hccn_tool 出场的地方。hccn_tool 是昇腾提供的网卡配置工具,用来设置 RoCE 网卡的 IP、网关、MTU、以及最重要的——device 与网卡的绑定关系。如果这个绑定错了,HCCL 会走错网卡,轻则性能暴跌,重则直接通信失败。
3. 核心细节解析与实操要点
3.1 hccn_tool 网卡配置:最容易翻车的一步
hccn_tool 的配置逻辑是:每张 NPU(昇腾加速卡)需要绑定一张 RoCE 网卡用于跨机通信。在 910B 的典型服务器上,8 张 NPU 对应 8 张 RoCE 网卡(或者 4 张双口网卡)。你需要做的是:
- 确认每张 NPU 的 device ID 和对应的网卡名称。
- 给每张网卡配置同网段的 IP。
- 设置网卡的 MTU(建议 4200 或 8500,取决于交换机支持)。
- 用 hccn_tool 做连通性测试。
先看设备与网卡的对应关系。执行:
hccn_tool -i 0 -link -g这条命令查看 device 0 的链路状态。-i指定 device ID,-link是链路操作,-g是 get。如果返回link up,说明物理链路正常。
然后查看网卡信息:
hccn_tool -i 0 -netdetect -g这条会显示 device 0 绑定的网卡 IP、网关等信息。如果显示0.0.0.0,说明还没配。
配置 IP 的命令:
hccn_tool -i 0 -ip -s address 192.168.10.10 netmask 255.255.255.0这里-s是 set,address后面跟 IP 和掩码。注意:每张卡的 IP 必须在同一网段,且不能冲突。我用的规划是机器 A 的 8 张卡用 192.168.10.10~17,机器 B 用 192.168.10.20~27。
设置网关:
hccn_tool -i 0 -gateway -s gateway 192.168.10.1设置 MTU:
hccn_tool -i 0 -mtu -s mtu 4200MTU 这个值很关键。如果设得比交换机支持的大,会出现大包分片甚至丢包,表现为通信时好时坏。我建议先用 4200 测试,稳定后再尝试 8500。如果交换机不支持 jumbo frame,就老老实实用 1500。
配置完之后,做连通性测试:
hccn_tool -i 0 -ping -g address 192.168.10.20这条是从 device 0 ping 对端机器的 device 0。如果通,说明物理层和 IP 层都没问题。
注意:hccn_tool 的配置在重启后会丢失,需要写进开机脚本或者用持久化配置。我一开始没注意,重启后所有 IP 都没了,排查了半天才发现是这个原因。
3.2 ranktable 生成:多机推理的"通讯录"
ranktable 是 HCCL 用来识别集群拓扑的文件,本质是一个 JSON,告诉每个 rank(进程)它的 device ID、IP、端口、以及它在全局的编号。MindIE 多机推理必须提供正确的 ranktable,否则各进程互相找不到。
一个典型的两机 16 卡 ranktable 长这样:
{ "version": "1.0", "server_count": "2", "server_list": [ { "server_id": "192.168.10.10", "device": [ {"device_id": "0", "device_ip": "192.168.10.10", "rank_id": "0"}, {"device_id": "1", "device_ip": "192.168.10.11", "rank_id": "1"}, ... ] }, { "server_id": "192.168.10.20", "device": [ {"device_id": "0", "device_ip": "192.168.10.20", "rank_id": "8"}, ... ] } ] }几个关键点:
server_id是机器的管理 IP,不是 RoCE 网卡 IP。device_ip是每张卡绑定的 RoCE 网卡 IP,必须和 hccn_tool 配的一致。rank_id是全局唯一的,从 0 开始连续编号。机器 A 是 0~7,机器 B 是 8~15。device_id是卡在机器内的编号,每台机器都从 0 开始。
我踩过的坑:一开始把device_ip写成了管理 IP,结果 HCCL 初始化时一直报connection refused。因为 HCCL 是拿device_ip去建链的,管理 IP 上根本没有 HCCL 的监听端口。
生成 ranktable 有两种方式:手动写,或者用 MindIE 提供的脚本自动生成。手动写适合机器少、拓扑固定的场景;自动生成适合大规模集群。我这次是手动写的,因为只有两台机器,写起来也就几分钟。
3.3 HCCL 通信参数:决定性能的关键
HCCL 是昇腾的集合通信库,类似 NVIDIA 的 NCCL。它的行为由一堆环境变量控制,这些变量设得好不好,直接决定你的推理吞吐是 100 tokens/s 还是 20 tokens/s。
核心环境变量:
| 变量名 | 作用 | 推荐值 | 说明 |
|---|---|---|---|
| HCCL_IF_IP | 指定 HCCL 使用的网卡 IP | 本机 RoCE IP | 不设会随机选,可能选错 |
| HCCL_SOCKET_IFNAME | 指定 socket 通信网卡 | 如 eth0 | 用于控制面通信 |
| HCCL_INTRA_ROCE_ENABLE | 机内是否走 RoCE | 0 | 机内走 HCCS 更快 |
| HCCL_BUFFSIZE | 通信缓冲区大小 | 200 | 单位 MB,大模型建议调大 |
| HCCL_ALGO | 集合通信算法 | Ring | Ring 适合大包,Tree 适合小包 |
| HCCL_EXEC_TIMEOUT | 执行超时 | 600 | 秒,大模型加载慢,要调大 |
HCCL_IF_IP是最容易出问题的。如果不设,HCCL 可能选到管理网卡,导致跨机通信走错路径。我建议在启动脚本里显式 export。
HCCL_BUFFSIZE默认是 200MB,对于 DeepSeek 这种大模型,通信数据量大,可以调到 400 甚至 800。但注意,这个值受限于 NPU 的显存,调太大可能 OOM。
HCCL_EXEC_TIMEOUT默认好像是 180 秒,但 DeepSeek 加载权重就要好几分钟,如果不调大,会在加载阶段就超时退出。我设成了 600 秒。
4. 实操过程与核心环节实现
4.1 环境准备与依赖检查
在动手之前,先确认基础环境。每台机器上执行:
npu-smi info这条命令看 NPU 状态。正常应该显示 8 张卡,每张卡的显存、温度、功耗都正常。如果有卡显示health status: warning或者error,先解决硬件问题。
然后检查 CANN 版本:
cat /usr/local/Ascend/ascend-toolkit/latest/version.cfgMindIE 对 CANN 版本有要求,我用的组合是 CANN 8.0.RC2 + MindIE 1.0.RC2。版本不匹配会出现各种奇怪的算子报错。
检查 Python 环境和 MindIE:
python3 -c "import mindie; print(mindie.__version__)"如果导入失败,说明 MindIE 没装好或者 PYTHONPATH 没设对。
4.2 模型准备与权重转换
DeepSeek 的原始权重是 HuggingFace 格式(safetensors),MindIE 需要的是它自己的格式。转换用 MindIE 提供的脚本:
python3 -m mindie.tools.convert_weight \ --model_path /data/models/DeepSeek-V2 \ --save_path /data/models/DeepSeek-V2-mindie \ --dtype bf16这个转换过程比较慢,DeepSeek-V2 大概要 20~30 分钟。转换后的权重会按 TP 切分好,所以你要在转换时就指定 TP 大小。如果后面改了 TP,得重新转。
提示:转换权重时确保磁盘空间足够。DeepSeek-V2 的 bf16 权重约 500GB,转换后可能更大。我一开始磁盘只剩 600GB,转一半就满了,白等半小时。
4.3 MindIE 配置文件详解
MindIE 的推理配置是一个 JSON 文件,核心字段:
{ "model_name": "DeepSeek-V2", "model_path": "/data/models/DeepSeek-V2-mindie", "world_size": 16, "tp": 8, "pp": 2, "ep": 1, "max_seq_len": 8192, "max_batch_size": 32, "max_input_len": 4096, "max_output_len": 2048, "dtype": "bf16", "rank_table_file": "/data/ranktable.json", "npu_mem_util": 0.9 }world_size是总卡数,tp * pp * ep应该等于world_size(某些配置下 ep 和 tp 有重叠,具体看 MindIE 版本)。我这里是 8 * 2 * 1 = 16。
max_seq_len是最大序列长度,包括输入和输出。max_input_len + max_output_len不能超过它。这个值越大,KV Cache 占用越多。8192 对于大多数对话场景够用,如果要处理长文档,得调到 32768 甚至更高,但显存要相应增加。
npu_mem_util是显存利用率,0.9 表示用 90% 的显存。调太高容易 OOM,调太低浪费显存。建议从 0.85 开始试。
4.4 CubeStudio 部署流程
CubeStudio 的部署分几步:
- 创建推理服务:在平台上选择"MindIE 推理"模板,填写服务名称、模型路径、配置文件路径。
- 配置资源:指定使用哪些节点、每个节点几张卡。这里要确保选的节点和 ranktable 里的一致。
- 上传 ranktable:把生成好的 ranktable.json 上传到平台,或者放到共享存储里让各节点都能访问。
- 设置环境变量:把前面说的 HCCL 相关变量填进去。
- 启动服务:平台会分发配置、拉起进程、做健康检查。
启动后,看日志确认状态。正常的话会看到:
HCCL init success, rank 0 of 16 Model loaded, taking 245.3s Warmup done, ready to serve如果卡在HCCL init,八成是 ranktable 或网卡配置有问题。如果卡在Model loaded,可能是权重路径不对或者显存不够。
4.5 性能验证与压测
服务起来后,用 curl 或者 Python 客户端发请求测试:
curl -X POST http://localhost:1025/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "DeepSeek-V2", "messages": [{"role": "user", "content": "你好"}], "max_tokens": 100 }'看返回的 tokens/s。我实测 16 卡跑 DeepSeek-V2,batch=1 时约 35 tokens/s,batch=16 时约 280 tokens/s。这个数字和官方宣传有差距,主要是我的网络是 200Gbps RoCE,如果是 400Gbps 会更好。
压测可以用 MindIE 自带的 benchmark 工具:
python3 -m mindie.benchmark \ --host localhost \ --port 1025 \ --model DeepSeek-V2 \ --concurrency 16 \ --input_len 512 \ --output_len 256这个会输出吞吐、延迟、首 token 时间等指标。
5. 常见问题与排查技巧实录
5.1 HCCL 初始化失败
现象:日志报HCCL init failed, error code 0x...,进程卡住或退出。
排查思路:
- 先确认 hccn_tool 配置的 IP 能互相 ping 通。在每台机器上 ping 对端所有卡的 IP。
- 检查 ranktable 里的
device_ip是否和 hccn_tool 配的一致。 - 检查
HCCL_IF_IP是否设对。 - 检查防火墙是否放行了 HCCL 用的端口(默认 60000 左右)。
我遇到过一次,是因为机器 B 的某张卡 IP 配错了,导致 rank 12 一直连不上。hccn_tool 的 ping 测试能快速定位是哪张卡的问题。
5.2 推理过程中通信超时
现象:服务能起来,但推理时偶尔报HCCL timeout,或者响应特别慢。
排查思路:
- 检查 MTU 设置。如果交换机不支持 jumbo frame,MTU 设 4200 会导致大包丢包。改成 1500 试试。
- 检查网卡是否有丢包。用
hccn_tool -i 0 -stat -g看统计信息。 - 调大
HCCL_EXEC_TIMEOUT。 - 如果是偶发,可能是网络拥塞,考虑做 QoS 或者换更空闲的网段。
5.3 显存不足(OOM)
现象:加载模型时或推理时 OOM。
排查思路:
- 降低
npu_mem_util,比如从 0.9 降到 0.85。 - 减小
max_batch_size或max_seq_len。 - 检查是否有其他进程占用显存。
npu-smi info能看到每张卡的显存使用。 - 如果模型太大,考虑量化(W8A8)或者增加卡数。
5.4 常见问题速查表
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| HCCL init failed | ranktable 错误 / IP 不通 | 检查 ranktable 和 hccn_tool 配置 |
| 通信超时 | MTU 不匹配 / 网络拥塞 | 调 MTU,检查交换机 |
| OOM | 显存利用率过高 / batch 太大 | 降 npu_mem_util,减 batch |
| 加载慢 | 磁盘 IO 瓶颈 / 权重格式不对 | 用 SSD,确认权重已转换 |
| 推理结果乱码 | dtype 不匹配 / 算子问题 | 检查 dtype,升级 CANN |
| 服务起不来 | 端口占用 / 配置路径错 | 检查端口,确认路径存在 |
5.5 独家避坑技巧
- 先单机跑通再上多机。单机 8 卡能跑通,说明模型、权重、MindIE 配置都没问题,再上多机就只需要关注通信。我一开始直接上多机,结果模型和通信问题混在一起,排查效率极低。
- 保留一份最小可复现配置。把能跑通的最小配置(最少卡数、最小模型)存下来,出问题时用它做对照。
- 日志分级看。MindIE 的日志分 INFO、WARNING、ERROR。先看 ERROR,再看 WARNING,INFO 只在需要细节时看。全看会被淹没。
- 网络先测再跑。用
hccn_tool的 ping 和带宽测试工具,确认网络没问题再起服务。网络问题在推理时暴露,排查成本高得多。 - 版本对齐。CANN、MindIE、驱动、固件的版本要匹配。我遇到过 CANN 和驱动版本差一个小版本,导致 HCCL 行为异常。升级前先查兼容性矩阵。
6. 一些参数计算与调优经验
6.1 显存估算
DeepSeek-V2 的参数量约 236B(总参数),但 MoE 激活参数约 21B。bf16 下,权重占用约 236B * 2 bytes = 472GB。16 卡分摊,每卡约 29.5GB。加上 KV Cache、激活值、通信缓冲区,每卡至少需要 40GB。910B 单卡 64GB,够用但不宽裕。
KV Cache 的计算:2 * num_layers * num_heads * head_dim * max_seq_len * batch_size * dtype_size。DeepSeek-V2 有 60 层,如果 max_seq_len=8192,batch=32,KV Cache 会占用相当可观的显存。这也是为什么max_seq_len和max_batch_size不能同时设太大。
6.2 TP/PP 切分对通信的影响
TP=8 意味着每层都要做 all-reduce,通信量正比于 hidden_size * batch * seq_len。PP=2 只在层边界传激活值,通信量小得多。所以跨机用 PP 是明智的。
但如果 PP 太大,流水线气泡会浪费算力。一般 PP 不超过 4。我这次 PP=2,气泡影响可接受。
6.3 batch size 与吞吐的关系
batch 越大,吞吐越高,但延迟也越高。在线服务通常要平衡两者。我的经验是:先找到显存能承受的最大 batch,然后根据延迟要求适当下调。DeepSeek-V2 在 16 卡上,batch=32 时吞吐约 400 tokens/s,但首 token 延迟会到 2 秒左右。如果要求低延迟,batch 降到 8~16。
7. 后续可以继续优化的方向
跑通只是第一步。如果要把这套服务用到生产,还有几个方向可以优化:
量化:W8A8 量化能把权重和激活都压到 8bit,显存占用减半,吞吐提升明显。MindIE 支持量化推理,但需要先做量化校准。精度损失通常在可接受范围内,具体要看业务对精度的要求。
KV Cache 优化:MindIE 支持 PagedAttention,能减少显存碎片。还可以考虑 KV Cache 量化,进一步压缩。
动态批处理:MindIE 的 continuous batching 能动态合并请求,提高 GPU 利用率。配置里开启后,高并发场景吞吐提升明显。
多实例部署:如果单实例吞吐不够,可以在同一批卡上起多个实例,每个实例负责一部分请求,通过负载均衡分发。但要注意显存分配,别互相挤爆。
监控与告警:生产环境需要监控 NPU 利用率、显存、温度、通信延迟等指标。昇腾提供了 npu-smi 和相关的 exporter,可以接入 Prometheus + Grafana。
我个人在实际操作中的体会是,昇腾生态的文档相比 CUDA 生态还是偏少,很多问题得靠看日志和试错。但一旦跑通,稳定性还是不错的。关键是把网络和 ranktable 这两块基础打牢,后面调优就是锦上添花。如果你也在折腾类似的东西,建议先把单机跑顺,再一步步加机器,别一上来就搞大规模,那样排查问题会非常痛苦。