本篇聚焦 Kubernetes 集群下的 GPU 算力调度与大模型服务编排,掌握K8s 集群搭建、NVIDIA Device Plugin 原理与部署、三大 AI 框架的容器化编排
一、为什么用 K8s 管理 GPU 集群
1. 无 K8s 管理的核心痛点
| 痛点 | 具体表现 |
|---|---|
| GPU 资源浪费 | 人工分配粒度粗,显存 / 算力碎片化,昂贵 GPU 空载率高,成本损耗大 |
| 任务调度混乱 | 多用户多任务抢卡,人工分配冲突多、效率低,无法按需求弹性分配 |
| 服务稳定性差 | 进程崩溃无自动恢复,业务中断;流量波动无法快速扩容,高峰期服务被打挂 |
| 扩容运维困难 | 多机管理混乱,环境不一致,部署、迁移、升级成本高、周期长 |
2. K8s 解决的核心价值
| 能力 | 说明 |
|---|---|
| 自动 GPU 调度 | 集群统一管理 GPU 资源,按需自动分配,资源利用率最大化 |
| 自动弹性扩缩 | 基于流量自动增减 Pod 副本,低谷省资源、高峰扛压力 |
| 服务自愈 | Pod 异常崩溃自动重启重建,保证服务持续可用 |
| 多机统一管理 | 标准化部署、配置、运维,支持大规模集群高效扩展,环境一致性强 |
二、K8s 集群部署(⭐)
1. 集群规划示例
| 主机名 | IP | 角色 |
|---|---|---|
| k8s-master | 192.168.100.146 | 控制面(CPU) |
| k8s-node1 | 192.168.100.147 | 工作节点(GPU) |
2. 所有节点基础环境配置
2.1 配置 APT 阿里云源
# 备份原源
cp /etc/apt/sources.list /etc/apt/sources.list.backup
# 替换为阿里云镜像
sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list
apt update
2.2 设置主机名与主机解析
# Master执行
hostnamectl set-hostname k8s-master && bash
# Worker执行
hostnamectl set-hostname k8s-node1 && bash
# 所有节点添加主机解析
cat <<EOF | sudo tee -a /etc/hosts
192.168.32.14 k8s-master
192.168.32.13 k8s-node1
EOF
2.3 时钟同步
apt install -y chrony
systemctl enable chrony && systemctl start chrony
timedatectl set-timezone Asia/Shanghai
2.4 关闭 Swap(必须,否则 kubelet 无法启动)
# 临时关闭
swapoff -a
# 永久关闭
sed -i '/swap/s/^/#/' /etc/fstab
# 验证
free -h
2.5 关闭防火墙
ufw disable
systemctl stop ufw && systemctl disable ufw
2.6 开启 IPv4 转发与桥接内核参数
cat <<EOF | sudo 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
modprobe br_netfilter
3. 安装 containerd 运行时
# 下载安装
curl -O https://mirrors.aliyun.com/docker-ce/linux/ubuntu/dists/jammy/pool/stable/amd64/containerd.io_1.6.26-1_amd64.deb
dpkg -i containerd.io_1.6.26-1_amd64.deb
# 生成并修改配置
mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
# 启用SystemdCgroup(与kubelet一致)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/g' /etc/containerd/config.toml
# 替换pause镜像为国内源
sed -i 's#sandbox_image = "registry.k8s.io/pause:.*"#sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"#' /etc/containerd/config.toml
# 启动服务
systemctl enable containerd && systemctl restart containerd
4. 配置 crictl 客户端
# 安装
export VERSION="v1.29.0"
wget https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-amd64.tar.gz
tar -zxvf crictl-${VERSION}-linux-amd64.tar.gz -C /usr/local/bin
# 配置
tee /etc/crictl.yaml <<EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
timeout: 10
EOF
5. 安装 kubeadm/kubelet/kubectl
# 添加GPG密钥与阿里云K8s源
curl -fsSL https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
cat <<EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
# 安装指定版本并锁定
apt update
apt install -y kubelet=1.28.2-00 kubeadm=1.28.2-00 kubectl=1.28.2-00
apt-mark hold kubelet kubeadm kubectl
systemctl enable kubelet
6. Master 节点初始化
# 预拉取镜像
kubeadm config images pull --image-repository=registry.aliyuncs.com/google_containers --kubernetes-version="v1.28.2"
# 初始化集群
kubeadm init \
--kubernetes-version=v1.28.2 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.96.0.0/12 \
--image-repository=registry.aliyuncs.com/google_containers
初始化成功后执行配置:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
7. 部署 Flannel 网络插件
kubectl apply -f kube-flannel.yml
# 验证
kubectl get pod -n kube-flannel
8. Worker 节点加入集群
在 Worker 节点执行 Master 初始化输出的kubeadm join命令,格式如下:
kubeadm join 192.168.215.129:6443 \
--token 97pgmv.vxjakgynflgdh4zw \
--discovery-token-ca-cert-hash sha256:52163ce2f39c0ccc44558e7339c52bfd006c78944088efb2f94dcc57d424b816
9. 集群状态验证
kubectl get nodes -o wide
kubectl get pods -n kube-system
所有节点状态为Ready、系统 Pod 全部 Running 则集群正常。
三、K8s GPU 算力调度(⭐)
1. NVIDIA Device Plugin 概述
- 定位:Kubernetes 集群级 GPU 设备管理插件,以 DaemonSet 形式运行在每个 GPU 节点。
- 核心功能:向 kubelet 注册 GPU 资源、监控 GPU 健康状态、实现 GPU 资源的分配与清理。
- 工作原理:
- 插件通过 gRPC 接口与 kubelet 通信,上报节点 GPU 数量、健康状态
- K8s 调度器查询设备资源,将 Pod 调度到有可用 GPU 的节点
- Pod 创建时,kubelet 调用插件的 Allocate 接口,将 GPU 设备、驱动库动态注入容器
- 容器内应用即可透明使用 GPU,全程标准化调度、无需手动干预
2. 部署前置:NVIDIA Container Toolkit
GPU 节点必须先安装 Container Toolkit,让 containerd 支持 GPU 运行时。
# 1. 添加GPG密钥与软件源
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo 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://#' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
# 2. 安装指定版本
apt update
export NVIDIA_CONTAINER_TOOLKIT_VERSION=1.17.8-1
apt install -y nvidia-container-toolkit=${NVIDIA_CONTAINER_TOOLKIT_VERSION}
# 3. 配置containerd默认运行时为nvidia
nvidia-ctk runtime configure --runtime=containerd
sed -i 's/default_runtime_name *= *"runc"/default_runtime_name = "nvidia"/g' /etc/containerd/config.toml
systemctl restart containerd
3. 部署 NVIDIA Device Plugin
kubectl apply -f nvidia-device-plugin.yml
- 部署后会在每个 GPU 节点启动一个插件 Pod,向集群注册
nvidia.com/gpu资源类型。
4. 部署验证
# 查看插件日志,无报错则正常
kubectl logs -f nvidia-device-plugin-daemonset-xxx -n kube-system
# 查看节点可分配GPU资源
kubectl describe node k8s-node1 | grep nvidia.com/gpu
5. 易混概念对比
| 对比项 | NVIDIA Container Toolkit | K8s NVIDIA Device Plugin |
|---|---|---|
| 作用层级 | 单机容器运行时级别 | K8s 集群调度级别 |
| 核心功能 | 让单个容器能访问 GPU | 让 K8s 集群能管理、调度 GPU |
| 部署位置 | 宿主机操作系统 | K8s 集群 DaemonSet |
| 依赖关系 | 依赖 containerd/Docker | 依赖 Container Toolkit + kubelet |
| 适用场景 | 单机 Docker/containerd 跑 GPU 容器 | K8s 集群多节点 GPU 编排调度 |
四、AI 框架 K8s 部署(⭐)
1. Ollama 部署
核心配置要点
- 资源声明:
limits.nvidia.com/gpu: 1声明申请 1 张 GPU - 数据持久化:hostPath 挂载模型目录,避免 Pod 重建丢失模型
- 服务暴露:NodePort 类型暴露 11434 端口
Deployment 核心片段
spec:
containers:
- name: ollama
image: 192.168.100.149/gpuimages/ollama:latest
ports:
- containerPort: 11434
env:
- name: OLLAMA_HOST
value: "0.0.0.0"
resources:
limits:
nvidia.com/gpu: 1
volumeMounts:
- name: ollama-data
mountPath: /root/.ollama
volumes:
- name: ollama-data
hostPath:
path: /data/ollama
常用操作
# 进入Pod拉取模型
kubectl exec -it ollama-xxx -- bash
ollama pull qwen3.5:4b
# 外部调用API
curl http://节点IP:30114/api/generate -d '{
"model": "qwen3.5:4b",
"prompt": "写一个K8s排障流程",
"stream": false
}'
2. vLLM 部署
核心配置要点
- 启动命令:通过 command/args 指定模型、并行数、上下文长度等参数
- 环境变量:开启 ModelScope 加速国内模型下载
- GPU 资源:声明对应数量的 GPU,支持张量并行
- 性能优化:建议使用
hostNetwork减少网络转发,配置大共享内存
Deployment 核心片段
spec:
containers:
- name: vllm
image: 192.168.100.149/gpuimages/vllm:latest
command: ["python3", "-m", "vllm.entrypoints.openai.api_server"]
args:
- "--model"
- "Qwen/Qwen2-1.5B-Instruct"
- "--tensor-parallel-size"
- "1"
- "--max-model-len"
- "4096"
- "--port"
- "8000"
- "--host"
- "0.0.0.0"
env:
- name: VLLM_USE_MODELSCOPE
value: "true"
resources:
limits:
nvidia.com/gpu: 1
ports:
- containerPort: 8000
API 验证
curl http://节点IP:30080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2-1.5B-Instruct",
"messages": [{"role": "user", "content": "介绍一下Kubernetes"}],
"max_tokens": 512
}'
3. Open WebUI 部署
核心配置要点
- 端口映射:容器 8080 端口映射到节点 3000 端口
- 数据持久化:挂载用户数据、配置、对话记录
- 环境变量:关闭联网搜索,纯内网离线运行
Deployment 核心片段
spec:
replicas: 1
containers:
- name: open-webui
image: 192.168.100.149/gpuimages/open-webui:latest
ports:
- containerPort: 8080
env:
- name: ENABLE_RAG_WEB_SEARCH
value: "false"
volumeMounts:
- name: open-webui-data
mountPath: /app/backend/data
volumes:
- name: open-webui-data
hostPath:
path: /data/open-webui
后端对接
- 对接 Ollama:设置中填写
http://ollama-service:11434 - 对接 vLLM:外部连接中添加 OpenAI 接口
http://vllm-service:8000/v1
五、汇总
- K8s 中 NVIDIA Device Plugin 的工作原理是什么?
插件以 DaemonSet 运行在每个 GPU 节点,通过 gRPC 与 kubelet 通信,上报节点 GPU 数量与健康状态;K8s 调度器根据nvidia.com/gpu资源调度 Pod;Pod 创建时,kubelet 调用插件分配 GPU,将设备节点、驱动库动态注入容器,实现 GPU 的集群级统一调度。
- 为什么部署 K8s 必须关闭 Swap?
Kubernetes 设计要求节点内存不足时由调度器驱逐 Pod,而不是使用 Swap。Swap 会导致节点性能不稳定、Pod QoS 失效、调度判断失真,因此 kubelet 默认强制要求关闭 Swap 才能启动。
- containerd 为什么要配置 SystemdCgroup?
kubelet 默认使用 SystemdCgroup 驱动管理容器 cgroup,containerd 必须与其保持一致,否则会出现 cgroup 驱动不匹配错误,导致节点无法正常加入集群、Pod 运行异常。
- vLLM 部署为什么需要大共享内存(–shm-size)?
答:vLLM 的 PagedAttention、连续批处理机制依赖大量进程间通信和共享内存数据交换;共享内存不足会导致推理性能下降、高并发下报错,因此通常配置数 GB 到十几 GB 的共享内存。
- K8s 部署 GPU 大模型服务相比裸 Docker 有什么优势?
集群统一调度 GPU,资源利用率更高;服务自愈 + 自动扩缩容,稳定性更强;标准化部署,多机扩展、迁移更方便;支持服务发现、负载均衡、滚动更新,更适合生产级大规模服务。