背景
k8s上部署推理服务很简便,vLLM作为一个流行的推理服务,自己也打包了镜像,可以比较方便地部署到k8s上,但还是免不了踩坑。
文档
部署
部署依赖
k8s上为了能够使用和分配GPU,需要安装一个daemonset: NVIDIA/k8s-device-plugin,建议用官方提供的helm chart安装。
安装完成后可以查看node的信息,查看gpu的分配情况:
Capacity:
nvidia.com/gpu: 2
Allocatable:
nvidia.com/gpu: 2
其中插件提供3种GPU分配方式:按GPU数量、按时间切片(Time-Slicing)、CUDA多进程服务(Multi-Process Service)。具体的设置在ConfigMap中进行配置,例如Time-Slicing:
apiVersion: v1
kind: ConfigMap
metadata:
name: nvidia-device-plugin-configs
data:
# timeSlicing.renameByDefault: 将nvidia.com/gpu重命名为nvidia.com/gpu.shared,避免概念混淆
# timeSlicing.resources.replicas: 将一个gpu切成多少个切片。例如replicas=10,gpu有2个,则总的可分配的数量为20
default: |-
config:
map:
default: |-
version: v1
flags:
migStrategy: none
sharing:
timeSlicing:
renameByDefault: true
resources:
- name: nvidia.com/gpu
replicas: 10
此时再看node信息为:
Capacity:
nvidia.com/gpu: 2
nvidia.com/gpu.shared: 20
Allocatable:
nvidia.com/gpu: 2
nvidia.com/gpu.shared: 20
版本检查
需要尽量保证宿主机的nvidia版本、cuda版本与pod镜像中是统一的,否则可能会出现向前兼容问题,从而报错。
因为vLLM镜像使用的基础镜像(nvidia/cuda
)是固定的版本,所以要么考虑升级宿主机的nvidia driver,要么自己重新编译vLLM镜像。
如果实在不方便,可以采用替换动态库的方式来work around,下面会讲怎么做。但是最好还是保持版本统一,少走弯路。
实际部署
这边假设宿主机和pod的版本不统一,且采用Time-Slicing来做,使用2GPU,这样算是比较复杂的配置了。
apiVersion: v1
kind: Pod
metadata:
labels:
app.kubernetes.io/instance: vllm-openai
name: vllm-openai
namespace: openpie-model
spec:
# 需要配置nvidia runtime
# 一般装了 NVIDIA/k8s-device-plugin 插件就有了。可以检查一下: kubectl get runtimeclass
runtimeClassName: nvidia
containers:
- name: vllm-openai
# 这里选择了一个driver版本是550的镜像
# 宿主机的nvidia driver版本是535,比较低,会遇到向前兼容的问题
image: vllm/vllm-openai:v0.6.3.post1
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args:
# 这里使用软连接强行替换为宿主机的535版本
# 注意这里的 tensor-parallel-size 需要小于等于分配到的GPU个数。例如使用"nvidia.com/gpu":"1"时,tensor-parallel-size只能为1
- |
ln -sf /usr/lib/x86_64-linux-gnu/libcuda.so.535.183.01 /usr/lib/x86_64-linux-gnu/libcuda.so.1;
ln -sf /usr/lib/x86_64-linux-gnu/libcudadebugger.so.535.183.01 /usr/lib/x86_64-linux-gnu/libcudadebugger.so.1;
ln -sf /usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.535.183.01 /usr/lib/x86_64-linux-gnu/libnvidia-nvvm.so.4;
ln -sf /usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.535.183.01 /usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.1;
vllm serve /model \
--port 18000 \
--served-model-name qwen2-7b \
--tensor-parallel-size 2 \
--gpu-memory-utilization 0.5 \
--max-model-len 512
env:
# 因为使用的是NVIDIA GeForce RTX 4090
# 40系不支持NCCL_IB和NCCL_P2P 需要在环境变量中disable掉
# 其他系的卡可能不需要
- name: NCCL_IB_DISABLE
value: "1"
- name: NCCL_P2P_DISABLE
value: "1"
# - name: OMP_NUM_THREADS
# value: "1"
resources:
limits:
# 这边配置的是12
# 要求是 这里的配置 加上 其他已配置的内容 要小于总数 2*10
nvidia.com/gpu.shared: "12"
requests:
nvidia.com/gpu.shared: "12"
volumeMounts:
# 挂载本地模型路径
- name: local-model-path
mountPath: /model
# 挂载共享内存
# 使用多卡时 会使用 NCCL SHM
# pod默认的64MB共享内存是不够用的 如果不挂载会报错
- name: shm
mountPath: /dev/shm
volumes:
- name: local-model-path
hostPath:
path: /data/models/Qwen/Qwen2-7B-Instruct
type: Directory
- name: shm
emptyDir: {}
其他资料
-
Multi-Instance GPU(MIG)
可以将一个GPU分片成多个实例,需要GPU和k8s同时支持。single策略是将所有分片的大小都固定成一样的,mixed策略是允许分片显存大小不同,并且在pod申请资源时指定多个不同类型的分片 -
关于一个集群上多个GPU部分支持共享、部分支持独享的讨论