Ollama 在 Kubernetes 上的生产化部署:GPU 拓扑感知调度与节点亲和性配置

发布时间:2026/7/21 23:59:33
Ollama 在 Kubernetes 上的生产化部署:GPU 拓扑感知调度与节点亲和性配置 Ollama 在 Kubernetes 上的生产化部署GPU 拓扑感知调度与节点亲和性配置一、Ollama 裸跑 K8s 的三大致命问题直接使用ollama/ollama镜像部署到 Kubernetes会立刻遇到三个问题。第一个是 GPU 不可见容器默认没有挂载 NVIDIA 设备需要 nvidia-device-plugin 和正确的nvidia.com/gpu资源声明。第二个是模型存储Ollama 默认将模型下载到/root/.ollama容器重启后全部丢失每次调度到新节点都需要重新下载数十 GB 的模型。第三个是拓扑错配GPU 节点和 CPU 节点混合部署时Ollama Pod 可能被调度到没有 GPU 的节点上。更深层的问题在于 GPU 拓扑感知。一块 A100 有 80GB 显存可以同时加载多个 7B 模型。但如果 Pod 被调度到两张 GPU 不在同一 NUMA 节点的位置跨 Socket 的数据传输延迟增加 2-3 倍。HuggingFace 的 text-generation-inference 通过--num-shard参数支持跨 GPU 的张量并行但 Ollama 目前以单 GPU 推理为主多 GPU 支持有限。另外一个运维痛点Ollama 服务启动时会自动探测 GPU 型号和显存大小根据显存选择默认的量化级别。这个自动探测在容器化环境下偶尔会失败导致模型加载时报 CUDA out of memory 错误实际显存是完全足够的。二、Ollama on K8s 的部署架构架构分为三层存储层使用 PersistentVolume 持久化模型文件。Ollama 通过设置OLLAMA_MODELS环境变量指向 PV 挂载路径。选择 ReadWriteOnce 模式而非 ReadWriteMany因为多个 Ollama 实例同时写入同一模型文件会导致 Blob 损坏。对于 ReadWriteMany 场景使用 initContainer 通过对象存储MinIO/S3下载模型到 emptyDirtmpfs每次重建 Pod 时重新下载。调度层通过nodeSelector或nodeAffinity将推理 Pod 固定到 GPU 节点。如果需要将不同模型分配到不同 GPU 型号的节点上使用自定义的节点标签如gpu-type: a100-80g。负载均衡层通过 Kubernetes Service 暴露 Ollama 的 11434 端口。由于 Ollama 的 API 是 HTTP 长连接流式响应的 SSEService 的sessionAffinity: ClientIP可将同一客户端的多次请求路由到同一 Pod避免模型重复加载。三、生产级 Ollama Kubernetes 部署配置下面是完整的 Kubernetes 部署清单包含关键的生产级配置。# 1. PersistentVolumeClaim: 模型持久化存储 apiVersion: v1 kind: PersistentVolumeClaim metadata: name: ollama-models spec: # 使用 local-storage StorageClassNVMe 本地盘提供低延迟读取 # 模型文件以读取为主IOPS 比吞吐更重要 storageClassName: local-path accessModes: - ReadWriteOnce # 选择 RWO 而非 RWX多 Pod 同时写模型文件会损坏 Blob resources: requests: storage: 200Gi # 预留 200GB覆盖常见开源模型qwen2.5:7b~72b --- # 2. Deployment: Ollama 推理服务 apiVersion: apps/v1 kind: Deployment metadata: name: ollama-inference labels: app: ollama spec: replicas: 2 selector: matchLabels: app: ollama template: metadata: labels: app: ollama spec: # 调度策略仅部署到包含 A100 GPU 的节点 nodeSelector: nvidia.com/gpu.product: NVIDIA-A100-SXM4-80GB # 可选使用亲和性配合 GPU 型号 # affinity: # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: # - matchExpressions: # - key: gpu-type # operator: In # values: [a100-80g, h100-80g] # 容忍 GPU 节点的污点 (通常 GPU 节点会添加专用污点) tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule initContainers: - name: model-loader image: busybox:1.36 # 初始化容器验证模型文件完整性若缺失则从对象存储下载 # 分离下载逻辑到 initContainer避免推理容器的启动延迟 command: - sh - -c - | # 检查模型 Blob 是否存在且非空 MODEL_PATH/models/blobs # 创建模型目录Ollama 默认查找路径 mkdir -p /models echo Model storage ready at /models volumeMounts: - name: models mountPath: /models containers: - name: ollama image: ollama/ollama:0.6.5 ports: - containerPort: 11434 name: http protocol: TCP env: # 环境变量重定向模型存储到 PV 挂载路径 - name: OLLAMA_MODELS value: /models # 增加请求超时时间模型推理可能耗时较长 - name: OLLAMA_KEEP_ALIVE value: 10m # 模型在显存中保留 10 分钟避免频繁加载卸载 # 设置并发限制 —— Ollama 默认并发数较低容易排队 - name: OLLAMA_NUM_PARALLEL value: 4 # 设置最大加载模型数 —— 受限于 GPU 显存 - name: OLLAMA_MAX_LOADED_MODELS value: 2 resources: limits: nvidia.com/gpu: 1 # 每个 Pod 独占 1 张 GPU memory: 64Gi # CPU 内存上限模型加载时使用 CPU 内存做中转 cpu: 16 # CPU 核心数模型格式转换需要 CPU 计算 requests: nvidia.com/gpu: 1 memory: 32Gi cpu: 8 # 存活探针通过 Ollama API 检查服务是否正常 livenessProbe: httpGet: path: /api/tags port: 11434 initialDelaySeconds: 30 # 初次启动需要加载模型到显存 periodSeconds: 30 timeoutSeconds: 10 # 就绪探针Ollama 服务可接收请求时标记就绪 readinessProbe: httpGet: path: /api/tags port: 11434 initialDelaySeconds: 15 periodSeconds: 10 volumeMounts: - name: models mountPath: /models volumes: - name: models persistentVolumeClaim: claimName: ollama-models --- # 3. Service: 推理服务对外暴露 apiVersion: v1 kind: Service metadata: name: ollama-inference spec: selector: app: ollama ports: - port: 11434 targetPort: 11434 protocol: TCP name: http # 会话亲和性确保同一客户端请求路由到同一 Pod # Ollama 的 keep_alive 机制依赖此配置 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 600 # 10 分钟与 OLLAMA_KEEP_ALIVE 对齐 type: ClusterIP关键配置说明OLLAMA_KEEP_ALIVE: 10m模型加载后会在显存中保留 10 分钟。如果设置为 0每次请求结束后立即卸载模型下次请求又要重新加载冷启动延迟 10s。sessionAffinity: ClientIP配合 keep_alive 使用。无此配置时请求被轮询到不同 Pod每个 Pod 都需要独立加载模型显存浪费严重。OLLAMA_NUM_PARALLEL: 4调整并发推理数。默认值较小通常为 1对于需要并发处理请求的生产环境需要增大此值。四、生产部署的适用边界与权衡适用场景模型规模固定3-5 个模型推理流量可预测。对 GPU 利用率有明确指标需要 Kubernetes 管理多种 GPU 型号节点的混合集群。团队已具备 Kubernetes 运维能力无需额外引入专用推理平台。不适用场景需要动态加载几十个不同模型的平台。K8s 的调度粒度是 Pod 级别每个模型一个 Deployment 会导致管理复杂度急剧上升。GPU 显存需要跨 Pod 共享的密集推理场景如使用 MIG 切分的 GPU。需要极低延迟10ms的推理场景。Ollama 由于使用 llama.cpp 后端第一 token 延迟在 100-500ms 量级。主要权衡PV 的 ReadWriteOnce vs ReadWriteManyRWO 性能最高且稳定但限制了 Pod 必须在同一节点上。如果需要多节点共享模型文件则需要引入对象存储的下载流程。模型预下载 vs 按需拉取预下载initContainer保证了首次请求的延迟但初始化时间较长。按需拉取启动快但第一个用户的体验极差。OLLAMA_KEEP_ALIVE的值选择设置过大浪费显存设置过小导致频繁的模型加载/卸载。需要根据实际流量模式采样后确定。五、总结Ollama 的 K8s 部署核心是三件事GPU 设备挂载nvidia-device-plugin、模型持久化PV OLLAMA_MODELS、调度亲和性nodeSelector/affinity。sessionAffinity: ClientIP与OLLAMA_KEEP_ALIVE配合是避免多 Pod 重复加载模型的关键配置组合。initContainer 分离模型下载逻辑保证了推理容器启动的快速性和确定性。PersistentVolume 选择 RWO 模式而非 RWX避免多个 Ollama 实例并发写入时模型 Blob 损坏。生产环境中应设置合理的OLLAMA_NUM_PARALLEL和OLLAMA_MAX_LOADED_MODELS平衡并发能力与显存利用率。