在使用容器计算服务 ACS(Container Compute Service)算力时,您无需深入了解底层硬件,也无需涉及GPU节点管理和配置即可开箱即用。ACS部署简单、支持按量付费,非常适合用于LLM推理任务,可以有效降低推理成本。本文介绍如何使用ACS GPU算力部署生产可用的Qwen3-32B模型推理服务。
背景信息
Qwen3-32B模型
vLLM
前提条件
首次使用阿里云容器计算服务 ACS(Container Compute Service)时,需要为服务账号授予系统默认角色。当且仅当该角色被正确授予后,ACS才能正常地调用相关服务(ECS、OSS、NAS、CPFS、SLB等),创建集群以及保存日志等。具体操作,请参见首次使用容器计算服务。
已使用kubectl连接Kubernetes集群。具体操作,请参见获取集群kubeconfig并通过kubectl工具连接集群。
GPU实例规格和成本预估
在推理阶段主要占用显存的是模型参数,可以通过以下公式计算。
模型参数量为:32B(即320亿),精度数据类型字节数为:默认精度16位浮点数 / 8位每字节 = 2字节。
除了加载模型占用的显存之外,还需要考虑运算时所需的KV Cache大小和GPU利用率,通常会预留一部分buffer,因此推荐使用80 GiB显存以上的资源配置:GPU:1卡,CPU:22 vCPU,内存:128 GiB。您可以参考规格推荐表和GPU计算类型卡型规格来选择合适的实例规格。关于如何计算ACS GPU实例产生的费用,请参见计费说明。
在使用ACS GPU实例时,实例规格同样遵循ACS Pod规格规整逻辑。
ACS Pod默认提供30 GiB的免费的临时存储空间(EphemeralStorage),本文中使用的推理镜像占用约9.8 GiB。如果该存储空间大小无法满足您的需求,您可以自定义增加临时存储空间大小。详细操作,请参见增加临时存储空间大小。
操作步骤
Qwen3-32B模型文件大小约65G,下载和上传模型文件通常需要较长时间。您可以提交工单,快速将模型文件复制到您的OSS Bucket。
步骤一:准备模型数据
大语言模型因其庞大的参数量,需要占用大量的磁盘空间来存储模型文件。建议您使用NAS存储卷或OSS存储卷来持久化存储模型文件,以下步骤以使用OSS存储Qwen3-32B模型文件作为示例。
执行以下命令下载Qwen3-32B模型。
说明请确认是否已安装git-lfs插件,如未安装可执行
yum install git-lfs
或者apt-get install git-lfs
安装。更多的安装方式,请参见安装git-lfs。git lfs install GIT_LFS_SKIP_SMUDGE=1 git clone https://www.modelscope.cn/Qwen/Qwen3-32B.git cd Qwen3-32B git lfs pull
在OSS中创建目录,将模型上传至OSS。
说明关于ossutil工具的安装和使用方法,请参见安装ossutil。
ossutil mkdir oss://<your-bucket-name>/models/Qwen3-32B ossutil cp -r ./Qwen3-32B oss://<your-bucket-name>/models/Qwen3-32B
创建PV和PVC。为目标集群配置名为
llm-model
的存储卷PV和存储声明PVC。具体操作,请参见使用OSS静态存储卷。控制台操作示例
以下为示例PV的基本配置信息:
配置项
说明
存储卷类型
OSS
名称
llm-model
访问证书
配置用于访问OSS的AccessKey ID和AccessKey Secret。
Bucket ID
选择上一步所创建的OSS Bucket。
OSS Path
选择模型所在的路径,如
/models/Qwen3-32B
。以下为示例PVC的基本配置信息:
配置项
说明
存储声明类型
OSS
名称
llm-model
分配模式
选择已有存储卷。
已有存储卷
单击选择已有存储卷链接,选择已创建的存储卷PV。
kubectl操作示例
以下为示例YAML:
apiVersion: v1 kind: Secret metadata: name: oss-secret stringData: akId: <your-oss-ak> # 配置用于访问OSS的AccessKey ID akSecret: <your-oss-sk> # 配置用于访问OSS的AccessKey Secret --- apiVersion: v1 kind: PersistentVolume metadata: name: llm-model labels: alicloud-pvname: llm-model spec: capacity: storage: 150Gi accessModes: - ReadOnlyMany persistentVolumeReclaimPolicy: Retain csi: driver: ossplugin.csi.alibabacloud.com volumeHandle: llm-model nodePublishSecretRef: name: oss-secret namespace: default volumeAttributes: bucket: <your-bucket-name> # bucket名称 url: <your-bucket-endpoint> # Endpoint信息,如oss-cn-hangzhou-internal.aliyuncs.com otherOpts: "-o umask=022 -o max_stat_cache_size=0 -o allow_other" path: <your-model-path> # 本示例中为/models/Qwen3-32B/ --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: llm-model spec: accessModes: - ReadOnlyMany resources: requests: storage: 30Gi selector: matchLabels: alicloud-pvname: llm-model
步骤二:部署模型
执行下列命令,基于vLLM模型推理框架部署Qwen3-32B模型的推理服务。
该推理服务暴露与OpenAI兼容的HTTP API。下列命令将模型参数文件视作是一种特殊类型的数据集合,挂载到推理服务容器的指定位置(/models/Qwen3-32B
)。--max-model-len
设置了该模型最大可处理的Token长度,增大该配置项可获得更好的模型对话效果,但是可能会占用更多GPU显存资源。
kubectl apply -f- <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: qwen3-32b
name: qwen3-32b
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: qwen3-32b
template:
metadata:
labels:
app: qwen3-32b
alibabacloud.com/compute-class: gpu
alibabacloud.com/compute-qos: default
alibabacloud.com/gpu-model-series: GU8TF
spec:
volumes:
- name: model
persistentVolumeClaim:
claimName: llm-model
- name: dshm
emptyDir:
medium: Memory
sizeLimit: 64Gi
containers:
- command:
- sh
- -c
- vllm serve /models/Qwen3-32B/ --port 8000 --trust-remote-code --max-model-len 32768 --gpu-memory-utilization 0.98
image: acs-registry-vpc.cn-beijing.cr.aliyuncs.com/egslingjun/inference-nv-pytorch:25.05-vllm0.8.5.post1-pytorch2.7-cu128-20250513-serverless
name: vllm
ports:
- containerPort: 8000
readinessProbe:
tcpSocket:
port: 8000
initialDelaySeconds: 30
periodSeconds: 30
resources:
limits:
nvidia.com/gpu: "1"
cpu: "22"
memory: 128G
volumeMounts:
- mountPath: /models/Qwen3-32B/
name: model
- mountPath: /dev/shm
name: dshm
---
apiVersion: v1
kind: Service
metadata:
name: qwen3-32b
spec:
type: LoadBalancer
ports:
- port: 8000
protocol: TCP
targetPort: 8000
selector:
app: qwen3-32b
EOF
步骤三:验证推理服务
获取服务的公网IP 。
export EXTERNAL_IP=$(kubectl get svc qwen3-32b -o jsonpath='{.status.loadBalancer.ingress[0].ip}') echo ${EXTERNAL_IP}
预期输出:
60.xxx.xxx.225
发送一条推理请求。
curl -H "Content-Type: application/json" \ http://${EXTERNAL_IP}:8000/v1/chat/completions \ -d '{ "model": "/models/Qwen3-32B/", "messages": [ { "role": "user", "content": "Say this is a test!" } ], "max_tokens": 512, "temperature": 0.7, "top_p": 0.9, "seed": 10 }'
预期输出:
{"id":"chatcmpl-2e5fdb02c1b246dc96cxxxxxx29c0fd3","object":"chat.completion","created":1747042811,"model":"/models/Qwen3-32B/","choices":[{"index":0,"message":{"role":"assistant","reasoning_content":null,"content":"<think>\nOkay, the user wrote \"Say this is a test!\" So I need to respond to that. Let me think. They probably want me to acknowledge that it's a test. Maybe they're checking how I respond to test prompts. I should confirm that it's a test and ask if there's something specific they need help with. Keep it friendly and open-ended. Let me make sure I don't miss any underlying needs. Maybe they want to see if I follow instructions or handle certain types of queries. Alright, a simple response should work here.\n</think>\n\nYou're absolutely right—this is a test! Is there something specific you'd like me to test or help with? Let me know, and I'll do my best!","tool_calls":[]},"logprobs":null,"finish_reason":"stop","stop_reason":null}],"usage":{"prompt_tokens":14,"total_tokens":167,"completion_tokens":153,"prompt_tokens_details":null},"prompt_logprobs":null}