路线图

Pod

星辉 2026-07-02 阅读 5 min 885 字 路线图
Pod 封面

Pod 是 Kubernetes 里最小的可调度单元,也是后面所有概念(Deployment、Service、Ingress...)的基础。学不好 Pod,后面的内容都是空中楼阁。本章从"为什么需要 Pod"讲起,覆盖 Pod 的 YAML 结构、多容器模式、生命周期和初始化容器。


Pod 是什么#

为什么 Pod 是最小调度单元#

你可能会想:Docker 里最小单位是容器,K8s 为什么不直接调度容器,而要包一层 Pod?

原因有两个:共享网络共享存储

有些场景下,多个容器需要:

  • localhost 互相通信(比如主应用容器 + 本地代理 sidecar)
  • 共享一块存储卷(比如主应用写日志,sidecar 读日志转发到外部)

如果 K8s 单独调度容器,就没法保证这两个容器一定被调度到同一台机器上——不同机器上的容器不能通过 localhost 通信,共享卷也无法挂载。

Pod 的 solution:把多个容器包装成一个调度单元,K8s 保证 Pod 里的所有容器一定跑在同一台机器的同一个网络命名空间里。

text
同一个 Pod 里的容器:
┌─────────────────────────────────────────┐
│  Pod (IP: 10.244.1.15)                  │
│                                         │
│  ┌─────────────┐  ┌──────────────────┐  │
│  │ 主容器        │  │ sidecar 容器      │  │
│  │ (app)       │  │ (log-collector) │  │
│  │             │  │                  │  │
│  │ localhost:8080  ← 互相访问用 localhost │  │
│  └─────────────┘  └──────────────────┘  │
│                                         │
│  共享:网络命名空间(同一 IP)、共享卷      │
└─────────────────────────────────────────┘

类比:宿舍和室友#

  • 容器 = 一个人
  • Pod = 一个宿舍(里面可以住一个人,也可以住两个人)
  • Node = 一栋楼

宿舍里的人共享同一个门牌号(Pod IP)和一些公共设施(共享卷)。K8s 调度时按"宿舍"分配房间(Node),不会把室友分到不同楼。

注意事项:绝大多数情况下一个 Pod 里只跑一个容器。多容器 Pod 主要用于 sidecar 模式(日志采集、服务网格代理、适配器)。如果你发现自己在 Pod 里放了两个业务容器,先想想能不能拆成两个独立的 Pod。


Pod YAML 结构#

下面是最精简但可运行的 Pod YAML,每个字段都标注了含义。

yaml
# apiVersion:API 组和资源版本,Pod 在核心 API 组(v1)
apiVersion: v1
kind: Pod
metadata:
  # name:Pod 名称,在同一 namespace 内必须唯一
  # 注意:直接创建的 Pod 名字是这个值;Deployment 创建的 Pod 会自动加后缀
  name: my-app
  # namespace:所属命名空间,不写默认是 default
  namespace: production
  # labels:标签,是 K8s 里最重要的概念之一
  # Service 靠 labels 找 Pod,Deployment 靠 labels 管 Pod
  labels:
    app: my-app
    env: production
    version: "1.0.0"
spec:
  # containers:容器列表(Pod 里可以跑多个容器)
  containers:
  - name: app          # 容器名称(在 Pod 内唯一)
    image: myapp:1.0.0 # 镜像名称和 tag
    # imagePullPolicy:镜像拉取策略
    # Always=每次都拉;IfNotPresent=本地没有才拉;Never=只用本地
    # tag 是 latest 时默认 Always,其他默认 IfNotPresent
    imagePullPolicy: IfNotPresent
    # ports:容器暴露的端口列表(文档性质,不写也能访问)
    ports:
    - containerPort: 8080   # 容器监听的端口
      protocol: TCP
    # env:环境变量
    env:
    - name: APP_ENV
      value: "production"
    - name: DB_HOST
      value: "db-service"
    # resources:资源请求和限制(非常重要!)
    resources:
      requests:        # 调度依据:K8s 保证节点有足够资源运行这个 Pod
        cpu: "100m"    # 0.1 个 CPU 核心
        memory: "128Mi"
      limits:          # 运行时上限:CPU 可压缩,超限被限流(throttle);内存不可压缩,超限被 OOM Kill
        cpu: "500m"
        memory: "256Mi"
    # livenessProbe:存活探针,失败会重启容器
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 10  # 启动后等 10 秒再开始检查
      periodSeconds: 10        # 每 10 秒检查一次
    # readinessProbe:就绪探针,失败会从 Service 摘除
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

注意事项:实际使用中你几乎不会直接写 Pod YAML,而是通过 Deployment 来管理 Pod。但直接创建 Pod 有助于理解每个字段的含义。

用 kubectl 跑起来试试#

bash
# 应用上面的 YAML
kubectl apply -f pod.yaml

# 查看 Pod 状态
kubectl get pod my-app -n production

# 看详细信息(排错必用)
kubectl describe pod my-app -n production

# 看日志
kubectl logs my-app -n production

# 进入容器
kubectl exec -it my-app -n production -- sh

多容器 Pod#

当一个 Pod 里跑多个容器时,最常见的模式是 sidecar(边车模式):主容器负责业务逻辑,sidecar 容器负责辅助功能。

示例:主容器 + 日志采集 sidecar#

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-logger
  labels:
    app: my-app
spec:
  # 共享卷:主容器写日志,sidecar 读日志
  volumes:
  - name: logs
    emptyDir: {}    # 空目录,Pod 删除后数据消失

  containers:
  # 主容器:业务应用
  - name: app
    image: myapp:1.0.0
    ports:
    - containerPort: 8080
    # 把日志写到共享卷
    volumeMounts:
    - name: logs
      mountPath: /var/log/app

  # sidecar 容器:日志采集代理
  - name: log-collector
    image: fluentd:alpine
    # 读取共享卷里的日志,转发到外部日志系统
    volumeMounts:
    - name: logs
      mountPath: /var/log/app
      readOnly: true

两个容器通过 logs 卷共享日志文件:主容器写,sidecar 读并转发。它们在同一 Pod 里,可以用 localhost 互相通信,共享同一个 Pod IP。

其他多容器模式#

除了 sidecar,还有两种常见模式:

  • Adapter(适配器):把主容器的输出格式转换成外部系统需要的格式。比如主容器输出自定义 metrics 格式,adapter 转成 Prometheus 格式。
  • Ambassador(大使):代表主容器处理外部通信。比如主容器只管业务逻辑,大使容器负责服务发现、重试、熔断等网络逻辑。

注意事项:某个容器崩溃时,kubelet 只会按 restartPolicy 重启该容器本身,并不会重建整个 Pod,其他容器不受影响。但普通 sidecar 与主容器之间没有启动/退出顺序保证——主容器已退出而 sidecar 仍在跑,在 Job 场景会导致 Pod 迟迟无法结束。K8s 1.33 起 GA 的原生 sidecar(把容器写进 initContainers 并设 restartPolicy: Always)解决了这类问题:sidecar 先于主容器启动、晚于主容器终止。


生命周期与重启策略#

Pod 状态流转#

Pod 从创建到消失,会经历以下状态:

text
创建 Pod
┌──────────┐
│ Pending  │  ← 调度中:API Server 已接收,但还没找到合适的节点
│          │     (可能原因:节点资源不足、PVC 未绑定、拉取镜像中)
└────┬─────┘
     │ 调度成功,容器启动
┌──────────┐
│ Running  │  ← 至少有一个容器在运行
│          │     (注意:Running 不代表应用真的能服务,要看就绪探针)
└────┬─────┘
     │ 所有容器正常退出
┌──────────┐  或  ┌──────────┐
│Succeeded │      │  Failed   │  ← 至少一个容器非正常退出
│          │      │          │     (比如退出码非0、被 OOM Kill)
└──────────┘      └──────────┘

特殊状态:
┌──────────────┐
│ CrashLoop    │  ← 容器反复崩溃,重启间隔指数增长
│ BackOff      │
└──────────────┘

用命令查看状态:

bash
kubectl get pods
# NAME     READY   STATUS    RESTARTS   AGE
# my-app   1/1     Running   0          5m
# my-app   0/1     Pending   0          30s   ← 还在调度
# my-app   0/1     CrashLoopBackOff   3     2m   ← 已重启3次

重启策略(restartPolicy)#

Pod 的 restartPolicy 决定容器退出后 K8s 是否重启它。

yaml
spec:
  restartPolicy: Always   # 默认值
策略行为适用场景
Always(默认)不管退出码是什么,都重启Deployment 管理的 Pod(期望始终在线)
OnFailure只有当容器非正常退出(退出码非0)时才重启Job(批处理任务,失败才重试)
Never不管什么情况都不重启一次性任务,退出后保留容器方便查看日志
bash
# 查看重启次数
kubectl get pod my-app
# RESTARTS 列显示重启次数
# 如果这个数字一直在涨,说明容器在不断崩溃

# 查看上次崩溃原因
kubectl describe pod my-app
# Events 里会有 Back-off restarting failed container

# 查看上次崩溃的日志(最有用)
kubectl logs my-app --previous

注意事项restartPolicy 只对 Pod 里的容器生效,Pod 本身不会被重启(Pod 是调度单元,不是进程)。Pod 里的容器重启,Pod 不重建。


Init Container#

有些初始化工作必须在主容器启动之前完成:比如等数据库就绪、下载配置文件、数据库 schema 迁移。

Init Container 就是做这件事的:在 containers 之前按顺序执行,全部成功完成后,才启动主容器。任何一个 Init Container 失败,Pod 就会重启(按重启策略)。

示例:等数据库就绪再启动主容器#

yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
  labels:
    app: my-app
spec:
  # Init Container:按顺序执行,全部成功后才启动主容器
  initContainers:
  # Init 容器 1:等待数据库就绪
  - name: wait-for-db
    image: postgres:15-alpine
    # 用 pg_isready 检查数据库是否可接受连接
    command:
    - sh
    - -c
    - |
      until pg_isready -h db-service -p 5432; do
        echo "等待数据库就绪..."
        sleep 2
      done
      echo "数据库已就绪!"

  # Init 容器 2:数据库 schema 迁移(在数据库就绪后执行)
  - name: db-migrate
    image: myapp:1.0.0
    command:
    - python
    - manage.py
    - migrate
    env:
    - name: DB_HOST
      value: "db-service"

  # 主容器(Init Container 全部成功后才启动)
  containers:
  - name: app
    image: myapp:1.0.0
    ports:
    - containerPort: 8080
    env:
    - name: DB_HOST
      value: "db-service"

执行顺序:

  1. wait-for-db 运行 → 成功 → 进入下一步
  2. db-migrate 运行 → 成功 → 进入下一步
  3. app 启动 → 正常运行

如果 wait-for-db 一直等不到数据库,它会一直重试,Pod 状态保持 Init:0/2,主容器不会被启动。

Init Container 的特点#

  • 按顺序执行:Init Container 列表里的容器按顺序一个一个执行,前一个成功后才执行下一个
  • 有自己独立的镜像:Init Container 可以用和主容器不同的镜像(比如上面的例子里用 postgres:alpine 做数据库检查)
  • 重启策略:Init Container 失败会触发 Pod 重启(按 restartPolicy),所以 CrashLoopBackOff 也可能是 Init Container 导致的
  • 资源共享:Init Container 和主容器共享同一个网络命名空间(IP),但不共享 emptyDir 卷(除非显式配置)
bash
# 查看 Init Container 状态
kubectl get pod app-with-init
# NAME           READY   STATUS    RESTARTS   AGE
# app-with-init  0/1     Init:0/2  0          30s   ← 0/2 表示 2 个 Init Container 还没跑完

# 查看 Init Container 日志(指定容器名)
kubectl logs app-with-init -c wait-for-db

# describe 里会显示每个 Init Container 的状态
kubectl describe pod app-with-init

小结#

本章核心要点:

  • Pod 是什么:K8s 最小调度单元,确保多个容器共享网络和存储、一起被调度到同一节点。类比:Pod=宿舍,容器=室友。
  • Pod YAML 结构apiVersion/kind/metadata/spec 四大部分。metadata.labels 是 K8s 里关联资源的核心机制,spec.containers 定义容器列表。
  • 多容器 Pod:sidecar 模式最常见——主容器管业务,sidecar 管辅助功能(日志采集、服务网格)。通过共享卷或 localhost 通信。
  • 生命周期Pending(调度中)→ Running(运行中)→ Succeeded/Failed(结束)。CrashLoopBackOff 表示容器反复崩溃。restartPolicy 控制重启行为:Always(默认)、OnFailureNever
  • Init Container:在主容器启动前按顺序执行初始化任务(等依赖就绪、做 schema 迁移)。全部成功后才启动主容器。排查 Init:0/N 状态时用 kubectl logs -c <init-container-name> 查看 Init Container 日志。