2. Kubernetes核心概念回顾:Pod、Deployment、Service、Namespace、资源配额

好,咱们进入第二章。说实话,很多搞AI推理的同学一上来就问我:“老师,我能不能直接跳过K8s基础,直接学vLLM扩缩容?”我的回答永远是——不行。你想想看,HPA自动扩缩容的本质是什么?是K8s在帮你管理Pod的数量。如果你连Pod是什么、Deployment怎么用都不清楚,那后面调参数、排故障的时候,真的会一头雾水。

这一章,我带你快速过一遍K8s最核心的几个概念。都是干货,不废话。我会结合我在生产环境中踩过的坑来讲,保证你听完就能用。

2.1 Pod:K8s里最小的调度单元

Pod是啥?说白了,它就是一组容器的“集合体”。在K8s的世界里,你没法直接跑一个容器,你必须把它包在Pod里。一个Pod可以包含一个或多个容器,这些容器共享网络栈、存储卷,以及一些生命周期管理。

我在项目中遇到过一个问题:有个同事把vLLM的推理进程和日志收集器放在了两个不同的Pod里,结果日志收集器老是连不上推理进程。后来我让他改成同一个Pod里的两个容器,问题瞬间解决。为什么?因为同一个Pod里的容器可以通过localhost互相通信,根本不需要走Service。

核心要点:
  • Pod是K8s的最小调度单位,不是容器
  • 同一个Pod内的容器共享IP和端口空间
  • Pod是“临时”的——它可能随时被销毁和重建
我的习惯:在生产环境中,我通常一个Pod只放一个主容器,再加一个Sidecar(比如日志采集、监控指标暴露)。这样职责清晰,排查问题也方便。

2.2 Deployment:声明式的Pod管理利器

你直接创建一个Pod,万一它挂了怎么办?K8s不会自动帮你重启。这时候就需要Deployment出场了。

Deployment是一个控制器,它负责保证你期望的Pod数量始终存在。你告诉它“我要3个vLLM推理Pod”,它就会一直盯着,少了就补,多了就删。而且它还支持滚动更新——你更新镜像版本时,Pod会逐个替换,不会造成服务中断。

我曾经踩过一个坑:有一次我直接修改了Deployment的replicas从3改成10,结果集群资源不够,Pod一直处于Pending状态。后来我才意识到,资源配额(ResourceQuota)没配好,Deployment根本拉不起那么多Pod。嗯,这里要注意——Deployment只管“声明”,不管“能不能调度成功”。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference
spec:
  replicas: 3
  selector:
    matchLabels:
      app: vllm
  template:
    metadata:
      labels:
        app: vllm
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        ports:
        - containerPort: 8000
        resources:
          requests:
            memory: "8Gi"
            cpu: "4"
          limits:
            memory: "16Gi"
            cpu: "8"
避坑指南:我曾经因为忘记设置resources.requests,导致HPA无法准确计算Pod的CPU/内存使用率,扩缩容完全失效。记住:HPA依赖的是requests值,不是limits值。

2.3 Service:稳定的网络入口

Pod是会漂移的——它可能因为节点故障、滚动更新等原因被重建,IP地址也会变。那客户端怎么访问你的vLLM服务呢?靠Service。

Service为一组Pod提供了一个稳定的虚拟IP(ClusterIP)和DNS名称。你访问Service,K8s会自动把流量转发到后端的某个Pod上。说白了,Service就是一个“负载均衡器”。

我个人习惯把Service和Deployment分开定义。虽然可以用kubectl expose命令一键生成,但生产环境我建议手写YAML,因为你可以精确控制端口映射、会话亲和性等细节。

apiVersion: v1
kind: Service
metadata:
  name: vllm-service
spec:
  selector:
    app: vllm
  ports:
  - protocol: TCP
    port: 8000
    targetPort: 8000
  type: ClusterIP
小技巧:如果你的vLLM推理服务需要暴露到集群外部,可以把type改成NodePort或LoadBalancer。但注意,生产环境建议用Ingress统一管理入口,而不是直接暴露Service。

2.4 Namespace:逻辑隔离的艺术

一个K8s集群里可能有多个团队、多个项目在跑。如果没有隔离,大家互相抢资源、命名冲突,那画面太美我不敢看。Namespace就是用来做逻辑隔离的。

你可以把Namespace理解成一个“虚拟集群”。不同的Namespace里,可以有同名的Deployment、Service,互不干扰。而且你可以给每个Namespace设置资源配额,防止某个团队把集群资源吃光。

我记得有一次,一个同事在default命名空间里直接跑了个大模型推理任务,结果把整个集群的GPU都占满了,其他团队的训练任务全部Pending。后来我强制要求:每个项目必须使用独立的Namespace,并配置ResourceQuota。从那以后,再也没出过类似问题。

推荐实践:
  • 每个环境(dev/staging/prod)使用独立的Namespace
  • 每个团队或项目使用独立的Namespace
  • 永远不要在default命名空间里跑生产负载

2.5 资源配额:给Pod戴上“紧箍咒”

资源配额(ResourceQuota)是K8s提供的一种限制机制。你可以限制一个Namespace下所有Pod的总CPU、总内存、总存储等资源上限。没有配额,一个“调皮”的Pod就可能把整个集群搞垮。

在vLLM推理场景下,资源配额尤其重要。因为大模型推理对GPU显存和CPU内存的需求非常高。如果不加限制,一个推理Pod可能吃掉所有可用内存,导致其他Pod被OOM Kill。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: vllm-quota
  namespace: ai-inference
spec:
  hard:
    requests.cpu: "40"
    requests.memory: "80Gi"
    limits.cpu: "80"
    limits.memory: "160Gi"
    persistentvolumeclaims: "10"
    requests.nvidia.com/gpu: "8"
避坑指南:我曾经遇到过一个情况:ResourceQuota设置了GPU数量上限,但忘记设置requests.nvidia.com/gpu这个字段。结果Pod还是能无限申请GPU,直到集群资源耗尽。记住:GPU资源也要在ResourceQuota里明确声明。

2.6 知识体系总览

为了让你更直观地理解这些概念之间的关系,我画了一张图。你可以看到:Namespace是顶层容器,里面包含Deployment和Service;Deployment管理Pod的生命周期;Pod里运行着容器;Service为Pod提供稳定的访问入口;ResourceQuota则对整个Namespace的资源使用进行约束。

Namespace: ai-inference ResourceQuota (资源配额) Deployment: vllm-inference Pod: vllm-pod-1 (副本1) 容器: vllm (推理进程) + sidecar (日志) Pod: vllm-pod-2 (副本2) 容器: vllm (推理进程) + sidecar (日志) Service: vllm-service ClusterIP: 10.96.0.1 端口: 8000 → 8000 选择器: app=vllm 外部客户端 Deployment (管理Pod) Pod (运行容器) Service (稳定入口)

这张图把整个知识体系串起来了。你记住一句话:Namespace是家,Deployment是管家,Pod是员工,Service是前台,ResourceQuota是财务。这样理解起来是不是轻松多了?

我的建议:学完这一章,你可以自己动手在测试集群里创建一套完整的资源:先建Namespace,再建ResourceQuota,然后部署Deployment和Service。用kubectl get all -n <namespace>看看效果。实践出真知,光看文档是记不住的。

好了,这一章的核心概念就讲到这里。下一章我们会深入HPA的原理和配置,到时候你会看到这些基础概念是如何被串联起来的。嗯,先消化一下今天的内容,有问题随时问我。


公众号:蓝海资料掘金营,微信deep3321