恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Kubernetes上生成式AI生产级落地:GPU调度、vLLM推理与自动伸缩实战

  • 首页
  • 资讯中心
  • /
  • Kubernetes上生成式AI生产级落地:GPU调度、vLLM推理与自动伸缩实战

相关资讯

天勤量化线程退出问题解决方案与最佳实践 2026/10/15 12:11:04
天勤量化线程退出问题解决方案与实践 2026/10/15 12:11:04
Servlet技术构建织金砂锅非遗电商平台实践 2026/10/15 12:11:04

最新资讯

如何把‘无懈可击‘变成可执行的标准?一套打磨流程实操指南
麒麟王短线至尊完美破解版源码拆解:量价异动与均线乖离策略复现
打开软件时弹出错误:Component ‘mswinsck.ocx‘ not correctly registered:file is missing or invalid。
身份验证安全机制与防护最佳实践
free5gc 5G核心网安装与测试实战:从环境搭建到端到端验证
考公录音整理到崩溃?一套「录—转—梳—用」实操流程,把3小时辅导班课程变成能背的考点清单

今日推荐

AI视频生成的时间一致性难题与时空建模演进
基于bbox的PDF文档解析:多栏重排与水印过滤实战
探矿数据进RAG前的清洗全攻略:TXT、Word、PDF、网页四类文档处理实践

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Kubernetes上生成式AI生产级落地:GPU调度、vLLM推理与自动伸缩实战

发布时间:2026/10/15 12:16:05
Kubernetes上生成式AI生产级落地:GPU调度、vLLM推理与自动伸缩实战 做生成式 AI 的同学这两年应该都有一个共同感受模型越来越重推理链路越来越复杂单机玩不转了。前四篇我们聊了容器化基础、GPU 驱动、模型镜像、以及简单的推理服务部署这一篇我打算把视角拉高聊聊真正把生成式 AI 工作负载稳定跑在 Kubernetes 上的完整实战经验。这篇文章的定位是“进阶落地篇”。你如果已经有一个 Kubernetes 集群或者正准备搭建想在上面跑 LLM 推理、微调、甚至多模型服务那这篇文章就是给你写的。我会从集群初始化开始讲到推理服务部署、自动伸缩、调度策略最后是排障实录。整个过程都是我这几年在真实环境里一点一点踩出来的不是官方文档的复读。还是老规矩先交代一下我用的环境Kubernetes v1.26.0容器运行时是 containerdGPU 是 NVIDIA A100 和 L40S 混布推理框架以 vLLM 和 TGI 为主。这套组合在 2024 到 2025 年这个时间点基本就是生产环境的标配了。1. 整体设计与方案选型思路1.1 为什么非要用 Kubernetes 跑生成式 AI很多人一开始会觉得跑大模型不就是一个 GPU 服务器 一个推理进程吗直接 systemd 拉起来不就行了我承认单机单卡跑一个小模型确实不用 Kubernetes。但只要你进入生产环境事情就变味了。第一生成式 AI 服务天然有流量波动。白天业务高峰可能几百个并发请求晚上可能就寥寥无几。如果用裸机你就得按峰值预留 GPU平时大部分时间都在浪费。Kubernetes 配合自动伸缩可以按需申请 GPU 资源省下的都是真金白银。第二模型版本迭代太快。LLM 几乎每周都有新 checkpoint你不可能每次更新都手动登录服务器改配置。Kubernetes 的滚动更新机制配合镜像仓库可以把“模型发布”变成一个标准的 DevOps 流程。第三多模型、多租户的场景资源隔离和调度是硬需求。一个集群里可能同时跑着 ChatGLM、Llama、Stable Diffusion、Embedding 模型它们的优先级、资源配额、显存需求都不一样。裸机环境下你想做这种资源池化基本靠人肉运维Kubernetes 天生就是干这个的。所以我一直跟团队说一句话Kubernetes 不是银弹但它是当前跑生成式 AI 工作负载最成熟的“操作系统”。你可以不用它的某些能力但没必要从零造轮子。1.2 系列前四篇的铺垫与这一篇的定位这里简单回顾一下前四篇做了什么方便新读者接上。前四篇我们依次解决了这几个问题第一篇是容器化基础把 PyTorch 和 CUDA 环境装进镜像第二篇是 GPU 驱动和容器运行时打通让容器能真正用到宿主机显卡第三篇是模型镜像的构建与仓库管理解决“模型和代码一起发布”的问题第四篇是单推理服务的 Deployment 部署以及简单的 Service 暴露。这一篇我们要做的是“从单服务到生产级平台”的跨越。需要把之前零散的能力串起来形成一个能应对真实业务压力的整体架构。具体来说这一篇会重点解决四件事集群初始化时 GPU 节点怎么加进去、推理服务怎么优雅地跑起来并对外提供稳定能力、流量变化时怎么自动伸缩而不抖动、以及出问题时怎么快速定位。这四件事恰好对应我后面的四个章节。1.3 整体架构控制平面与 GPU 节点池分离生产环境的架构我强烈建议控制平面和 GPU 工作节点分离不要搞 all-in-one。控制平面节点跑 kube-apiserver、etcd、controller-manager、scheduler、coreDNS 这些系统组件它们对 CPU 和内存的稳定性要求很高。GPU 节点则专注跑推理、微调任务需要安装额外的驱动和运行时。我当前的集群架构大概是这样3 台控制平面节点8C16G 虚拟机跑 kubeadm 高可用etcd 独立在三块 SSD 上。4 台 GPU 节点每台 2 张 A100 或 4 张 L40S512G 内存系统盘和模型盘分离。3 台 CPU 节点跑日志收集、Ingress Controller、监控组件、对象存储网关等辅助服务。共享存储JuiceFS 对象存储用来放模型文件和训练数据。这个架构的核心思路是“各司其职”。GPU 节点不承担控制面压力控制面节点不装 GPU 驱动辅助服务不占用 GPU 节点资源。后续扩展时只需要往 GPU 节点池里加机器即可。1.4 方案选型推理框架与运行时推理框架的选择上目前生产环境主流是两个vLLM 和 Hugging Face TGI。我两个都用过说说实际感受。vLLM 胜在吞吐量高、社区活跃、兼容 OpenAI API 协议PagedAttention 对显存利用率的提升非常明显。如果你主要是做 LLM 的 OpenAI 兼容接口vLLM 是首选。TGI 的优点是部署简单、和 Hugging Face 生态集成度极高但性能和 vLLM 相比在长上下文场景下略有差距。我的选择是核心对话模型用 vLLM小规模的 Embedding 和重排模型用 TorchServe 或者直接 Python 服务。Stable Diffusion 类的文生图服务用专属镜像不用 vLLM——因为 vLLM 目前还是以文本模型为主。另外微调场景我用的是 SGLang 和 DeepSpeed调度模板我会在后面的章节里展开说。2. 集群初始化与 GPU 节点接入实录2.1 版本选型为什么锁在 v1.26.0 不升先把最容易忽视的版本问题说透。很多新手上来就装最新版 Kubernetes结果发现和 GPU 驱动、CSI 驱动、CRI 兼容性对不上折腾一星期。我当前集群用的是 v1.26.0这是经过大量生产验证的稳定版本。kubeadm init 时的输出里能看到[init] using kubernetes version: v1.26.0这个版本对 NVIDIA GPU 插件和 containerd 的兼容性都很好。选版本有一个非常朴素的原则不要追新看主流云厂商托管集群的默认版本。比如当时主要云厂商托管版大量停留在 1.24 到 1.28 之间说明这个区间的组件生态是最成熟的。v1.26.0 刚好在这个区间里而且 CRI 已经是 containerd 的天下了Docker 时代的兼容包袱已经卸掉了。2.2 kubeadm init 的 preflight 检查与常见坑第一次跑kubeadm init的人大概率会被 preflight 检查卡住。这一步是 kubeadm 在正式初始化前做的系统预检检查项非常细。我遇到过的问题可以列个表检查项失败原因解决方案swap 检查系统开了 swapswapoff -a并注释 /etc/fstab 里的 swap 行CRI 检查containerd 未配置或 socket 路径不对确认 containerd 已启动crictl info能正常输出端口占用6443、2379、2380 等端口被占ss -lntp查占用进程释放端口内核模块检查br_netfilter、overlay 未加载modprobe overlay br_netfilter写进 /etc/modules-load.d/系统参数检查net.bridge.bridge-nf-call-iptables 0写入 /etc/sysctl.d/k8s.conf 并sysctl --systempreflight 失败不可怕可怕的是你忽略它强行继续。--ignore-preflight-errorsall这个参数我强烈不建议在生产环境使用——它只是把问题藏起来了后面大概率会在更糟糕的时机爆出来。2.3 控制平面初始化与工作节点加入控制平面初始化我是用配置文件方式执行的方便后续高可用扩展。核心配置节选如下apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration kubernetesVersion: v1.26.0 controlPlaneEndpoint: 10.10.0.10:6443 networking: podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/16执行kubeadm init --configkubeadm-config.yaml然后按输出执行 kubeconfig 配置命令再安装 Calico 网络插件。这里注意podSubnet 要和 Calico 的 IP 池保持一致否则 Pod 网络会出问题。工作节点加入集群的命令是kubeadm jointoken 有效期默认 24 小时。我习惯把 token 信息保存到本地文件避免过期后还要重新生成kubeadm token create --print-join-command join-command.txtGPU 节点加入集群前我一定会先做一件事给节点打标签gpu-nodetrue并且加上 GPU 资源相关的污点nvidia.com/gpu:NoSchedule。这样后续只有声明了 GPU 需求的 Pod 才能调度到这些节点上普通业务 Pod 不会跑上来抢资源。2.4 NVIDIA 容器运行时让 Pod 用上 GPUKubernetes 本身不认识 GPU它只认扩展资源。要让 Pod 能使用 GPU需要安装 NVIDIA Device Plugin同时把容器运行时切换成 nvidia-container-runtime。具体步骤我简化成三条安装 NVIDIA 驱动宿主机层面执行nvidia-smi能正常输出。安装 nvidia-container-toolkit配置 containerd 使用 nvidia 运行时。部署 NVIDIA Device Plugin让每个 GPU 变成 Kubernetes 的扩展资源nvidia.com/gpu。这里最容易踩的坑是 containerd 的配置文件修改后没有重启生效。你改了 config.toml必须systemctl restart containerd然后再把 kubelet 重启让 kubelet 重新上报资源。还有一个隐蔽的坑GPU 节点上有多个驱动版本或者驱动与 CUDA 版本不匹配会导致 Device Plugin 报错。排查时先不要看 Kubernetes先在宿主机上确认nvidia-smi是否正常再crictl run一个含 CUDA 的测试容器验证。我手头常备一个nvidia/cuda:12.1.0-base镜像专门用来做这个验证。2.5 存储规划模型文件放哪里生成式 AI 工作负载对存储的要求和传统业务不一样。最大的特点就是模型文件巨大动辄几十 GB 甚至上百 GB而且加载进显存需要高吞吐的读性能。我的存储分层策略是这样的模型文件主存储JuiceFS 挂载到 GPU 节点底层是对象存储本地可用内存/磁盘做缓存。好处是多个节点共享同一份模型不会每台机器都存一份副本。临时训练数据本地 NVMe 临时目录用emptyDir 挂载进 Pod。训练 checkpoint 定期同步到共享存储。日志与指标直接打到宿主机磁盘通过 Filebeat 采集不进共享存储。共享存储的挂载我用的是 CSI Driver。JuiceFS 的 CSI 插件配置起来不算复杂但要注意挂载参数里cache-dir的路径要落在 SSD 上否则下载模型的速度会受限于磁盘 IO。3. 生成式 AI 工作负载的部署实践3.1 推理服务的镜像构建模型推理镜像和普通业务镜像最大的区别是镜像体积巨大基础镜像动辄几个 GB加上依赖和模型文件可能超过 10GB。所以镜像构建的优化空间非常大。先说我的分三层构建策略第一层基础运行环境层。Python 3.10 CUDA 12.1 PyTorch 2.1这一层基本不变单独打一个镜像 tag比如llm-base:cu121-py310。所有推理服务都基于它构建。第二层推理框架层。把 vLLM 或者 TGI 装好锁版本。这一步依赖较多单独一层可以充分利用镜像缓存避免每次改代码都重新装框架。第三层业务代码层。只 COPY 推理脚本、配置文件、prompt 模板。这一层变化最频繁但基础层缓存命中后构建时间能控制在半分钟以内。模型文件我不建议打进镜像而是通过共享存储挂载。原因有两个一是镜像仓库传输 20GB 的镜像是灾难二是模型更新频率和代码不一致强行绑在一起会导致镜像版本爆炸。3.2 Deployment 还是 StatefulSet第一个问题跑推理服务用 Deployment 还是 StatefulSet我见过不少人直接用 Deployment结果发现一个问题Pod 重建后 IP 变了而且没有稳定的标识。对于无状态的 HTTP 推理服务Deployment 确实够用配合 Service 负载均衡即可。但如果你有 WebSocket 长连接、或者需要稳定的 RPC 通信比如 gRPC我建议用 StatefulSet。它给每个 Pod 一个稳定的序号和网络标识比如model-llm-0.model-llm-headless.namespace.svc客户端可以做精确的连接管理。还有一类场景微调任务。这种短生命周期、需要跑完就退出的工作负载应该用 Job 而不是 Deployment。比如用 DeepSpeed 跑 LoRA 微调提交一个 Job跑完看日志然后删掉即可。我的实际选择是在线推理用 Deployment离线批量推理用 Job需要稳定连接的用 StatefulSet。3.3 关键配置vLLM 推理服务的 Deployment 示例下面这个配置是我生产环境里一个 Llama-3-8B 服务的实际 Deployment做了必要脱敏。可以直接参考改改参数就能用。apiVersion: apps/v1 kind: Deployment metadata: name: llama3-8b-server namespace: ai labels: app: llama3-8b spec: replicas: 1 selector: matchLabels: app: llama3-8b template: metadata: labels: app: llama3-8b spec: nodeSelector: nvidia.com/gpu.present: true tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: vllm image: registry.internal/ai/vllm-llama3-8b:20240512 command: [python3, -m, vllm.entrypoints.openai.api_server] args: - --model/models/llama3-8b - --served-model-namellama3-8b - --tensor-parallel-size1 - --max-model-len8192 - --gpu-memory-utilization0.85 - --port8000 ports: - containerPort: 8000 name: http resources: requests: cpu: 8 memory: 32Gi nvidia.com/gpu: 1 limits: cpu: 8 memory: 32Gi nvidia.com/gpu: 1 volumeMounts: - name: model-storage mountPath: /models readinessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 120 periodSeconds: 10 lifecycle: preStop: exec: command: [/bin/sh, -c, sleep 30] volumes: - name: model-storage csi: driver: juicefs-csi volumeAttributes: path: /models/llama3-8b这个配置里有两个细节值得解释。第一个是resources里的nvidia.com/gpu: 1。这个写法是请求一张 GPU不是占满一张卡。如果你用的是 NVIDIA MIG多实例 GPU也可以写成小数或者整数标识但这里我们先不展开。第二个是readinessProbe的initialDelaySeconds: 120。为什么是 120 秒而不是默认的几秒钟因为大模型加载权重到显存通常需要 1 到 3 分钟8B 模型可能快一点70B 模型甚至更久。如果探针设得太激进Pod 一直在“未就绪”和“重启”之间反复横跳反而更糟。3.4 服务暴露Ingress 与 Gateway推理服务跑起来之后怎么暴露给外部最直接的方式是 Service 的 LoadBalancer 类型但私有化部署的环境里我更喜欢用 Ingress。我在集群里部署了 NGINX Ingress Controller配置了一个支持大体积请求的 IngressapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: llm-ingress namespace: ai annotations: nginx.ingress.kubernetes.io/proxy-body-size: 50m nginx.ingress.kubernetes.io/proxy-read-timeout: 300 nginx.ingress.kubernetes.io/proxy-send-timeout: 300 spec: ingressClassName: nginx tls: - hosts: - llm.example.com secretName: llm-tls rules: - host: llm.example.com http: paths: - path: / pathType: Prefix backend: service: name: llama3-8b-service port: number: 8000这里最关键的注解是proxy-read-timeout: 300。因为 LLM 生成式推理的响应时间通常要几十秒甚至几分钟Nginx 默认的超时时间只有 60 秒如果不改长文本生成到一半就会被网关切断。这也是容易被忽略的坑。3.5 GPU 资源的调度细节Kubernetes 调度 GPU 资源时默认行为是“不可超卖”。也就是说你请求了 1 张 GPU调度器就认为你独占一张卡。但现实里一个小模型可能只用半张卡大模型又要多卡并行调度策略需要细分。我的做法是混合调度 自定义优先级。先按显存需求把 GPU 节点池分成两类大显存池A100 80G调度给 70B 级别的大模型。中显存池L40S 48G调度给 7B 到 13B 模型。然后通过 nodeSelector 和 nodeAffinity 控制调度目标。比如大模型的 Deployment 会指定nvidia.com/gpu.memory: 80G这样的节点标签。还有一个重要概念是tolerations。我给 GPU 节点默认打了污点只允许需要 GPU 的 Pod 调度上来。这样普通业务不会试图占用 GPU 节点调度器也省心很多。3.6 模型存储与加载优化模型文件放在 JuiceFS 上有一个现实问题节点冷启动时模型文件不在本地缓存首次加载需要从对象存储拉取速度取决于网络。一个 8B 模型大约是 16GB千兆网络下要两三分钟这还算能接受70B 模型 130GB那就要十几分钟了。我做了两个优化。第一个优化是节点预热或者说“模型预取”。在部署推理服务前先在 GPU 节点上跑一个 DaemonSet把常用的模型文件从 JuiceFS 预读取到本地缓存。这个过程不会占用 GPU 显存只占磁盘。实测下来预热过的节点拉起一个 8B 模型从 Pod 创建到就绪只需要 30 秒左右。第二个优化是 hook 脚本自动预热。我在 CSI 插件的挂载参数里设置了缓存预热目录配合 GPU 节点上的 cron job每半小时拉取一次模型文件变更列表。模型发布后节点缓存最多延迟半小时就能拿到新版本。4. 自动伸缩与弹性保障4.1 为什么不能用传统的 HPA生成式 AI 服务的自动伸缩比普通 Web 服务难很多。难在两点一是模型服务的资源消耗和请求并发强相关但指标采集周期长二是 GPU 资源伸缩不像 CPU 那样灵活新 Pod 启动要几秒钟到几分钟。传统 HPA 基于 CPU 和内存指标对 LLM 推理服务来说基本不可用。因为模型推理时 CPU 利用率通常不高GPU 利用率才是瓶颈。而默认的 metrics-server 根本不采集 GPU 指标。我的做法是基于 GPU 利用率和队列深度的组合指标使用 KEDA 来驱动伸缩。4.2 KEDA 集成按 GPU 利用率和请求队列伸缩KEDA 可以监听外部指标源再驱动 HPA 进行伸缩。我用了 Prometheus 作为指标源采集每个 Pod 的 GPU 利用率、请求排队长度、每秒请求数等指标。核心配置是一个 KEDA 的 ScaledObject 示例apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: llama3-8b-scaledobject namespace: ai spec: scaleTargetRef: name: llama3-8b-server minReplicaCount: 1 maxReplicaCount: 4 triggers: - type: prometheus metadata: serverAddress: http://prometheus.monitoring.svc:9090 query: | sum(rate(vllm:requests_total{servicellama3-8b}[1m])) threshold: 50 - type: prometheus metadata: serverAddress: http://prometheus.monitoring.svc:9090 query: | avg(vllm:gpu_utilization{servicellama3-8b}) threshold: 50这里要特别说一个经验自动伸缩阈值不要只设一个要设置差别很大的上下阈值。比如缩放上去的阈值是 GPU 利用率超过 80%缩下来的阈值是低于 20%。如果上下阈值靠得太近就会出现 Pod 反复创建删除的抖动每次创建都要加载模型浪费 GPU 资源。4.3 伸缩风暴的抑制禁止频繁缩容KEDA 默认的缩容冷却时间不够长对推理服务来说频繁缩容是灾难。我的配置里一定会改这几个参数spec: advanced: horizontalPodAutoscalerConfig: behavior: scaleUp: stabilizationWindowSeconds: 120 policies: - type: Percent value: 100 periodSeconds: 60 scaleDown: stabilizationWindowSeconds: 1800 policies: - type: Pods value: 1 periodSeconds: 300scaleDown的 stabilizationWindowSeconds 我设成了 1800 秒也就是半小时内不允许连续缩容。scaleUp则尽量激进因为流量上来时你要尽快扩容而缩容则需要谨慎晚缩几分钟不会有问题但缩错了就要承受排队的流量尖峰。4.4 节点级别的 GPU 弹性伸缩Pod 级别的自动伸缩解决的是“单节点内多副本”的问题如果整个节点池的 GPU 都被占满了Pod 伸缩也白搭。节点级别的 GPU 弹性伸缩需要配合集群的 Cluster Autoscaler 或 Karpenter 来做。我目前用的是 Cluster Autoscaler它监听调度失败的 Pod当 Pod 因为 GPU 资源不足而 pending 时自动为节点池扩容新的 GPU 节点。但我要提醒一句Cluster Autoscaler 对 GPU 节点的伸缩周期通常比较长从触发扩容到节点 ready可能 5 到 10 分钟。所以它是应对“小时级”流量波动的不能应对“分钟级”突发。如果业务确实有分钟的尖峰你得保留一个 GPU 节点的最小余量。5. 可观测性日志、指标与链路追踪5.1 推理服务的核心指标生成式 AI 服务的可观测性比传统 Web 服务多了几个关键指标它们是排障和容量规划的基石GPU 显存利用率模型推理时显存占用是否接近上限决定能否承受更大 batch。GPU 算力利用率SM 利用率多少判断是计算密集还是推理等待密集。首 token 延迟TTFT用户发出请求到收到第一个 token 的时间体验指标。每 token 生成延迟TPOT后续每个 token 生成速度影响输出总时长。请求队列深度排队中的请求数量判断是否需要扩容。每秒请求数RPS与并发数评估整体吞吐。这些指标vLLM 原生会暴露一部分比如vllm:gpu_cache_usage_percvllm:num_requests_running等。我通过 Prometheus 采集 vLLM 的指标端点再配合 DCGM 采集 GPU 硬件指标最终在 Grafana 上统一展示。5.2 日志的采集与管理大模型服务的日志有一个特点prompt 和响应结果可能包含敏感数据。我不建议把完整 prompt 打进日志而是做脱敏处理。我们实际做的是在日志采集端过滤掉prompt字段和output字段只记录 URL、状态码、耗时、token 用量。日志采集用 Filebeat Elasticsearch 的经典组合。每个 GPU 节点上跑一个 Filebeat DaemonSet采集容器标准输出和挂载的日志文件。这里要特别注意 Filebeat 对多行日志的处理——Python 的 traceback 是多行的如果没配多行匹配一条异常会被拆成无数条排障时简直要疯掉。5.3 链路追踪生成式 AI 链路从客户端到网关到推理服务中间还可能有 RAG 检索、向量数据库查询链路比传统 API 长得多。如果不做链路追踪出了问题只能靠猜。我的方案是 OpenTelemetry 链路追踪。在推理服务里埋点记录每个阶段耗时。Grafana Tempo 作为后端存储Grafana 里可视化。其中最重要的一个链路节点是“RAG 检索耗时”和“LLM 生成耗时”的拆分开。因为很多用户觉得“生成式 AI 响应慢”实际慢在向量数据库检索而不是模型推理。5.4 告警策略告警我这里只设了四个高优先级的因为告警太多等于没告警GPU 显存利用率超过 95% 持续 5 分钟。说明可能 OOM需要扩容或减小并发。TTFT 超过 10 秒持续 3 分钟。说明模型排队严重需要扩容副本。请求错误率超过 5%。说明服务异常或模型故障。GPU 节点掉线或 Device Plugin 异常。这是集群层面的故障。告警的收敛很重要。比如模型加载失败可能触发一连串的 5xx 告警但根因只有一个。我在告警规则里加了 “单个时间窗口内同一服务只触发一次” 的收敛策略避免告警风暴。6. 常见问题与排查技巧实录6.1 GPU 显存溢出OOM实战排查我在生产环境里碰到最多的一个问题就是模型推理到一半容器直接被杀dmesg里看到Out of memory: Killed process。这个问题的根因通常在 vLLM 的--gpu-memory-utilization参数上。如果设成 0.9意味着预留 10% 的显存给 KV cache 的碎片和其他开销。但有些操作比如长上下文生成、大 batch会临时申请更多显存超过预留阈值就 OOM。我的排查思路是三步走看 Pod 状态和重启次数kubectl describe pod看 Last State 和退出码。如果退出码为 137基本可以确认是被 OOM Killer 杀掉。看 vLLM 的日志重点关注CUDA out of memory或Killed字样能看到具体失败的显存申请大小。计算显存平衡gpu-memory-utilization和max-model-len相互影响。max-model-len 越大KV cache 占用越多。我最后的解决办法是把gpu-memory-utilization从 0.9 降到 0.8同时把max-model-len从 8192 降到 6144实测下来既满足了业务需求也留出了足够的显存余量。6.2 模型加载慢导致 Pod 一直重启另一个高频问题是Pod 创建后一直处于CrashLoopBackOff看起来是 readinessProbe 失败被反复杀掉。我的排查过程是这样的kubectl logs看容器日志确认模型是否加载成功。如果日志显示加载到一半就退出往往是显存不够或存储读取异常。kubectl exec进容器直接跑一遍python -c import torch; print(torch.cuda.is_available())确认 CUDA 可用。检查 JuiceFS 挂载是否正常到节点上df -h看挂载点。这类问题还有一个隐蔽原因Pod 启动时模型文件在远程存储上首次读取慢超过了 readinessProbe 的时间窗。我最后是把 initialDelaySeconds 调到 300 秒并且用生命周期 hook 在容器启动前预加载模型到本地缓存。6.3 自动伸缩不生效的秘密指标没采集到KEDA 配置好了看 Grafana 上指标也有但 Pod 就是不扩。这个问题我排查了一个下午最后发现是 Prometheus 的指标采集频率和 HPA 的同步周期不匹配。Kubernetes HPA 默认每 15 秒同步一次指标而 Prometheus 的抓取间隔是 30 秒。如果 KEDA 从 Prometheus 查询到的数据是旧值或者指标的rate()函数窗口太短导致指标值一直为 0HPA 就认为不需要扩容。我的解决方法是单独为推理服务设置一个抓取任务把 scrape_interval 调到 10 秒并且在 Prometheus 查询里使用max_over_time来平滑指标波动。改完后扩容响应时间从几分钟缩短到了 30 秒内。6.4 滚动更新时服务中断推理服务滚动更新默认策略是“先创建新 Pod等就绪后删旧 Pod”。但对于大模型推理这个默认策略有一个致命问题新 Pod 启动加载模型要 1 到 3 分钟旧 Pod 早已被删除整个服务在加载期间对外不可用。我用的方案有两个一是maxSurge: 1和maxUnavailable: 0保证更新期间始终有一个旧 Pod 在提供服务。二是 PreStop 钩子里加一个优雅退出逻辑收到 SIGTERM 时先等待正在处理的请求完成再真正退出。vLLM 框架对这个支持得不错具体实现是lifecycle: preStop: exec: command: [/bin/sh, -c, sleep 30]这个sleep 30是给 vLLM 预留 30 秒完成正在处理的请求。如果请求耗时更长可以适当增加。经验上 60 秒足以覆盖绝大多数情况。6.5 多模型共存的资源隔离同一个集群里跑多个模型最大的问题是互相干扰。一个大模型的显存占用冲到 90%可能把旁边小模型的显存挤爆。我的做法是按模型大小划分 nodeSelector 标签。大模型和小模型用不同的节点池。每个模型设置严格的 Limits禁止超卖。使用 PriorityClass 区分业务优先级核心模型优先调度。如果不做隔离我建议至少配置 LimitRange 和 ResourceQuota限制每个命名空间的 GPU 总量。7. 一点经验总结与后续规划这些年的实操下来我的体感是Kubernetes 跑生成式 AI最大的难点不是部署本身而是“资源效率”。同样的 GPU 集群调度策略和伸缩策略不同最终吞吐量和成本可能差出两三倍。如果你现在正要上手我给三个最实用的建议第一版本稳妥为主不要追新。先把一套成熟版本跑透再考虑升级。我们的集群从 v1.26.0 平稳运行了大半年才逐步测试升级到更高版本。第二一定要做 GPU 资源监控。没有 DCGM 和 Prometheus你排查显存问题就像闭着眼睛开车。第三自动伸缩的参数要反复调优。先调容忍的抖动范围再调 HPA 上下阈值不要一上来就追求全自动。后续我计划在这个系列里再聊聊多集群管理、GPU 共享与 MIG、以及 RAG 架构在 Kubernetes 上的落地欢迎继续关注。如果你在部署过程中遇到这里没有写到的坑也欢迎在评论区和大家交流说不定你的问题就是下一篇的主题。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号