[控场AI]
· 9 分钟阅读· 4,851 字

Ollama在K3s上龟速?GPU直通配置实战指南

Ollama在K3s上龟速?GPU直通配置实战指南

详解K3s上Ollama默认跑CPU的原因及四层GPU直通配置,附验证与排错。

文章针对在K3s上部署Ollama时GPU未被利用、生成速度极慢的问题,指出根本原因是K3s内嵌的containerd默认隔离宿主机PCI硬件,容器看不到NVIDIA设备节点,导致Ollama回退到CPU推理。解决方案是搭建四层硬件桥梁:宿主机安装NVIDIA驱动与Container Toolkit、通过模板扩展让K3s的containerd注册NVIDIA运行时、部署NVIDIA K8s Device Plugin使调度器感知GPU、在Ollama Pod中声明GPU资源并指定runtimeClassName。文章还给出验证直通成功的三个信号(进程、显存占用、生成速度),并针对Pod Pending、CUDA OOM、内核升级导致驱动失效三类故障提供处理方法,最后讨论了该方案适用的场景与投入产出比。

在K3s上部署Ollama跑本地大模型,本该是件既保护隐私又省钱的爽事。但很多人照着教程走完,发现生成速度只有每秒1个token,CPU风扇狂转、内存打满,体验直接崩盘。问题不在模型,也不在K3s本身,而在于容器默认压根看不到你的NVIDIA显卡。这篇指南拆解原因,并给出完整的GPU直通配置路径。

Ollama on K3s

为什么Ollama在K3s里默认跑在CPU上

在裸机macOS或装了CUDA驱动的Windows上,Ollama能自动探测GPU并把神经网络层卸载到显存。但Kubernetes是一个被刻意隔离的容器化环境,K3s内置的containerd运行时默认不会把宿主机的PCI硬件暴露给容器。

结果就是连锁反应:容器里的Ollama进程看不到/dev/nvidia*设备节点,无法加载CUDA运行时,于是安静地退回CPU做矩阵乘法。7B或13B模型在CPU上推理,系统内存和CPU缓存之间要持续做高延迟的数据搬运,最终表现为线程争抢、热降频,生成速度远低于GPU显存方案。

修复思路是搭一座从Linux内核驱动到K3s Pod命名空间的硬件桥梁,整个链路分为四层:Linux内核与NVIDIA驱动 → NVIDIA Container Toolkit与运行时 → NVIDIA K8s Device Plugin → 带GPU资源声明的Ollama Pod。

containerd 与 OCI 运行时:K3s 默认使用内嵌的 containerd 作为容器运行时,而 containerd 通过 OCI(Open Container Initiative)运行时规范来启动容器。默认的 runc 运行时只负责创建命名空间、挂载文件系统等基础隔离,并不会主动将宿主机的设备节点(如 /dev/nvidia*)挂载进容器。要让容器访问 GPU,必须换成能注入设备的运行时(如 nvidia-container-runtime),或在 containerd 配置中启用 NVIDIA 运行时插件。这就是为什么即使宿主机驱动正常,容器内的 Ollama 依然看不到显卡——OCI 运行时根本没把设备暴露进去。

第一步:宿主机装好驱动与Container Toolkit

动手改K3s之前,先确认宿主机本身是健康的。运行nvidia-smi,应该能看到GPU型号、驱动版本和CUDA版本信息。如果这一步就失败,后面的配置都没有意义。

接着安装NVIDIA Container Toolkit,它的作用是让containerd能把宿主机GPU设备注入OCI容器规格。在Ubuntu/Debian上通过官方仓库安装即可:

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 [^ ]* #&[signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] #' | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

第二步:让K3s的containerd认识NVIDIA运行时

K3s使用自己内嵌的containerd,配置文件在/var/lib/rancher/k3s/agent/etc/containerd/config.toml。这里有个关键坑:直接编辑这个文件没用,K3s每次重启都会覆盖它。正确做法是使用模板扩展机制。

先把现有配置复制成.tmpl模板文件,然后在模板里注册nvidia-container-runtime:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
  runtime_type = "io.containerd.runc.v2"
  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
    BinaryName = "/usr/bin/nvidia-container-runtime"

改完执行sudo systemctl restart k3s生效。这一步做完,containerd才具备启动CUDA容器的能力。

第三步:部署Device Plugin,让调度器看见GPU

containerd能跑CUDA容器还不够,Kubernetes调度器需要知道节点上有多少块GPU可供分配。这由NVIDIA Kubernetes Device Plugin负责,用Helm装进kube-system命名空间,并指定runtimeClassName=nvidia。

部署完成后用kubectl get nodes -o jsonpath='{.items[*].status.allocatable}'检查,如果返回结果里出现"nvidia.com/gpu": "1",说明集群已经具备GPU感知能力,调度器可以把GPU当作可分配资源来用。

Kubernetes Device Plugin 机制:Device Plugin 是 Kubernetes 用于扩展可调度资源(如 GPU、FPGA、网卡)的框架。它通过 gRPC 向 kubelet 注册资源名称(如 nvidia.com/gpu)和数量,kubelet 再将这些资源上报给 API Server,最终体现在节点的 allocatable 字段中。调度器依赖这些信息来决定 Pod 能否分配到 GPU。NVIDIA Device Plugin 以 DaemonSet 形式运行在每个节点上,负责发现 GPU 并管理容器的设备分配。没有它,集群就不知道节点有 GPU,Pod 声明 nvidia.com/gpu 时会一直 Pending。

第四步:给Ollama声明GPU资源

这是见证效果的一步。部署Ollama时必须在Pod spec里显式声明GPU请求和限制,同时在runtimeClassName里指定nvidia,否则前面所有配置都白费。核心片段如下:

spec:
  runtimeClassName: nvidia
  containers:
  - name: ollama
    image: ollama/ollama:latest
    resources:
      requests:
        nvidia.com/gpu: "1"
      limits:
        nvidia.com/gpu: "1"

注意requests和limits都要写nvidia.com/gpu。只写requests不写limits,在某些调度场景下会出现资源分配异常。

runtimeClassName 的作用:在 Pod spec 中设置 runtimeClassName: nvidia 会告诉 kubelet 使用名为 nvidia 的 RuntimeClass 来运行该 Pod。RuntimeClass 是 Kubernetes 的一种资源,它将 Pod 与特定的容器运行时配置关联起来。这里引用的 nvidia 运行时正是在第二步中通过 containerd 模板注册的 nvidia-container-runtime。如果没有指定 runtimeClassName,即使声明了 GPU 资源,容器仍会使用默认的 runc 运行时启动,导致设备无法注入。因此,资源声明和 runtimeClassName 必须同时配置,缺一不可。

验证:确认GPU真的在干活

部署后在Pod里跑ollama run llama3:8b,同时宿主机另开终端执行watch -n 1 nvidia-smi。三个信号可以确认直通成功:进程列表出现ollama_llama_server,显存占用从0跳到约4.5–5.5 GiB(量化8B模型),生成速度达到每秒60–90个token。拿到这个数字,说明整条链路打通了。

三个常见故障与处理

Pod一直Pending,提示Insufficient nvidia.com/gpu。 说明Device Plugin没注册成功。先在宿主机确认nvidia-smi正常,再重启daemonset:kubectl rollout restart daemonset nvidia-device-plugin-daemonset -n kube-system。

加载模型时报CUDA out of memory。 模型参数量超过显存容量。换更小的量化版本,比如用llama3:8b-instruct-q4_K_M替代FP16,或者通过OLLAMA_NUM_PARALLEL=1限制并行请求数。

重启后GPU直通突然失效。 多半是Linux内核自动升级导致NVIDIA DKMS模块不匹配。禁用内核与NVIDIA包的自动更新,或直接锁定驱动版本,比如sudo apt-mark hold nvidia-driver-550。

DKMS 与内核模块:NVIDIA 驱动通常通过 DKMS(Dynamic Kernel Module Support)机制在每次内核更新时自动重新编译内核模块。如果内核升级后 DKMS 编译失败或未触发,nvidia-smi 可能报错“NVIDIA-SMI has failed because it couldn't communicate with the NVIDIA driver”,导致 GPU 直通失效。锁定驱动版本(如 apt-mark hold)可以避免自动升级带来的不匹配,但长期来看仍需在升级内核后手动验证 DKMS 状态(dkms status)并重建模块。

这套配置值不值得做

把Ollama跑在K3s上的价值在于:轻量编排加上本地GPU算力,构成一套可控的私有AI基础设施。GPU直通的配置成本集中在前期的四层桥接上,一旦打通,后续部署向量数据库做RAG、接K8sGPT做集群诊断、用Longhorn做持久化存储,都建立在同一套GPU感知的集群之上。延迟从30秒级降到毫秒级,这个投入产出比对于有数据合规要求的团队来说相当划算。

如果只做单机推理、不打算扩展成多服务架构,直接在宿主机跑Ollama反而更省事。K3s方案适合那些已经把服务容器化、需要统一调度和资源隔离的场景。搞清楚地盘再动手,能省下不少折腾时间。

#Ollama K3s GPU直通#K3s部署Ollama#NVIDIA Container Toolkit#Kubernetes GPU调度#Ollama CPU回退#本地大模型推理加速#NVIDIA Device Plugin
分享:

相关推荐