2、Kubernetes核心概念速览:Pod、Deployment、Service、ConfigMap、Secret,为部署vLLM做准备

好,咱们进入正题。在把vLLM塞进K8s之前,有几个核心概念必须得先搞明白。说白了,K8s就是一套管理容器的“操作系统”,而Pod、Deployment这些,就是它里面的“进程”、“文件”和“网络配置”。

我个人习惯,学新东西先看骨架,再看血肉。所以这一章,咱们就把K8s里跟vLLM部署最相关的五个概念捋一遍。你想想看,vLLM跑起来需要GPU、需要模型文件、需要对外暴露API端口,这些需求,正好对应着K8s里的不同资源。

核心逻辑图:K8s资源如何支撑vLLM运行

K8s核心概念 → vLLM部署映射 Pod Deployment Service ConfigMap Secret vLLM进程 + GPU资源 + 模型加载 副本数管理、滚动更新、故障自愈 对外暴露API端口(如8000) 模型路径、max_model_len等配置 HuggingFace Token、API密钥

2.1 Pod:vLLM的“家”

Pod是K8s里最小的调度单元。一个Pod可以包含一个或多个容器,这些容器共享网络和存储。对于vLLM来说,一个Pod里通常就一个主容器——跑vLLM推理进程的那个。

我记得刚开始接触K8s时,总有人问:“为啥不直接跑容器,非要套个Pod?”原因很简单:Pod提供了“共享上下文”。比如vLLM需要加载一个很大的模型文件,你可以把模型放在一个共享卷里,Pod里的所有容器都能访问。虽然咱们一般只用单容器Pod,但这个设计让扩展变得非常灵活。

💡 我的经验: 在Pod定义里,一定要显式声明GPU资源。否则K8s不会自动把GPU分配给vLLM。我曾经见过一个同事,Pod跑起来了,但vLLM一直报“CUDA error”,查了半天发现是没写 resources.limits.nvidia.com/gpu

apiVersion: v1
kind: Pod
metadata:
  name: vllm-pod-example
spec:
  containers:
  - name: vllm
    image: vllm/vllm-openai:latest
    resources:
      limits:
        nvidia.com/gpu: 1   # 必须声明!否则GPU不可用
    ports:
    - containerPort: 8000   # vLLM默认API端口

2.2 Deployment:让vLLM“高可用”

Pod是“一次性”的。节点挂了,Pod就没了。你总不希望用户正问着问题,突然服务中断吧?这时候就需要Deployment出场了。

Deployment负责管理Pod的“期望状态”。你告诉它“我要3个vLLM副本”,它会一直确保有3个Pod在运行。如果某个Pod挂了,它会自动再起一个。如果模型版本更新了,它还能做滚动更新,逐个替换Pod,保证服务不中断。

说白了,Deployment就是给Pod加了一层“保险”。我在生产环境里,从来不会直接创建Pod,都是用Deployment。哪怕只跑1个副本,也用它——因为万一Pod被误删了,Deployment能秒级恢复。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-deployment
spec:
  replicas: 2          # 跑2个vLLM实例
  selector:
    matchLabels:
      app: vllm
  template:
    metadata:
      labels:
        app: vllm
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        resources:
          limits:
            nvidia.com/gpu: 1

⚠️ 注意: 多副本部署vLLM时,每个Pod都会加载一份完整的模型到显存里。如果你的GPU显存是80GB,模型需要70GB,那一个节点上只能跑1个副本。别想着“我节点有8张卡,就开8个副本”——得看模型大小和显存容量。

2.3 Service:给vLLM一个“固定入口”

Pod的IP是动态的。每次重启,IP都会变。客户端总不能每次都去查IP吧?Service就是解决这个问题的——它提供一个稳定的虚拟IP和DNS名称,把请求转发到后端的Pod上。

对于vLLM,我们通常创建一个ClusterIP类型的Service,让集群内部的其他服务(比如你的前端应用)通过这个Service访问vLLM的API。如果需要对外暴露,可以改成NodePort或LoadBalancer。

嗯,这里要注意:Service的selector要跟Deployment里的label匹配。我见过有人把label写错了,结果Service找不到后端Pod,请求全超时。排查起来还挺费劲的。

apiVersion: v1
kind: Service
metadata:
  name: vllm-service
spec:
  selector:
    app: vllm          # 必须匹配Deployment的label
  ports:
  - protocol: TCP
    port: 8000          # Service端口
    targetPort: 8000    # Pod内vLLM端口
  type: ClusterIP       # 集群内部访问

2.4 ConfigMap:管理vLLM配置

vLLM有很多启动参数,比如模型路径、最大序列长度、张量并行度等。把这些参数硬编码在Deployment YAML里?太不灵活了。ConfigMap就是用来存这些配置的。

你可以把ConfigMap挂载到Pod里,或者通过环境变量注入。我个人习惯用环境变量方式,因为vLLM的启动命令可以直接读取环境变量,改动配置时只需要更新ConfigMap,然后重启Pod就行,不用重新构建镜像。

apiVersion: v1
kind: ConfigMap
metadata:
  name: vllm-config
data:
  model_name: "meta-llama/Llama-2-7b-chat-hf"
  max_model_len: "4096"
  tensor_parallel_size: "1"

💡 避坑指南: ConfigMap的value默认都是字符串。如果你的参数是数字或布尔值,记得在启动脚本里做类型转换。我曾经因为 max_model_len 被当成字符串传给vLLM,结果它报了个类型错误,排查了半小时。

2.5 Secret:保护敏感信息

ConfigMap存的是非敏感配置。那HuggingFace的access token、API密钥这些敏感信息怎么办?用Secret。它跟ConfigMap用法类似,但数据会做base64编码,并且K8s会控制访问权限。

比如你要从HuggingFace下载一个需要授权的模型,就得提供token。把这个token放在Secret里,然后挂载到Pod中,vLLM启动时就能读取到。

apiVersion: v1
kind: Secret
metadata:
  name: hf-secret
type: Opaque
data:
  token: aGZfxxxxxxxxxxxxxxxxxxxx   # base64编码后的token

⚠️ 安全提醒: base64不是加密,只是编码。任何有权限查看Secret的人都能解码。在生产环境,建议配合外部密钥管理服务(如Vault)使用。另外,不要把Secret提交到Git仓库里——我见过有人把生产环境的token直接写在YAML里传到了GitHub,后果嘛...你懂的。

小结

这五个概念,就是你在K8s上部署vLLM的“工具箱”:

  • Pod:vLLM进程的载体,记得声明GPU资源
  • Deployment:保证vLLM高可用,支持滚动更新
  • Service:提供稳定的访问入口
  • ConfigMap:管理模型路径、推理参数等配置
  • Secret:保护HuggingFace token等敏感信息

把这些概念理清了,下一章咱们就可以动手写一个完整的vLLM部署YAML了。到时候你会发现,原来这些概念组合起来,就是一套生产级的推理服务。


专注资料整理