Pod
Pod 是 Kubernetes 里最小的可调度单元,也是后面所有概念(Deployment、Service、Ingress...)的基础。学不好 Pod,后面的内容都是空中楼阁。本章从"为什么需要 Pod"讲起,覆盖 Pod 的 YAML 结构、多容器模式、生命周期和初始化容器。
Pod 是什么#
为什么 Pod 是最小调度单元#
你可能会想:Docker 里最小单位是容器,K8s 为什么不直接调度容器,而要包一层 Pod?
原因有两个:共享网络 和 共享存储。
有些场景下,多个容器需要:
- 用
localhost互相通信(比如主应用容器 + 本地代理 sidecar) - 共享一块存储卷(比如主应用写日志,sidecar 读日志转发到外部)
如果 K8s 单独调度容器,就没法保证这两个容器一定被调度到同一台机器上——不同机器上的容器不能通过 localhost 通信,共享卷也无法挂载。
Pod 的 solution:把多个容器包装成一个调度单元,K8s 保证 Pod 里的所有容器一定跑在同一台机器的同一个网络命名空间里。
同一个 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,每个字段都标注了含义。
# 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 跑起来试试#
# 应用上面的 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#
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 从创建到消失,会经历以下状态:
创建 Pod
│
▼
┌──────────┐
│ Pending │ ← 调度中:API Server 已接收,但还没找到合适的节点
│ │ (可能原因:节点资源不足、PVC 未绑定、拉取镜像中)
└────┬─────┘
│ 调度成功,容器启动
▼
┌──────────┐
│ Running │ ← 至少有一个容器在运行
│ │ (注意:Running 不代表应用真的能服务,要看就绪探针)
└────┬─────┘
│ 所有容器正常退出
▼
┌──────────┐ 或 ┌──────────┐
│Succeeded │ │ Failed │ ← 至少一个容器非正常退出
│ │ │ │ (比如退出码非0、被 OOM Kill)
└──────────┘ └──────────┘
特殊状态:
┌──────────────┐
│ CrashLoop │ ← 容器反复崩溃,重启间隔指数增长
│ BackOff │
└──────────────┘用命令查看状态:
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 是否重启它。
spec:
restartPolicy: Always # 默认值| 策略 | 行为 | 适用场景 |
|---|---|---|
Always(默认) | 不管退出码是什么,都重启 | Deployment 管理的 Pod(期望始终在线) |
OnFailure | 只有当容器非正常退出(退出码非0)时才重启 | Job(批处理任务,失败才重试) |
Never | 不管什么情况都不重启 | 一次性任务,退出后保留容器方便查看日志 |
# 查看重启次数
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 就会重启(按重启策略)。
示例:等数据库就绪再启动主容器#
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"执行顺序:
wait-for-db运行 → 成功 → 进入下一步db-migrate运行 → 成功 → 进入下一步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卷(除非显式配置)
# 查看 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(默认)、OnFailure、Never。 - Init Container:在主容器启动前按顺序执行初始化任务(等依赖就绪、做 schema 迁移)。全部成功后才启动主容器。排查
Init:0/N状态时用kubectl logs -c <init-container-name>查看 Init Container 日志。