最近带着一帮人搞k8s AI推理服务集群,后续要部署各种规模的大模型对外提供服务,老板让运维和我(infra研发)去分别弄下。 运维那边的人实在是太古板,弄的都是什么centos7.9 + k8s 1.28,走的老device-plugin,甚至还是手动装驱动,dkmd玩意儿,想跑ebpf,ai推理 vllm,权重挂载啥的都用古董方法,思想太老旧了。
没办法啊,现在ai时代,技术更新那么快,我干脆去找其它组撸来了些新设备,我自己搭个集群行了吧。本文算是SOP吧,也算是对自己从 0 搭 AI集群的记录。
用到的 Volcano, Clium, HAMI-DRA, Nvidia-ctk,ContainerdV2,k8s 1.37.1 。
DRA是从k8s1.34.2+转正的一个新特性,简单说是用于显示更多资源细节,比如vram啊,features啊啥的
还有就是我用HAMI实现到了vGPU切分,这会一个容器可以不像以前device-plugin那样,只能一对一调度了,现在还可以按显存啊,gpu使用率啥的调度。这里说的 vGPU 是 HAMi 对物理 GPU 显存和算力份额的软切分,不是 NVIDIA 商业版 vGPU,也不是 MIG。它能解决一张卡给多个 Pod 使用的问题,但隔离能力依赖 HAMi-core,和硬件分区不是一回事。
环境 这次使用的版本,DRA在 Kubernetes 1.34.2 已经 GA,HAMi-DRA 需要用到 DRA Consumable Capacity。该特性在 1.34 是 alpha,需要显式打开;1.36 进入 beta 并默认开启,而我这用的 1.37 不需要再额外配置 feature gate了,已经开了。
组件
版本
操作系统
Ubuntu 26.04,cgroup v2
Kubernetes
v1.37
containerd
v2.x,已启用 CDI
CNI
Cilium CLI v0.20.1
NVIDIA 驱动
610,DRA 要求 >= 580
Node Feature Discovery
v0.19.0 (暂时还没用到,不过先装了)
NVIDIA DRA driver
v0.5.0
Volcano
chart 1.16.0-alpha.2
HAMi-DRA
chart 0.2.3
这几个组件分工如下:
NVIDIA DRA driver 负责把 GPU 和 ComputeDomain 发布成 ResourceSlice,对应 gpu.nvidia.com 和 compute-domain.nvidia.com。
Volcano 负责批调度和 Gang scheduling,并通过 feature gate 接入 DRA。
HAMi-DRA chart 里的 webhook 负责把传统 HAMi 资源请求转换成 ResourceClaim,同时部署 hami-core-gpu.project-hami.io DRA driver,用来做显存、算力份额切分。
NFD 用来发现节点特征,GPU 节点还需要打上 DRA 和 HAMi 使用的节点标签。
中国大陆网络 下面的命令都使用上游默认地址。实际在国内网络里部署时,最麻烦的通常不是配置文件,而是各个 registry 和 GitHub Release 能不能稳定访问。有条件的话,建议提前在机房或者公司内自建源,不要等安装到一半再补。(我这里文章把实际的镜像给hide了,实际上我是一边部署,一遍拉不下来,然后还得换个海外可以拉的拉了再推到公司镜像用)
需要自建或代理的内容:
APT 源:Kubernetes 的 pkgs.k8s.io、这里可以用阿里的,但是nvidia-dra驱动也在这上面,阿里的源没有,你看着办。
GitHub:get-helm-4 脚本、Cilium CLI Release、Volcano chart Release、NFD 的 kustomize 地址都在 GitHub。内网可以起个python http server,生产还是建议弄个nginx/apache。
Helm 仓库:cert-manager、HAMi-DRA 的 index.yaml 和 chart 包可以做内网镜像;更推荐直接同步到 Harbor 的 OCI 仓库,后续升级时检查 digest。
容器镜像:主要涉及 registry.k8s.io、docker.io、quay.io、ghcr.io。可以直接用 DaoCloud 之类的公共代理临时测试,但生产环境更建议同步到自建 Harbor,再用各 chart 的 registry、repository、imagePullSecrets 参数替换。
本文部署影响的镜像来源如下:
Kubernetes registry.k8s.io NVIDIA DRA driver registry.k8s.io/dra-driver-nvidia/dra-driver-nvidia-gpu Node Feature Discovery registry.k8s.io/nfd/node-feature-discovery Volcano docker.io/volcanosh/* HAMi DRA driver docker.io/projecthami/k8s-dra-driver HAMi webhook/monitor ghcr.io/project-hami/* cert-manager quay.io/jetstack/* Cilium quay.io/cilium/*
如果只是临时绕过网络问题,可以给 containerd 配 registry mirror;正式环境不要把临时公共代理写死到镜像 tag 里。
宿主机环境 下面这些操作在 master 和所有 worker 上都要执行。
安装工具和容器运行时 apt-get update apt-get install -y apt-transport-https ca-certificates curl gpg install -d -m 0755 /etc/apt/keyrings curl -fsSL curl -fsSL https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.37/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://mirrors.aliyun.com/kubernetes-new/core/stable/v1.37/deb/ /" | tee /etc/apt/sources.list.d/kubernetes.list apt-get update apt-get install -y kubelet kubeadm kubectl containerd socat conntrack axel git swapoff -a sed -i '/ swap / s/^/#/' /etc/fstab systemctl enable --now containerd cat <<EOF | tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF modprobe overlay modprobe br_netfilter cat <<EOF | tee /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF sysctl --system
先生成containerd默认配置(不然后面nvidia-ctk工作不了),再把 cgroup driver 改成 systemd:
mkdir -p /etc/containerdcontainerd config default > /etc/containerd/config.toml systemctl restart containerd
然后修改 pause 镜像。Kubernetes 1.37 的 kubeadm 默认使用 registry.k8s.io/pause:3.10.2,,保证 containerd 的 sandbox 镜像和它一致即可:
[plugins.'io.containerd.cri.v1.images'.pinned_images] sandbox = 'registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10.2'
国内建议直接用阿里的
安装 NVIDIA 驱动和 CDI 先用 ubuntu-drivers devices 检查可用驱动:
选择 580 或更新的,非 nouveau 驱动。本次测试用的是 610:
apt install -y nvidia-driver-610 reboot
重启后确认 nvidia-smi 正常:
驱动装好后,nouveau 会自动禁用,不需要再手动处理。
接着安装 NVIDIA Container Toolkit,并让 containerd 和 CDI 认识 GPU:
这个地方倒是不需要fq,可以直接拉,但会比较慢。
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | tee /etc/apt/sources.list.d/nvidia-container-toolkit.list apt-get update apt-get install -y nvidia-container-toolkit nvidia-ctk runtime configure --runtime=containerd mkdir -p /etc/cdinvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml systemctl restart containerd
nvidia-ctk cdi generate 会把当前节点上的 GPU 设备写进 CDI spec。DRA driver 实际注入设备时也会用到这套 CDI 配置,所以这一步不能省。
初始化 Kubernetes Master 在 master 上初始化集群:
kubeadm init --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers mkdir -p $HOME /.kubecp -i /etc/kubernetes/admin.conf $HOME /.kube/configchown $(id -u):$(id -g) $HOME /.kube/config
kubeadm init 输出的 join 命令要保存下来,在你的节点上加进去
安装 Cilium CLI。CLI 只是一个安装工具,Cilium 组件本身还是用官方镜像:
axel -n 32 https://github.com/cilium/cilium-cli/releases/download/v0.20.1/cilium-linux-amd64.tar.gz tar -xzvf cilium-linux-amd64.tar.gz install -m 0755 cilium /usr/local/bin/cilium rm -f cilium-linux-amd64.tar.gz
Worker 在每台 worker 上执行 kubeadm init 输出的 join 命令:
kubeadm join <MASTER_IP>:6443 \ --token <TOKEN> \ --discovery-token-ca-cert-hash sha256:<CA_CERT_HASH>
节点加入后回到 master,确认节点状态,看ready就行:
kubectl get nodes -o wide
安装基础组件 Cilium 在 master 上安装 CNI:
cilium install cilium status --wait kubectl get pods -n kube-system
后续如果新增节点,跑 cilium upgrade。
NFD 和节点标签 安装 NFD v0.19.0。这里直接用官方 overlay,镜像也是默认的 registry.k8s.io/nfd/node-feature-discovery:
kubectl apply -k 'https://github.com/kubernetes-sigs/node-feature-discovery/deployment/overlays/default?ref=v0.19.0' kubectl -n node-feature-discovery rollout status deploy/nfd-master kubectl -n node-feature-discovery rollout status deploy/nfd-gc kubectl -n node-feature-discovery rollout status ds/nfd-worker
给有 GPU 的节点打上标签。nvidia.com/gpu.present 给 NVIDIA DRA driver 使用,gpu=on 给 HAMi 的 driver DaemonSet 使用:
for node in worker2 worker3; do kubectl label node "$node " nvidia.com/gpu.present=true gpu=on --overwrite done
节点名按实际环境替换,不要照抄。master 如果确实有 GPU,也可以一起打标签(不过打了也不调度就是了)。
安装 Volcano 下载对应版本的 chart,然后安装到 volcano-system。镜像不覆盖,chart 默认就是 docker.io/volcanosh/*:
wget https://github.com/volcano-sh/helm-charts/releases/download/volcano-1.16.0-alpha.2/volcano-1.16.0-alpha.2.tgz helm upgrade --install volcano ./volcano-1.16.0-alpha.2.tgz \ --namespace volcano-system \ --create-namespace \ --set custom.scheduler_feature_gates="DynamicResourceAllocation=true"
只开 feature gate 还不够,Volcano 的 predicates 插件也得把 DRA 打开。在 predicates 插件下面加这段参数,:
- name: predicates arguments: predicate.DynamicResourceAllocationEnable: true
编辑 ConfigMap 补上:
kubectl -n volcano-system edit configmap volcano-scheduler-configmap kubectl -n volcano-system rollout restart deploy/volcano-scheduler
安装 HAMi-DRA (默认支持nvidia) HAMi-DRA 依赖 cert-manager 生成 webhook 证书。cert-manager 使用官方 chart 和默认镜像:
helm repo add jetstack https://charts.jetstack.io helm repo update helm upgrade --install cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set crds.enabled=true
添加 HAMi-DRA 的 Helm 仓库:
helm repo add hami-dra https://project-hami.github.io/HAMi-DRA helm repo update
这次实验为了和现场输出保持一致,HAMi-DRA 装在 default namespace,所以命令里没有再指定 namespace。驱动只使用 webhook、monitor 和 HAMi DRA driver,不接管 containerd 里的 NVIDIA runtime,因此设置 drivers.nvidia.containerDriver=false:
helm upgrade --install hami-dra hami-dra/hami-dra \ --set drivers.nvidia.containerDriver=false
正式环境更建议单独建一个 hami-dra-system namespace:
helm upgrade --install hami-dra hami-dra/hami-dra \ --namespace hami-dra-system \ --create-namespace \ --set drivers.nvidia.containerDriver=false
等待 webhook、monitor 和 driver 就绪:
kubectl get pods | grep hami kubectl rollout status deploy/hami-dra-webhook kubectl rollout status deploy/hami-dra-monitor kubectl rollout status ds/hami-dra-driver-kubelet-plugin
HAMi-DRA 的 webhook 默认会排除 release namespace。也就是说,如果 chart 装在 default,那 default 里的 Pod 不会被转换成 ResourceClaim;如果装在 hami-dra-system,业务就不要再放到 hami-dra-system 里。测试时新建一个独立 namespace 即可,例如:
kubectl create namespace hami-vgpu-test
业务 Pod 还是按原来的方式写:
resources: requests: nvidia.com/gpu: 1 nvidia.com/gpumem: 2048 nvidia.com/gpucores: 20 limits: nvidia.com/gpu: 1 nvidia.com/gpumem: 2048 nvidia.com/gpucores: 20
webhook 会把这些扩展资源从 Pod 里移除,并生成对应的 ResourceClaim。nvidia.com/gpucores 是百分比,不填时默认占满整卡的 cores,多 Pod 共享同一张卡时最好显式设置。
验证 先看 DeviceClass:
kubectl get deviceclasses
再等待一两分钟,查看 ResourceSlice。这里应该能同时看到 NVIDIA 官方 DRA 和 HAMi DRA 发布的切片:
root@master:~# kubectl get resourceslices NAME NODE DRIVER POOL AGE 00000-compute-domain.nvidia.com-worker2-lsmpf worker2 compute-domain.nvidia.com worker2 24h 00000-compute-domain.nvidia.com-worker3-pmtr8 worker3 compute-domain.nvidia.com worker3 24h 00000-gpu.nvidia.com-worker2-28f6w worker2 gpu.nvidia.com worker2 24h 00000-gpu.nvidia.com-worker3-hzbrb worker3 gpu.nvidia.com worker3 24h worker2-hami-core-gpu.project-hami.io-9csl7 worker2 hami-core-gpu.project-hami.io worker2 3m worker3-hami-core-gpu.project-hami.io-rflmv worker3 hami-core-gpu.project-hami.io worker3 3m
最后检查所有组件:
root@master:~# kubectl get pods -A NAMESPACE NAME READY STATUS RESTARTS AGE cert-manager cert-manager-54b7dc69c4-f2d8r 1/1 Running 0 49m cert-manager cert-manager-cainjector-8677bbdb7f-jswcx 1/1 Running 0 49m cert-manager cert-manager-webhook-55b94f85b4-9hwdw 1/1 Running 0 49m default hami-dra-driver-kubelet-plugin-fzjfl 1/1 Running 0 3m30s default hami-dra-driver-kubelet-plugin-g4fmk 1/1 Running 0 3m30s default hami-dra-monitor-7b684b79c5-sfshj 1/1 Running 0 16m default hami-dra-webhook-77fb68dbfd-n85hv 1/1 Running 0 29m kube-system cilium-22lt4 1/1 Running 0 28h kube-system cilium-dd8lk 1/1 Running 0 24h kube-system cilium-envoy-4sp8f 1/1 Running 0 24h kube-system cilium-envoy-c52tf 1/1 Running 0 28h kube-system cilium-envoy-lvdbf 1/1 Running 0 25h kube-system cilium-operator-cd976bbf6-b7jwz 1/1 Running 0 28h kube-system cilium-qqfzp 1/1 Running 0 25h kube-system coredns-558f5766d5-5mh89 1/1 Running 0 28h kube-system coredns-558f5766d5-pptxs 1/1 Running 0 28h kube-system etcd-master 1/1 Running 0 28h kube-system hami-admission-create-v6clz 0/1 ImagePullBackOff 0 57m kube-system kube-apiserver-master 1/1 Running 0 28h kube-system kube-controller-manager-master 1/1 Running 0 28h kube-system kube-proxy-cjwsk 1/1 Running 0 25h kube-system kube-proxy-l2t6l 1/1 Running 0 28h kube-system kube-proxy-lh6ng 1/1 Running 0 24h kube-system kube-scheduler-master 1/1 Running 0 28h node-feature-discovery nfd-gc-695bc59bdb-999vw 1/1 Running 0 24h node-feature-discovery nfd-master-545b988c65-jvm9m 1/1 Running 0 24h node-feature-discovery nfd-worker-8cr6h 1/1 Running 0 24h node-feature-discovery nfd-worker-hpbkh 1/1 Running 0 24h nvidia-dra-driver-gpu dra-driver-nvidia-gpu-controller-7cd78dbc87-t868c 1/1 Running 0 25h nvidia-dra-driver-gpu dra-driver-nvidia-gpu-kubelet-plugin-65jcz 2/2 Running 0 24h nvidia-dra-driver-gpu dra-driver-nvidia-gpu-kubelet-plugin-btm44 2/2 Running 0 24h volcano-system volcano-admission-67848b8c68-shmnf 1/1 Running 0 66m volcano-system volcano-controllers-79fd754fd-2gxp6 1/1 Running 0 66m volcano-system volcano-scheduler-6fff6f9747-hwzpp 1/1 Running 0 66m
上面的 hami-admission-create-v6clz 是之前试装另一版 HAMi 留下的 Job,和本文这套 HAMi-DRA chart 没关系。确认不需要后可以删掉:
kubectl -n kube-system delete job hami-admission-create
注意点 HAMi 是显存和算力份额的软切分,实际限制依赖 HAMi-core,不能把它当成 MIG 那样的硬件隔离。推理服务上生产前,最好用实际模型测试显存越界、算力争抢和并发下的延迟。
另外,使用 DRA 后,kubectl describe node 的 allocatable 里看不到 nvidia.com/gpu 是正常的。GPU 资源已经由 ResourceSlice 和 ResourceClaim 表达,不再走 device-plugin 的扩展资源计数。