5Kubernetes 实战

Pod:基本构建块

Pod 深入

Pod标签注解

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第5章:Pod:Kubernetes的基本构建块(Pods: The Building Blocks of Kubernetes)

一、导读

1.1 本章学习目标

本章全面深入地讲解 Pod 的方方面面。从 Pod 的生命周期到标签和注解,从调度约束到 Init Container,你将掌握 Pod 的所有核心特性。这些知识帮助你将 Pod 调度到合适的节点、在启动前完成初始化、将 Pod 信息暴露给容器。

本章的核心学习目标包括:

  • 理解 Pod 的完整生命周期:从 Pending 到终止的每个阶段。
  • 掌握标签和注解的使用:Label 用于选择,Annotation 用于元数据。
  • 配置调度约束:nodeSelector、nodeAffinity、taint/toleration。
  • 使用 Init Container:在主容器启动前完成初始化任务。
  • 使用 Downward API:将 Pod 信息(名称、IP、标签等)暴露给容器。
  • 理解 Pod 的终止流程:优雅终止和强制终止的区别。

1.2 为什么本章如此重要

Pod 是 Kubernetes 中唯一可以直接创建的资源(其他资源都是控制器管理的)。深入理解 Pod 的特性,你才能:

  • 将 Pod 调度到合适的节点(GPU 节点、SSD 节点、特定区域)
  • 在应用启动前完成数据预热、等待依赖服务
  • 让容器感知自己的运行环境(Pod 名称、Namespace、标签)
  • 正确配置优雅终止,避免请求丢失

1.3 核心问题引导

Pod 从创建到终止经历了哪些阶段?每个阶段发生了什么?

nodeAffinity 和 nodeSelector 的区别是什么?什么时候用哪个?

Taint 和 Toleration 如何配合工作?有哪些内置的 Taint?

Init Container 和普通容器有什么区别?Init Container 之间如何传递数据?

当 Pod 被删除时,容器是如何被终止的?SIGTERM 和 SIGKILL 的区别?


二、核心概念详解

2.1 Pod 的生命周期

Pod 的完整生命周期:

Pending → Running → Succeeded/Failed
                ↓
           Terminating(删除中)
  • Pending:API Server 已创建 Pod 对象,但容器尚未运行

- 调度决策

- 拉取镜像

- 创建容器

  • Running:至少一个容器正在运行
  • Succeeded:所有容器正常退出(退出码 0),不会重启
  • Failed:至少一个容器异常退出
  • Unknown:无法与 kubelet 通信,无法获取状态

2.1.1 Pod 的条件(Conditions)

bash
kubectl get pod my-pod -o jsonpath='{.status.conditions}'

Pod 的 Conditions:

  • PodScheduled:Pod 已被调度到节点
  • ContainersReady:所有容器就绪
  • Initialized:所有 Init Container 完成
  • Ready:Pod 可以接收流量(ContainersReady && Initialized)

2.2 标签和注解

2.2.1 标签(Labels)

标签是 K8s 资源的核心标识机制。Controller 通过 Label Selector 管理资源。

yaml
metadata:
  labels:
    app: web
    env: production
    version: "1.0"
    team: backend
    tier: frontend

标签的命名规则:

  • 前缀(可选):DNS 子域名格式,如 kubernetes.io/
  • 名称:最长 63 字符,字母数字开头和结尾
  • 值:最长 63 字符,可以为空
bash
# 基于标签筛选
kubectl get pods -l 'app=web,env=production'
kubectl get pods -l 'app in (web,api)'
kubectl get pods -l 'version notin (v1.0)'
kubectl get pods -l '!canary'  # 不包含 canary 标签

# 管理标签
kubectl label pod my-pod env=staging
kubectl label pod my-pod env=production --overwrite
kubectl label pod my-pod env-  # 删除标签

2.2.2 注解(Annotations)

注解用于存储非标识性的元数据,不影响调度和选择。

yaml
metadata:
  annotations:
    kubernetes.io/change-cause: "Update to v1.1"
    prometheus.io/scrape: "true"
    prometheus.io/port: "9090"
    description: "This is a web server pod"
    owner: "team-backend"
    build-url: "https://ci.example.com/builds/123"

注解的常见用途:

  • 构建信息(commit hash、构建 URL)
  • 监控配置(Prometheus scrape 配置)
  • 部署工具元数据(Helm、ArgoCD)
  • 变更记录

2.3 调度约束

2.3.1 nodeSelector

最简单的节点选择方式:匹配节点标签。

yaml
spec:
  nodeSelector:
    disktype: ssd
    zone: us-east-1a
bash
# 给节点添加标签
kubectl label nodes node-1 disktype=ssd

2.3.2 nodeAffinity

更灵活的节点亲和性规则,支持多种操作符。

yaml
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: disktype
                operator: In
                values:
                  - ssd
                  - nvme
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 80
          preference:
            matchExpressions:
              - key: zone
                operator: In
                values:
                  - us-east-1a
        - weight: 20
          preference:
            matchExpressions:
              - key: zone
                operator: In
                values:
                  - us-east-1b

操作符:In、NotIn、Exists、DoesNotExist、Gt、Lt

2.3.3 Taint 和 Toleration

Taint(污点)标记在节点上,Toleration(容忍)配置在 Pod 上。只有 Toleration 匹配 Taint 的 Pod 才能调度到该节点。

bash
# 给节点添加 Taint
kubectl taint nodes node-1 gpu=true:NoSchedule
kubectl taint nodes node-1 dedicated=team-a:PreferNoSchedule
kubectl taint nodes node-1 maintenance=true:NoExecute

# 移除 Taint
kubectl taint nodes node-1 gpu=true:NoSchedule-
yaml
spec:
  tolerations:
    - key: "gpu"
      operator: "Equal"
      value: "true"
      effect: "NoSchedule"
    - key: "dedicated"
      operator: "Equal"
      value: "team-a"
      effect: "PreferNoSchedule"
    - key: "maintenance"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 3600

Taint 效果:

  • NoSchedule:新 Pod 不会被调度
  • PreferNoSchedule:尽量避免调度,但不保证
  • NoExecute:已运行的 Pod 会被驱逐(除非有 Toleration)

内置 Taint:

  • node.kubernetes.io/not-ready:节点未就绪
  • node.kubernetes.io/unreachable:节点不可达
  • node.kubernetes.io/out-of-disk:磁盘不足
  • node.kubernetes.io/memory-pressure:内存压力
  • node.kubernetes.io/disk-pressure:磁盘压力

2.4 Init Container

Init Container 在主容器启动之前运行,按顺序执行,每个必须成功完成。

yaml
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command: ['sh', '-c', 'until nc -z db-service 5432; do sleep 2; done']
    - name: data-preload
      image: data-loader:1.0
      command: ['python', 'preload.py']
      volumeMounts:
        - name: data
          mountPath: /data
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: data
          mountPath: /data
  volumes:
    - name: data
      emptyDir: {}

Init Container 的常见用途:

  • 等待依赖服务就绪
  • 数据预热/迁移
  • 生成配置文件
  • 下载必要的数据

2.5 Downward API

Downward API 将 Pod 的元数据暴露给容器。

yaml
spec:
  containers:
    - name: app
      image: myapp:1.0
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: APP_LABEL
          valueFrom:
            fieldRef:
              fieldPath: metadata.labels['app']
      volumeMounts:
        - name: pod-info
          mountPath: /etc/pod-info
          readOnly: true
  volumes:
    - name: pod-info
      downwardAPI:
        items:
          - path: "labels"
            fieldRef:
              fieldPath: metadata.labels
          - path: "annotations"
            fieldRef:
              fieldPath: metadata.annotations

2.6 Pod 的终止

当 Pod 被删除时,K8s 执行优雅终止流程:

API Server 将 Pod 标记为 Terminating

kubelet 发送 SIGTERM 给容器

容器开始优雅关闭(处理完当前请求、关闭连接)

如果超过 terminationGracePeriodSeconds(默认30秒),发送 SIGKILL 强制终止

删除 Pod

yaml
spec:
  terminationGracePeriodSeconds: 60
  containers:
    - name: app
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 15"]

preStop 钩子在 SIGTERM 之前执行,常用于:

  • 从 Service 的 Endpoints 中注销(等待负载均衡器更新)
  • 优雅关闭连接
  • 清理资源

三、实战案例

3.1 使用 Init Container 完成初始化

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
    - name: wait-for-dependencies
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          echo "Waiting for database..."
          until nc -z db-service 5432; do
            echo "Database not ready, waiting..."
            sleep 2
          done
          echo "Database is ready!"
          
          echo "Waiting for cache..."
          until nc -z cache-service 6379; do
            echo "Cache not ready, waiting..."
            sleep 2
          done
          echo "Cache is ready!"
    - name: setup-config
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          echo "Setting up configuration..."
          cat > /config/app.conf <<EOF
          database.host=db-service
          database.port=5432
          cache.host=cache-service
          cache.port=6379
          EOF
      volumeMounts:
        - name: config
          mountPath: /config
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: config
          mountPath: /etc/app-config
  volumes:
    - name: config
      emptyDir: {}

3.2 使用 nodeAffinity 调度到 GPU 节点

yaml
apiVersion: v1
kind: Pod
metadata:
  name: gpu-training
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: accelerator
                operator: In
                values:
                  - nvidia-tesla-v100
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions:
              - key: zone
                operator: In
                values:
                  - us-east-1a
  tolerations:
    - key: "gpu"
      operator: "Exists"
      effect: "NoSchedule"
  containers:
    - name: training
      image: tensorflow/tensorflow:latest-gpu
      resources:
        limits:
          nvidia.com/gpu: 1

四、本章小结

本章全面讲解了 Pod 的核心特性:

生命周期:Pending → Running → Succeeded/Failed,每个阶段有明确的条件检查。

标签和注解:Label 用于选择和标识,Annotation 用于存储元数据。

调度约束:nodeSelector 简单匹配、nodeAffinity 灵活规则、Taint/Toleration 控制调度。

Init Container:在主容器启动前按顺序执行初始化任务。

Downward API:将 Pod 信息暴露为环境变量或文件。

优雅终止:SIGTERM → preStop → 等待 → SIGKILL。


五、思考与练习

思考题

如果一个 Pod 同时配置了 nodeSelector 和 nodeAffinity,两者会如何交互?

Init Container 失败后会发生什么?Pod 会进入什么状态?

为什么 Downward API 不能暴露容器的资源限制?

优雅终止时,为什么需要 preStop 中的 sleep?直接发送 SIGTERM 有什么问题?

NoExecute Taint 和 Pod 的 tolerationSeconds 如何配合?

练习题

给集群节点添加自定义标签,使用 nodeAffinity 将 Pod 调度到特定节点。

配置 Taint 使某个节点只运行特定 Pod(如 GPU 工作负载)。

使用 Init Container 在应用启动前等待数据库就绪。

使用 Downward API 将 Pod 名称和标签暴露为环境变量。

配置优雅终止,观察 Pod 删除时的终止流程。

5/8Pod:
0%