我们公司上个月把LLM推理服务迁移到了K8s+vLLM,跑了一个月,整体稳定。这篇文章记录一下我们踩过的坑,给想上车的朋友一些参考。
为什么自建
直接用云服务商API不好吗?
说实话,对于大部分场景确实够用了。我们迁移主要是三个原因:
- 数据合规:金融业务,不能把数据发到第三方
- 成本:日均调用量上百万,自建比API便宜得多
- 定制:需要跑微调模型,公有API不支持
如果你的场景没有这些需求,直接用API就行,别折腾。
集群准备
我们用的是阿里云ACK,配置如下:
# 7B模型,单卡A10G(24GB显存)
Worker节点配置:
- 数量:2台
- GPU:NVIDIA A10G × 1
- CPU:8核
- 内存:32GB
- 系统盘:100GB SSD
生产环境跑70B的话,需要4卡A100。这个成本就比较感人了,一块A100 80GB差不多小十万。
GPU Operator必须装,不然K8s识别不了显卡:
# 安装GPU Operator
kubectl apply -f https://raw.githubusercontent.com/NVIDIA/gpu-operator/main/statefulset.yaml
# 验证安装
kubectl get pods -n gpu-operator
vLLM单节点部署
测试环境用Deployment跑就够了:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vllm-inference
namespace: llm-serving
spec:
replicas: 1
selector:
matchLabels:
app: vllm
template:
metadata:
labels:
app: vllm
spec:
containers:
- name: vllm
image: vllm/vllm-openai:v0.4.0
ports:
- containerPort: 8000
resources:
limits:
nvidia.com/gpu: "1"
memory: "24Gi"
cpu: "8"
env:
- name: MODEL_NAME
value: "deepseek-ai/DeepSeek-V4-Flash"
args:
- --model
- $(MODEL_NAME)
- --tensor-parallel-size
- "1"
- --gpu-memory-utilization
- "0.9"
- --max-model-len
- "8192"
服务暴露:
apiVersion: v1
kind: Service
metadata:
name: vllm-service
namespace: llm-serving
spec:
type: ClusterIP
ports:
- port: 8000
targetPort: 8000
selector:
app: vllm
压测验证
部署完了别急着上线,先压测:
# 测试并发
for i in {1..10}; do
curl http://vllm-service.llm-serving:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{"model": "deepseek-ai/DeepSeek-V4-Flash", "prompt": "写一个快速排序", "max_tokens": 200}' &
done
wait
我们测出来单卡A10G跑7B模型:
- 并发8请求,延迟稳定在200-300ms
- 并发16请求,开始出现排队
- 并发32请求,显存不够,开始OOM
多卡并行踩坑
70B的模型必须多卡,不然显存根本装不下。
# 4卡并行部署
spec:
containers:
- name: vllm
resources:
limits:
nvidia.com/gpu: "4" # 关键:申请4张卡
args:
- --model
- $(MODEL_NAME)
- --tensor-parallel-size
- "4" # 张量并行,一张卡放不下四分之一
- --pipeline-parallel-size
- "1"
- --gpu-memory-utilization
- "0.95"
踩的坑:
- 卡间通信带宽不够——我们之前用的机器网络是25G的,跑起来卡顿严重。换到100G网络才好
- NCCL配置问题——多机并行的时候NCCL经常超时,加了这些参数才解决:
# Node上设置
export NCCL_IB_TIMEOUT=30
export NCCL_IB_DISABLE=0
export NCCL_DEBUG=INFO # 调试用,生产关掉
自动扩缩容
用KEDA根据请求数扩缩:
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: agent-scaler
spec:
scaleTargetRef:
name: vllm-inference
pollingInterval: 10
cooldownPeriod: 60
minReplicaCount: 1
maxReplicaCount: 10
triggers:
- type: prometheus
metadata:
serverAddress: http://prometheus:9090
metricName: pending_requests
threshold: "20"
query: sum(rate(vllm_server_num_requests_waiting_total[1m]))
有个坑:vLLM的metrics有时候不准,我们遇到过一次扩缩容没触发的情况。后面加了自定义metrics解决了。
KServe标准化
生产环境建议用KServe管理InferenceService,配置如下:
apiVersion: serving.kserve.io/v1beta1
kind: InferenceService
metadata:
name: llm-service
namespace: llm-serving
spec:
predictor:
model:
modelFormat:
name: vLLM
runtime: vllm
storageUri: s3://my-bucket/llama-3-8b
resources:
limits:
nvidia.com/gpu: "1"
memory: "24Gi"
KServe的好处是统一了推理接口,以后换模型只需要改storageUri,不用动代码。
总结
跑了一个月,稳定是真的稳定,平均每天处理请求300万+,P99延迟控制在800ms以内。
成本方面:4卡A100一个月云服务费大概8万,我们日均请求量800万,换算下来比用API便宜了60%。
值不值得上,看你们体量。如果日均请求量没上百万,建议还是先用API,省心。
有问题评论区问,看到了回。