vLLM 服务上 K8s 前,我先把 GPU、探针和镜像过了一遍

这次不是写模型调参。

服务用 vLLM 跑,测试环境里接口能返回结果,但准备上 K8s 时问题开始变多:新节点拉镜像慢,Pod 偶尔 Pending,探针在模型加载完成前就开始失败,压测时 GPU 利用率也不稳定。

我一开始也想看 vLLM 参数。后来发现更应该先把运行环境排清楚:镜像能不能进来,GPU 资源是否声明准确,探针是否给冷启动留时间,日志能不能看出 GPU 到底有没有被有效使用。

下面是这次留下来的检查顺序。

先把关键镜像拆开

推理服务不止一个镜像。vLLM、CUDA 基础环境、监控组件、网关组件都可能来自不同来源。直接 apply YAML,失败时很容易只看到 ImagePullBackOffContainerCreating 卡住。

我先单独拉关键镜像:

bash 复制代码
docker pull docker.1ms.run/vllm/vllm-openai:latest
docker pull docker.1ms.run/nvidia/cuda:12.4.1-runtime-ubuntu22.04
docker pull docker.1ms.run/prom/prometheus:latest

这一步只解决依赖进入环境的问题。它不是性能优化,也不是 GPU 调度方案。先把这一层排掉,后面看 Pending、探针和日志时会少很多干扰。

Deployment 里把 GPU 写明确

最小 Deployment 可以先这样写:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-api
  namespace: ai-infer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: llm-api
  template:
    metadata:
      labels:
        app: llm-api
    spec:
      nodeSelector:
        accelerator: nvidia
      containers:
        - name: vllm
          image: docker.1ms.run/vllm/vllm-openai:latest
          args:
            - "--model"
            - "/models/qwen"
            - "--host"
            - "0.0.0.0"
            - "--port"
            - "8000"
          ports:
            - containerPort: 8000
          resources:
            limits:
              nvidia.com/gpu: "1"
          volumeMounts:
            - name: model-dir
              mountPath: /models
      volumes:
        - name: model-dir
          persistentVolumeClaim:
            claimName: qwen-model-pvc

这里的重点不是这份 YAML 能直接适配所有环境,而是几个边界要清楚:

  • GPU 节点用标签筛出来。
  • Pod 明确声明 GPU。
  • 模型目录用 PVC 或固定挂载,不要混在镜像里。
  • 镜像地址写在 image:,后续回滚和审计都看得见。

Kubernetes v1.36 继续推进 DRA 相关能力,方向也是让设备类资源更可声明、更可调度、更可观察。即使当前集群还没用 DRA,把 GPU 资源和事件先写清楚也很有必要。

Pending 先看调度,不要急着改模型

Pod 没起来时,我先看事件:

bash 复制代码
kubectl -n ai-infer get pod -l app=llm-api
kubectl -n ai-infer describe pod -l app=llm-api
kubectl get node -L accelerator
kubectl describe node <gpu-node-name>

常见分支:

现象 优先看
Insufficient nvidia.com/gpu GPU 资源是否被占满
node(s) didn't match node selector 节点标签是否正确
ImagePullBackOff 镜像地址、网络、凭证
长时间 ContainerCreating PVC、模型目录、镜像体积

这一步不要被模型参数带偏。Pod 还没进入稳定运行前,先看调度和依赖。

探针要给模型冷启动留时间

vLLM 服务的冷启动通常比普通 API 慢。模型加载、显存分配、KV cache 准备都会占时间。

我会把探针拆开:

yaml 复制代码
startupProbe:
  httpGet:
    path: /health
    port: 8000
  failureThreshold: 60
  periodSeconds: 5

readinessProbe:
  httpGet:
    path: /health
    port: 8000
  initialDelaySeconds: 20
  periodSeconds: 10

livenessProbe:
  httpGet:
    path: /health
    port: 8000
  initialDelaySeconds: 120
  periodSeconds: 20

startupProbe 给模型加载时间;readinessProbe 决定什么时候接流量;livenessProbe 再决定是否重启。GPU 服务不要只配一个很激进的健康检查。

日志和 GPU 状态一起看

启动以后我会看四组信息:

bash 复制代码
kubectl -n ai-infer logs deploy/llm-api --tail=120
kubectl -n ai-infer describe pod -l app=llm-api
kubectl top pod -n ai-infer
nvidia-smi

如果业务日志显示服务正常,但 nvidia-smi 里利用率很低,就要看请求是否排队、batch 是否合理、服务是否真正打到 GPU。

如果 kubectl top pod 看起来正常,但请求延迟很高,就要补应用侧指标:首 token 延迟、排队时间、生成 token 速度、错误率。

回滚要提前验证

我会在上线前跑一次回滚链路:

bash 复制代码
kubectl -n ai-infer rollout status deploy/llm-api
kubectl -n ai-infer rollout history deploy/llm-api
kubectl -n ai-infer rollout undo deploy/llm-api

推理服务的回滚还要看模型目录。镜像回去了,模型权重没回去,也可能出现行为不一致。比较稳的做法是让镜像版本、模型版本和配置版本都能对应起来。

这次留下来的清单

  • 镜像先单独拉,别把依赖问题留到 Pod 事件里。
  • GPU 节点标签和资源声明要明确。
  • Pending 先看调度和节点事件。
  • startupProbe 给模型加载留时间。
  • readiness 和 liveness 分开看。
  • 日志要能对上 GPU 利用率、显存和请求延迟。
  • 回滚要同时覆盖镜像、模型目录和参数配置。

复盘

vLLM 上 K8s 以后,问题不只在模型参数。GPU 服务是一类更贵、更慢、更依赖资源边界的工作负载。

我现在会先问:依赖能不能进环境?GPU 有没有被正确声明?探针是不是太急?日志能不能解释 GPU 在做什么?回滚是不是演练过?

这些问题答清楚,再谈吞吐和成本优化会更踏实。

相关推荐
用户298698530141 小时前
用 Java 操作 Word 文档?试试添加内容控件
java·后端
golang学习记1 小时前
Go 里什么时候可以“panic”?
后端
jakeswang2 小时前
【AI面经】大模型半夜发短信骂客户?Agent 工具调用失控,你如何设计防护机制?
java·后端
神奇小汤圆2 小时前
如何设计实现一个 LLM Gateway ?
后端
神奇小汤圆2 小时前
2026最新Java面试【高频真题+答案】大厂面试官带你划重点(建议收藏)
后端
扉页的墨2 小时前
Go Channel 高级用法:那个让线上服务半夜宕机的 select 死锁,我排查了6个小时
后端·面试·go
用户5850435573472 小时前
RESTful API 及其 SpringMVC 实现
后端