【infra之路】Kubernetes 核心概念详解:AI Infra 视角下的容器编排

前言

在 AI Infra 领域,Kubernetes(简称 K8s)已经成为管理 GPU 集群和 AI 训练/推理工作负载的事实标准。无论是训练一个 7B 模型需要调度 8 张 A100,还是部署一个 vLLM 推理服务需要自动扩缩容,背后都是 K8s 在做资源分配和生命周期管理。

但对于非运维背景的同学来说,K8s 的概念体系很容易让人晕头转向:Pod、Deployment、Service、ConfigMap、DaemonSet......这些东西之间是什么关系?为什么需要这么多层抽象?

这篇文章从 AI Infra 的视角出发,把 K8s 的核心概念讲清楚,重点放在你需要理解到什么程度才能做好 AI 基础设施的工作

一、Kubernetes 是什么,为什么需要它

K8s 是一个容器编排系统。你可以把它理解为一个"自动化的运维工程师":你告诉它"我需要 3 个 nginx 实例始终运行",它就帮你保证这件事------如果有实例挂了,它自动拉起新的;如果节点资源不够,它把实例调度到更合适的节点上。

在 AI 场景下,K8s 解决的核心问题是:

  • GPU 资源调度:集群里有几十张 GPU,谁来决定哪个训练任务用哪几张卡?
  • 工作负载管理:训练任务挂了怎么办?推理服务需要自动扩缩容怎么办?
  • 环境一致性:不同人的训练环境怎么保证一致?容器镜像解决了这个问题。

二、集群架构:控制平面 + 工作节点

图1:K8s 集群架构。左侧是控制平面(Control Plane),负责决策和状态管理;右侧是工作节点(Worker Node),负责实际运行容器。

一个 K8s 集群由两部分组成:

控制平面(Control Plane / Master)

控制平面是集群的"大脑",做所有决策。它有四个核心组件:

kube-apiserver :集群的统一入口。你对 K8s 的所有操作(kubectl applykubectl get)都是发给 API Server 的 RESTful 请求。它负责认证、授权和准入控制。

etcd:集群的唯一状态存储,一个高可用的 Key-Value 数据库。集群的所有"真相"都存在这里------有哪些 Pod、哪些 Node、配置是什么。etcd 挂了,整个集群就失去了记忆。

kube-scheduler:调度器。当一个新 Pod 被创建但还没有分配到节点时,调度器根据资源需求(CPU、内存、GPU)、亲和性规则、节点负载等因素,选出一个最合适的节点。

kube-controller-manager :控制器管理器。运行一系列控制器进程,持续把集群的当前状态期望状态对齐。比如你声明"需要 3 个副本",但实际只有 2 个,控制器就会创建一个新的。

工作节点(Worker Node)

工作节点是真正干活的地方,每个节点上有三个组件:

kubelet:节点上的代理。接收控制平面的指令(Pod Spec),确保本节点上的容器按预期运行。它定期向 API Server 汇报节点状态。

kube-proxy:网络代理。维护节点上的网络规则(iptables/IPVS),实现 Service 的负载均衡和流量转发。

容器运行时:实际运行容器的软件,如 containerd 或 CRI-O。早期 K8s 用 Docker,现在 Docker 已被弃用,直接使用 containerd。

三、核心资源对象

K8s 中的一切都是"资源对象",用 YAML 文件声明,通过 kubectl apply -f 提交给 API Server。下面按重要性排序讲解。

图2:K8s 核心资源对象的关系。Deployment 管理 ReplicaSet,ReplicaSet 管理 Pod,Service 通过标签选择器关联 Pod,对外暴露稳定的访问入口。

3.1 Pod ------ 最小调度单元

Pod 是 K8s 中最小的、不可再分的部署单元。一个 Pod 包含一个或多个紧密关联的容器,它们共享网络命名空间(同一个 IP)和存储卷。

你可以把 Pod 想象成一台"虚拟机"------里面有你的应用容器,它们可以通过 localhost 互相通信,共享同一个 IP 地址。

yaml 复制代码
apiVersion: v1
kind: Pod
metadata:
  name: training-job
  labels:
    app: llm-train
spec:
  containers:
  - name: trainer
    image: pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime
    command: ["python", "train.py"]
    resources:
      limits:
        nvidia.com/gpu: 1     # 请求 1 块 GPU
        memory: "32Gi"
        cpu: "8"

重要认知:Pod 是临时的,随时可能被创建、销毁、重启。永远不要直接依赖 Pod IP------它每次重建都会变。这就是为什么需要 Service。

3.2 Deployment ------ 管理 Pod 的期望状态

你不会手动创建 Pod(那太累了)。Deployment 帮你管理一组 Pod 的期望状态:需要几个副本、用什么镜像、怎么更新。

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llm-inference
spec:
  replicas: 3           # 始终保持 3 个 Pod 运行
  selector:
    matchLabels:
      app: llm-inference
  template:             # Pod 模板
    metadata:
      labels:
        app: llm-inference
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        ports:
        - containerPort: 8000
        resources:
          limits:
            nvidia.com/gpu: 1

Deployment 的工作方式是:创建 ReplicaSet → ReplicaSet 创建和管理 Pod。当你更新镜像版本时,Deployment 会做滚动更新(Rolling Update)------逐步替换旧 Pod,保证服务不中断。

3.3 Service ------ 稳定的访问入口

Pod 有 IP,但 IP 会变。Service 给一组 Pod 提供一个稳定的 IP 和 DNS 名称,并通过标签选择器(Label Selector)自动发现后端 Pod。

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: llm-inference-svc
spec:
  selector:
    app: llm-inference      # 关联标签为 app=llm-inference 的 Pod
  ports:
  - port: 80                # Service 暴露的端口
    targetPort: 8000         # Pod 上容器的端口
  type: ClusterIP            # 集群内访问

Service 有几种类型:

类型 说明 场景
ClusterIP 集群内部 IP,外部无法访问 内部服务间通信
NodePort 在每个节点上开一个固定端口(30000-32767) 简单的外部访问
LoadBalancer 由云厂商提供外部负载均衡器 生产环境对外暴露服务

3.4 ConfigMap 与 Secret ------ 配置与密钥

把配置从容器镜像中解耦出来,做到"一次构建镜像,多处使用不同配置"。

yaml 复制代码
# ConfigMap: 存储非敏感配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: training-config
data:
  learning_rate: "0.001"
  batch_size: "32"
  model_name: "llama-7b"

---
# Secret: 存储敏感信息(Base64 编码)
apiVersion: v1
kind: Secret
metadata:
  name: wandb-secret
type: Opaque
data:
  api-key: d2FuZGIua2V5Lnh4eA==    # echo -n 'wandb.key.xxx' | base64

在 Pod 中可以通过环境变量或挂载文件的方式使用它们。

3.5 Namespace ------ 逻辑隔离

Namespace 把集群划分成多个逻辑空间,不同 Namespace 的资源互相隔离(但不完全隔离网络)。

bash 复制代码
kubectl create namespace training      # 训练任务
kubectl create namespace inference     # 推理服务
kubectl create namespace monitoring    # 监控组件

在企业环境中,Namespace 常用来隔离不同团队或项目,配合 ResourceQuota 限制每个空间的资源上限。

四、一个完整的 AI 推理服务部署示例

把上面的概念串起来,看一个部署 LLM 推理服务的完整 YAML:

yaml 复制代码
# 1. ConfigMap: 推理参数
apiVersion: v1
kind: ConfigMap
metadata:
  name: vllm-config
data:
  model: "meta-llama/Llama-2-7b-hf"
  max-model-len: "4096"
  gpu-memory-utilization: "0.9"
---
# 2. Secret: HuggingFace Token
apiVersion: v1
kind: Secret
metadata:
  name: hf-token
type: Opaque
data:
  token: aGZfeHh4eA==
---
# 3. Deployment: 管理推理 Pod
apiVersion: apps/v1
kind: Deployment
metadata:
  name: llama-inference
spec:
  replicas: 2
  selector:
    matchLabels:
      app: llama-inference
  template:
    metadata:
      labels:
        app: llama-inference
    spec:
      containers:
      - name: vllm
        image: vllm/vllm-openai:latest
        args:
          - --model
          - $(MODEL_NAME)
          - --max-model-len
          - $(MAX_LEN)
        env:
          - name: MODEL_NAME
            valueFrom:
              configMapKeyRef:
                name: vllm-config
                key: model
          - name: MAX_LEN
            valueFrom:
              configMapKeyRef:
                name: vllm-config
                key: max-model-len
          - name: HF_TOKEN
            valueFrom:
              secretKeyRef:
                name: hf-token
                key: token
        ports:
        - containerPort: 8000
        resources:
          limits:
            nvidia.com/gpu: 1
            memory: "32Gi"
            cpu: "8"
---
# 4. Service: 暴露稳定的 API 端点
apiVersion: v1
kind: Service
metadata:
  name: llama-api
spec:
  selector:
    app: llama-inference
  ports:
  - port: 80
    targetPort: 8000
  type: LoadBalancer

部署命令:

bash 复制代码
kubectl apply -f llm-inference.yaml
kubectl get pods                    # 查看 Pod 状态
kubectl get svc llama-api           # 获取外部 IP
curl http://<EXTERNAL-IP>/v1/chat/completions -d '...'

五、GPU 调度:AI Infra 的核心话题

在 AI 场景中,GPU 是最稀缺的资源。K8s 原生不识别 GPU,需要通过插件机制来支持。

5.1 NVIDIA Device Plugin

K8s 通过 Device Plugin 机制 扩展硬件支持。NVIDIA 提供了一个 DaemonSet,在每个 GPU 节点上运行,向 kubelet 注册 nvidia.com/gpu 资源:

图3:K8s GPU 管理与 Device Plugin 工作机制。Device Plugin 以 DaemonSet 形式运行在每个 GPU 节点上,探测 GPU 资源并向 kubelet 注册,kubelet 上报给 API Server,调度器据此做调度决策。

复制代码
GPU 节点启动
  → NVIDIA Device Plugin Pod 启动(DaemonSet 自动部署)
  → 探测本节点的 GPU 数量和型号
  → 向 kubelet 注册扩展资源: nvidia.com/gpu = 8
  → kubelet 上报给 API Server: "本节点有 8 块 GPU"
  → 调度器就能根据 Pod 的 GPU 请求做调度决策

在 Pod 中请求 GPU 只需在 resources.limits 中声明:

yaml 复制代码
resources:
  limits:
    nvidia.com/gpu: 2    # 请求 2 块 GPU

注意 :GPU 只能在 limits 中声明,不能只在 requests 中。K8s 规定扩展资源(extended resources)必须 limits = requests,不支持超卖。

5.2 节点选择与 GPU 型号

集群里可能有不同型号的 GPU(A100、H100、V100)。通过**节点标签(Node Labels)**和 nodeSelector / nodeAffinity 让 Pod 调度到特定 GPU 的节点上:

bash 复制代码
# 给节点打标签
kubectl label nodes gpu-node-01 gpu-model=a100-80g
kubectl label nodes gpu-node-02 gpu-model=h100-80g
yaml 复制代码
# Pod 中选择节点
spec:
  nodeSelector:
    gpu-model: a100-80g

NVIDIA 的 GPU Feature Discovery(GFD)工具可以自动给节点打上 GPU 型号、显存大小、互联拓扑等标签。

5.3 多 GPU 分布式训练

分布式训练需要多个 Pod 协同工作,每个 Pod 使用若干 GPU。K8s 原生的 Deployment 不适合这种场景(Pod 之间没有协调机制),通常使用 JobStatefulSet ,配合 Volcano 等批调度器实现 Gang Scheduling

yaml 复制代码
# Gang Scheduling: 所有 Pod 要么一起启动,要么一起等
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: distributed-training
spec:
  minAvailable: 4          # 4 个 Pod 必须同时调度成功才启动
  tasks:
  - replicas: 4
    template:
      spec:
        containers:
        - name: trainer
          image: my-training-image:latest
          resources:
            limits:
              nvidia.com/gpu: 8    # 每个 Pod 用 8 卡
        # 总共 4 Pod × 8 GPU = 32 卡分布式训练

Gang Scheduling 解决了分布式训练的一个关键问题:如果 4 个 Worker 需要同时启动才能做 AllReduce,但 K8s 只调度了 3 个,那这 3 个就会一直等着------死锁。minAvailable: 4 保证 4 个都准备好了才一起启动。

5.4 GPU 共享与切分

一张 A100 80GB 可能跑多个小推理任务。K8s 支持两种方式在多个 Pod 之间共享一张 GPU:

时间分片(Time Slicing):NVIDIA Device Plugin 把一张物理 GPU 虚拟成多份。多个 Pod 轮流使用,通过时间片切换。适合推理场景,不适合训练(性能损耗大)。

MIG(Multi-Instance GPU):A100/H100 硬件级支持,把一张 GPU 物理切分成多个独立实例(如 7 个 5GB 实例),每个实例有独立的显存、缓存和计算单元,互不干扰。

六、存储与网络基础

持久化存储(PVC)

AI 训练需要读取大量数据集、保存模型 checkpoint。Pod 重启后本地文件会丢失,需要持久化存储。

K8s 通过 PV(PersistentVolume)+ PVC(PersistentVolumeClaim) 模式管理存储:

yaml 复制代码
# 声明需要 500GB 存储
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: dataset-storage
spec:
  accessModes:
    - ReadWriteMany        # 多 Pod 同时读
  resources:
    requests:
      storage: 500Gi
  storageClassName: nfs    # 使用 NFS 后端

然后在 Pod 中挂载:

yaml 复制代码
volumes:
- name: dataset
  persistentVolumeClaim:
    claimName: dataset-storage
containers:
- name: trainer
  volumeMounts:
  - name: dataset
    mountPath: /data       # Pod 内 /data 目录对应 PVC

常见的存储后端:NFS(简单通用)、Ceph(高性能分布式)、云厂商 CSI 驱动(EBS、GCE PD)。

网络模型

K8s 的网络设计遵循三个原则:

  1. 每个 Pod 有独立 IP------不需要端口映射(Docker 的痛点)
  2. Pod 之间可以直接通信------跨节点也能直连
  3. Node 上的代理可以和该 Node 上的所有 Pod 通信

网络实现由 CNI 插件负责(Calico、Cilium、Flannel 等)。对 AI Infra 来说,Pod 间通信主要用于分布式训练的梯度同步,性能敏感场景通常配合 RDMA 网络。

七、常用 kubectl 命令速查

bash 复制代码
# 集群信息
kubectl cluster-info                   # 集群状态
kubectl get nodes -o wide              # 节点列表(含 GPU 信息)
kubectl describe node gpu-node-01      # 节点详情(GPU 分配情况)

# 资源管理
kubectl apply -f deployment.yaml       # 创建/更新资源
kubectl delete -f deployment.yaml      # 删除资源
kubectl get pods -o wide               # Pod 列表(含节点和 IP)
kubectl get svc                        # Service 列表
kubectl get events --sort-by=.metadata.creationTimestamp  # 集群事件

# 调试
kubectl logs <pod-name>                # 查看容器日志
kubectl logs -f <pod-name>             # 实时跟踪日志
kubectl exec -it <pod-name> -- bash    # 进入容器
kubectl describe pod <pod-name>        # Pod 详情(含事件和调度信息)
kubectl top pods                       # Pod 资源使用率

# GPU 相关
kubectl get nodes -L nvidia.com/gpu.product  # 查看各节点 GPU 型号
kubectl get pods -o custom-columns=NAME:.metadata.name,GPU:.spec.containers[*].resources.limits.nvidia\.com/gpu
                                         # 查看各 Pod 的 GPU 请求量

八、AI Infra 面试常见 K8s 问题

Q:Pod 和容器的区别是什么?

Pod 是 K8s 的调度单元,可以包含多个容器。容器是实际运行的进程。一个 Pod 里的容器共享网络和存储,彼此可以通过 localhost 通信。K8s 不直接管理容器,只管理 Pod。

Q:Deployment 的滚动更新是怎么工作的?

Deployment 创建新的 ReplicaSet,逐步增加新 Pod 数量,同时减少旧 ReplicaSet 的 Pod 数量。通过 maxSurge(最多多出的 Pod 数)和 maxUnavailable(最多不可用的 Pod 数)控制节奏,保证服务不中断。

Q:如何在 K8s 上调度 GPU 任务?

通过 NVIDIA Device Plugin 注册 nvidia.com/gpu 扩展资源,Pod 在 resources.limits 中声明 GPU 需求,调度器根据节点可用 GPU 数量做调度。用 nodeSelector 或 nodeAffinity 选择特定 GPU 型号的节点。

Q:什么是 Gang Scheduling?为什么 AI 训练需要它?

Gang Scheduling 保证一组 Pod 要么全部被调度成功、要么全部等待。分布式训练(如 8 卡 AllReduce)要求所有 Worker 同时在线才能通信,如果只启动了一部分就会死锁。Gang Scheduling 通过 minAvailable 解决这个问题。

Q:Service 的 ClusterIP 和 NodePort 有什么区别?

ClusterIP 只在集群内部可访问,适合服务间内部通信。NodePort 在每个节点上开一个固定端口(30000-32767),外部可以通过 NodeIP:NodePort 访问。生产环境通常用 LoadBalancer 类型,由云厂商提供外部负载均衡。

九、总结

K8s 的概念体系虽然庞大,但核心逻辑很清晰:声明式 API + 控制器模式。你声明"我要什么"(YAML),控制器持续工作把现实往你的声明对齐。Pod 是最小运行单元,Deployment 管理 Pod 的副本和更新,Service 提供稳定访问入口,ConfigMap/Secret 管理配置。

对于 AI Infra 工程师来说,最重要的能力是:理解 GPU 调度机制(Device Plugin、节点标签、Gang Scheduling),能写训练/推理的 YAML 配置,知道怎么用 PVC 管理数据和模型存储,会用 kubectl 排查问题。这些是日常工作中每天都在用的东西。

参考资料