Ascend-SACT/Qwen3-Coder-480B-A35B-Instruct-w8a8
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

MA部署 Qwen3-Coder-480B A2四机大EP

1. 背景描述

1.1 硬件环境

型号卡数
910 B332

1.2 软件版本

软件名版本
vllm_ascendv0.11.0rc2
HDKAscend HDK 25.2.3
CANN8.3.RC2
HCS851

1.3 模型规格

模型系列详细模型下载地址
Qwen3Qwen3-Coder-480B-w8a8-QuaRothttps://modelers.cn/models/Modelers_Park/Qwen3-Coder-480B-A35B-Instruct-w8a8-QuaRot

1.4 问题描述

客户环境基于 vllm_ascend部署Qwen3-Coder-480B 四机大EP,需要支持健康检查及故障自动重拉功能,故需要自动化启动脚本。基于以上挑战,基于客户的实际环境给出了大EP部署脚本,以适配MA平台对接vllm_ascend实现Qwen3-Coder-480B大EP推理部署。

2 解决方案与结果简述

2.1 问题根因

MA(管理平台)尚未适配Qwen3-Coder-480B大EP部署的功能,MA生成的global_rank_table表不能直接用于Qwen3-Coder-480B大EP服务部署、且其余大EP所需操作均未脚本化。

2.2 解决措施

  1. MA平台镜像准备,需确认vllm官方镜像是否支持MA平台部署(即不需要制作)。
  2. 代码优化,主要涉及triton和mooncake相关。
  3. 脚本适配

脚本自动化,包括 下载权重、ranktable文件生成(需基于MA生成的global_rank_table,提取本机IP,使用生成脚本生成部署所需的ranktable.json)、启动脚本run_dp_template.sh修改、拉起服务等步骤。

2.3 结果

完成MA平台Qwen3-Coder-480B自动化脚本部署,完成健康检查探针开发,实现一键拉起大EP服务以及故障自动重拉。

3. 详细方案

3.1 部署资源获取

简要描述:安装版本配套依赖关系和客户要求模型,获取对应的权重和MindIE镜像、驱动,注意使用QuaRot后缀结尾的权重以获得较优的精度。

3.1.1 模型权重获取

Qwen3-Coder-480B-A35B-Instruct-w8a8-QuaRot

3.1.2 vllm_ascend镜像获取

v0.11.0rc2

3.1.3 驱动版本升级

客户环境升级驱动需联系客户侧相关人员操作

3.2 确认MA平台UI特性开关是否开启

简要描述:PD分离部署需确认MA是否支持多机多卡部署。

在"ModelArts" > "部署上线" > "在线服务" 中,单击 "部署",出现 "分布式推理" 开关选项。

1

3.3 代码优化(可选)

简要描述:主要涉及triton(未合入主线版本)的代码手动修改,以提升性能。

  1. 使用triton里的 qwen3_moe_opt.py 替换vllm/vllm/model_executor/models/qwen3_moe.py
  2. pip install 安装triton里的 triton_ascend-3.2.0.dev20250922-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

3.4 镜像适配

3.4.1 准备基础脚本

**简要描述:**将需要使用但无需频繁修改的脚本打包进镜像,方便后续部署。

将目录的 scripts_merge.zip 解压后添加至镜像的 ~/480b_test/ 目录下

3.4.2 用户修改为ma-user(可选)

**简要描述:**如果客户要求AI平台不允许使用root角色镜像,则需要基于MindIE官方镜像适配ma-user用户,参考如下Dockerfile制作。

#  使用指定的基础镜像
FROM xxx:xxx

#  创建用户 ma-user 并设置其主目录、用户ID、组ID等
RUN useradd -d /home/ma-user -m -u 1000 -g 100 -s /bin/bash ma-user && \
    #  创建模型存储目录
    mkdir -p /home/mind/model && \
    #  设置目录的所有权
    chown -R 1000:100 /home/mind && \
    chown -R 1000:100 /usr/local/* && \
    #  将默认的 sh 替换为 bash
    rm /bin/sh && ln -s /bin/bash /bin/sh

#  设置工作目录
WORKDIR /home/mind/model/

#  切换到新创建的用户
USER ma-user

#  定义容器启动时执行的命令(可选)
# ENTRYPOINT ["/bin/sh","-c","sh run.sh"]

注意:如果客户侧允许使用root角色运行镜像,则可省略该步骤。(实测MA851版本可支持root角色启动推理服务)。

将制作好的vllm镜像交由客户上传至MA镜像仓。

3.5 启动脚本

简要描述:自动化一键启动脚本,包括 下载权重、ranktable文件生成(需基于MA生成的global_rank_table,提取本机IP,使用生成脚本生成部署所需的ranktable.json)、启动脚本run_dp_template.sh及ip修改。可参考如下脚本。

3.5.1 P节点 run_dp_template.sh

#!/bin/bash
nic_name="eth0"

export VLLM_VERSION=0.11.0
export HCCL_IF_IP=71.10.29.128 # 本机ip
export GLOO_SOCKET_IFNAME=$nic_name # ifconfig 查询的本机网口
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name
export DISAGGREGATED_PREFILL_RANK_TABLE_PATH=disaggregated_prefill_v1/ranktable.json # 改成对应ranktable
export VLLM_LOGGING_LEVEL="info"
export OMP_PROC_BIND=false
export OMP_NUM_THREADS=100
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export HCCL_OP_EXPANSION_MODE="AIV"
export HCCL_BUFFSIZE=1024
export TASK_QUEUE_ENABLE=1
export VLLM_USE_VI=1
export HCCL_OP_RETRY_ENABLE="{L0:0, L1:0, L2:0}"
export ASCEND_RT_VISIBLE_DEVICES=$1
export VLLM_ASCEND_ENABLE_FLASHCOMM=1

export VLLM_TORCH_PROFILER_WITH_STACK=0
export VLLM_TORCH_PROFILER_DIR="/mnt/cache/profiling/"


vllm serve /mnt/cache/Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot \
    --host 0.0.0.0 \
    --enforce-eager \
    --port $2 \
    --enable-prefix-caching \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --enable-expert-parallel \
    --seed 1024 \
    --served-model-name Qwen \
    --max-model-len 262144 \
    --max-num-batched-tokens 4096 \
    --max-num-seqs 20 \
    --trust-remote-code \
    --gpu-memory-utilization 0.92 \
    --quantization ascend \
    --enable-auto-tool-choice \
    --chat-template /vllm-workspace/vllm/examples/tool_chat_template_qwen3coder.jinja \
    --tool-call-parser qwen3_coder \
    --kv-transfer-config \
    '{
        "kv_connector": "LLMDataDistCMgrConnector",
        "kv_buffer_device": "npu",
        "kv_role": "kv_producer",
        "kv_parallel_size": "1",
        "kv_port": "34569",
        "engine_id": "0",
        "kv_connector_module_path": "vllm_ascend.distributed.llmdatadist_c_mgr_connector"
    }' \
    --additional-config '{
        "ascend_scheduler_config":{"enabled":false, "enable_chunked_prefill":true},
        "enable_cpu_binding":true
    }'

3.5.2 D节点 run_dp_template.sh

#!/bin/bash
nic_name="eth0"

export VLLM_VERSION=0.11.0
export HCCL_IF_IP=71.10.29.128 # 本机ip
export GLOO_SOCKET_IFNAME=$nic_name # ifconfig 查询的本机网口
export TP_SOCKET_IFNAME=$nic_name
export HCCL_SOCKET_IFNAME=$nic_name
export DISAGGREGATED_PREFILL_RANK_TABLE_PATH=disaggregated_prefill_v1/ranktable.json # 改成对应ranktable
export VLLM_LOGGING_LEVEL="info"
export OMP_PROC_BIND=false
export OMP_NUM_THREADS=100
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export HCCL_OP_EXPANSION_MODE="AIV"
export HCCL_BUFFSIZE=1024
export TASK_QUEUE_ENABLE=1
export VLLM_USE_VI=1
export HCCL_OP_RETRY_ENABLE="{L0:0, L1:0, L2:0}"
export ASCEND_RT_VISIBLE_DEVICES=$1
export VLLM_ASCEND_ENABLE_FLASHCOMM=1

export VLLM_TORCH_PROFILER_WITH_STACK=0
export VLLM_TORCH_PROFILER_DIR="/mnt/cache/profiling/"


vllm serve /mnt/cache/Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot \
    --host 0.0.0.0 \
    --port $2 \
    --enable-prefix-caching \
    --data-parallel-size $3 \
    --data-parallel-rank $4 \
    --data-parallel-address $5 \
    --data-parallel-rpc-port $6 \
    --tensor-parallel-size $7 \
    --enable-expert-parallel \
    --seed 1024 \
    --served-model-name Qwen \
    --max-model-len 262144 \
    --max-num-batched-tokens 4096 \
    --max-num-seqs 20 \
    --trust-remote-code \
    --gpu-memory-utilization 0.92 \
    --quantization ascend \
    --enable-auto-tool-choice \
    --chat-template /vllm-workspace/vllm/examples/tool_chat_template_qwen3coder.jinja \
    --tool-call-parser qwen3_coder \
    --compilation-config '{"cudagraph_capture_sizes":[4,8,12,16,20,40,80],"cudagraph_mode": "FULL_DECODE_ONLY"}' \
    --kv-transfer-config \
    '{
        "kv_connector": "LLMDataDistCMgrConnector",
        "kv_buffer_device": "npu",
        "kv_role": "kv_consumer",
        "kv_parallel_size": "1",
        "kv_port": "34569",
        "engine_id": "0",
        "kv_connector_module_path": "vllm_ascend.distributed.llmdatadist_c_mgr_connector"
    }' \
    --additional-config '{
        "ascend_scheduler_config":{"enabled":false, "enable_chunked_prefill":true},
        "enable_cpu_binding":true
    }'

3.5.3 start.sh

set -x
echo "server start!"

# 0. 下载权重
mkdir -p /mnt/cache/profiling/
cd /mnt/cache/
WEIGHTS_DIR="Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot"
if [ ! -d "$WEIGHTS_DIR" ];then
    echo "下载权重"
    obsutil config -i=xxx -k=xxx -e=xxx
    obsutil cp obs://xxx/Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot/ ./ -r -f &
    sleep 5m
else
    echo "权重目录已存在"
fi
# cp -f /mnt/cache/Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot/tokenizer_config.json.raw /mnt/cache/Qwen3-Coder-480B-A35B-Instruct-W8A8-QuaRot/tokenizer_config.json

# 1. 更新生成ranktable.json(需要4台一起生成)
MS_GLOBAL_RANKTABLE_TABLE=/user/global/config/global_rank_table.json

while true; do
    json_string=$(cat $MS_GLOBAL_RANKTABLE_TABLE)
    echo $json_string
    RESULT=$(jq -r '.status' $MS_GLOBAL_RANKTABLE_TABLE 2>/dev/null)
    echo $RESULT
    if [ "$RESULT" = "completed" ]; then
        echo "MA ranktable is completed ;"
        break;
    fi
    sleep 1;
done;

cd ~/480b_test/scripts_merges/disaggregated_prefill_v1
rm -rf ranktable.json

host_IP=$(hostname -i | xargs)
echo "host_IP = $host_IP"
sed -i 's|LOCAL_HOSTS=("71.10.29.128")|LOCAL_HOSTS=("'$host_IP'")|g' ./gen_ranktable.sh

netstat -anop

NODE_IPS=$(jq -r '.server_group_list[].server_list[].server_ip' $MS_GLOBAL_RANKTABLE_TABLE)
echo $NODE_IPS
IFS=$'\n' read -r -d '' -a ip_array <<< "$NODE_IPS"
P0="${ip_array[0]}"
P1="${ip_array[1]}"
D0="${ip_array[2]}"
D1="${ip_array[3]}"
echo "P0 = $P0"
echo "P1 = $P1"
echo "D0 = $D0"
echo "D1 = $D1"

bash gen_ranktable.sh -ips $P0 $P1 $D0 $D1 --npus-per-node 8 --network-card-name eth0 --prefill-device-cnt 16 --decode-device-cnt 16

# 2. 修改run_dp_template.sh环境配置
cd ~/480b_test/scripts_merges/
if [[ "$host_IP" = "$P0" ]] || [[ "$host_IP" = "$P1" ]]; then
    echo "pppppp"
    cp /mnt/obs/qwen3_coder_480b/scripts/run_dp_template_p.sh run_dp_template.sh
else
    echo "ddddddd"
    cp /mnt/obs/qwen3_coder_480b/scripts/run_dp_template_d.sh run_dp_template.sh
fi

sed -i 's|HCCL_IF_IP=71.10.29.128|HCCL_IF_IP="'$host_IP'"|g' ./run_dp_template.sh

# 3. 拉起服务
# [dp=4, test=4]
# cp -f /mnt/obs/qwen3_coder_480b/launch_online_dp.py ./
cd ~/480b_test/scripts_merges/
mkdir -p /mnt/cache/480b_log/

if [ "$host_IP" = "$P0" ]; then
    echo "p0"
    nohup python launch_online_dp.py --dp-size 4 --tp-size 4 --dp-size-local 2 --dp-rank-start 0 --dp-address $P0 --dp-rpc-port 12345 --vllm-start-port 9010 2>&1 | tee /mnt/cache/480b_log/$(date +%Y-%m-%d_%H-%M-%S)_$(hostname)_run.log &
elif [ "$host_IP" = "$P1" ]; then
    echo "p1"
    nohup python launch_online_dp.py --dp-size 4 --tp-size 4 --dp-size-local 2 --dp-rank-start 2 --dp-address $P0 --dp-rpc-port 12345 --vllm-start-port 9010 2>&1 | tee /mnt/cache/480b_log/$(date +%Y-%m-%d_%H-%M-%S)_$(hostname)_run.log &
elif [ "$host_IP" = "$D0" ]; then
    echo "d0"
    nohup python launch_online_dp.py --dp-size 4 --tp-size 4 --dp-size-local 2 --dp-rank-start 0 --dp-address $D0 --dp-rpc-port 12345 --vllm-start-port 9010 2>&1 | tee /mnt/cache/480b_log/$(date +%Y-%m-%d_%H-%M-%S)_$(hostname)_run.log &
elif [ "$host_IP" = "$D1" ]; then
    echo "d1"
    nohup python launch_online_dp.py --dp-size 4 --tp-size 4 --dp-size-local 2 --dp-rank-start 2 --dp-address $D0 --dp-rpc-port 12345 --vllm-start-port 9010 2>&1 | tee /mnt/cache/480b_log/$(date +%Y-%m-%d_%H-%M-%S)_$(hostname)_run.log &
fi

if [ "$host_IP" = "$P0" ]; then
    echo "this is p0 and proxy"
    echo "http server start!"
    timeout 7h python -m http.server 1025
    echo "http.server stoped"

    echo "start proxy"
    cd ~/480b_test/scripts_merges/ &
    nohup python disaggregated_prefill_v1/load_balance_proxy_server_example.py \
        --host $P0 \
        --port 1025 \
        --prefiller-hosts $P0 $P0 $P1 $P1 \
        --prefiller-port 9010 9011 9010 9011 \
        --decoder-hosts $D0 $D0 $D1 $D1 \
        --decoder-port 9010 9011 9010 9011 2>&1 | tee /mnt/cache/480b_log/access.log &

    echo "server start end!"
    sleep 3600000000
fi

python -m http.server 1025
echo "server start end!"
sleep 3600000000

将这三份启动脚本 上传至obs的 xxx/scripts/ 目录下。

3.6 健康检查

健康检查脚本需要能够检测服务是否正常运行,通过构造推理请求观察服务端是否正常响应来判断,也需要结合华为云ModelArts一起使用。

3.6.1 健康检查脚本

健康检查脚本注意点如下:

  1. 在健康检查失败时,需要能够查看日志确定失败原因。
  2. 构造推理请求时,通过限定max_tokens避免响应内容过长占用较多npu资源。
  3. 需要设置请求超时时间避免服务端卡死不返回问题,设置的超时时间需要与服务的卡数、吞吐相匹配,例如客户4机32卡环境,输入16k输出10k要求并发数80,该情况下实测推理请求最长等待不超过15分钟,考虑冗余脚本设置超时时间20分钟,也需要和MA部署的健康检查配置匹配。
  4. 检测请求是否正常响应时,除了检测HTTP请求状态码外,还需要检查响应内容是否正确,例如4机PD分离环境中,在D节点异常退出时,发送推理请求仍能接收到200 OK的正常响应,但响应内容为空,需要补充解析响应内容。

完整健康检查脚本vllm_probe.py如下:

3.6.2 MA健康检查相关配置

完整流程会在后续创建AI应用、部署服务中说明,此处简要说明与健康检查相关配置:

  1. 将健康检查脚本放置在OBS中,并通过存储挂载的方式映射至容器内: 2
  2. 创建AI应用时配置健康检查,参考配置如下: 3 注意点说明: (1) 与通常部署在MA上的mindie服务不同,vllm并不区分start、ready状态,无需配置启动探针。 (2) 就绪探针周期用于确定识别服务启动成功,参考配置表示10分钟后开始检查,之后每隔200秒检查1次,返回成功后将会标记服务启动成功。 (3) 存活探针周期不应超出在vllm_probe.py中设置的timeout时间,可以保持一致,健康检查周期与超时时间保持一致,最大失败次数2即说明服务最多在2 * 1200 s异常后重新拉起。

3.7 function call功能配置

要支持function call功能,需要在启动服务时,附带qwen3-coder-480b对应的function call参数,参考命令如下:

4

注意其中手动指定了chat-template,为镜像中集成的tool_chat_template_qwen3coder.jinja,是因为若不指定默认使用模型自带的tokenizer_config.json,客户环境中并没更新最新的权重文件,导致响应内容参数类型不正确:

5

测试function call的方法由于已经有较多文章说明,例如可以参考qwen3官网function call指导,不再本文中重复说明。

3.8 隔离资源池

简要描述:隔离资源池可确保操作范围仅影响到本资源池内的所有物理节点。

单实例大EP需4机,1P1D

3.9 创建AI应用

简要描述:此步骤需要在MA平台上进行操作,完成AI应用的创建。

在“AI应用 > 自定义应用”中,单击“创建应用”,创建AI应用。

元模型来源:选择“从容器镜像中选择”。

容器调用接口:以制作的vllm镜像为准,下图以HTTP协议,1025端口为例(修改端口涉及较多配置和启动脚本的修改,建议保持该值)。

配置AI应用元模型及健康检查

6

健康检查脚本可参考3.6节:

*注意健康检查探针使用时需要调整适配,防止探测失败。

参数设置完成后,单击“立即创建”创建AI应用。

3.10 基于AI应用部署PD分离服务

在“部署上线 > 在线服务”中,单击“部署”。在部署服务页面参数设置完成后,即可部署PD分离服务。参数设置如下:

7

8

注意:将obs 的 xxx/scripts/ 路径映射到 /mnt/obs/qwen3_coder_480b/ 目录

等待服务状态变为“运行中”即可。

4. 解决效果

(1) 性能提升:该大EP部署方案可基本满足客户性能要求;

(2) 效率提升:客户现场开发适配MA和vllm_ascend大EP脚本,实现一键式拉起服务,整体部署效率提升300%,实现15分钟内完成服务部署。