这一章我们把 Pod 拆开讲清楚:它为什么是最小调度单元、多个容器如何协作、Init Container 与 Sidecar 分别解决什么问题、Probe 为什么会直接影响流量和重启,以及常见状态与重启策略到底怎么看。::cite[372]::cite[373]::cite[505]
2.1.1 Pod 的本质与设计初衷
官方对 Pod 的定义很直接:Pod 是 Kubernetes 中可以创建和管理的最小可部署计算单元。一个 Pod 可以包含一个或多个容器,这些容器会被 共同调度到同一台节点,并共享网络与存储上下文。::cite[372]
你可以把 Pod 理解成“逻辑主机”。在传统虚拟机里,多个进程共享同一个网络栈、同一个磁盘空间;在 Kubernetes 里,Pod 就承担了这个“共同宿主”的角色。容器本身仍然是隔离的,但它们可以通过 localhost 通信、共享卷中的文件,并被整体地创建、运行、迁移与销毁。::cite[372]
Pod 的设计初衷主要有三点:
- 把需要紧密协作的容器放在一个调度单元里。
- 让这些容器天然共享网络和存储,而不是通过复杂的跨节点访问来拼接。
- 让控制器(如 Deployment)以 Pod 为粒度进行副本管理和故障修复。::cite[372]
为什么不直接把每个容器都独立调度?
因为现实中的应用并不总是单进程模型。很多场景里,主业务进程之外还需要一个辅助进程,例如:
- 日志采集器
- 配置渲染器
- 本地代理
- 证书刷新器
- 数据同步程序
如果这些辅助进程必须和业务容器共享本地文件或通过 localhost 协作,把它们放到同一个 Pod 会简单很多。::cite[372]::cite[505]
2.1.2 Pod 中的一个或多个容器如何协作
Pod 中的容器有两个最核心的共享能力:
| 共享项 | 说明 |
|---|---|
| 网络命名空间 | 同一个 Pod 内的容器共享 IP 和端口空间,可以直接通过 localhost 通信 |
| 存储卷 | 多个容器可以把同一个 Volume 挂载到不同路径,实现文件共享 |
这也是为什么“多容器 Pod”通常用于 紧耦合协作,而不是把无关服务硬塞进一个 Pod。::cite[372]
单容器 Pod:最常见形态
大多数业务场景下,一个 Pod 只运行一个主容器。即使如此,你仍然应该把这个容器当作 Pod 的一部分去理解,而不是把 Pod 等同于容器。
多容器 Pod:典型协作方式
常见的协作模式包括:
-
主容器 + Sidecar 容器
- 主容器处理业务。
- Sidecar 负责日志转发、代理、监控增强等辅助功能。
-
主容器 + 本地代理
- 例如应用容器通过
localhost调用同 Pod 内的认证代理或缓存代理。
- 例如应用容器通过
-
主容器 + 文件生产者/消费者
- 一个容器写入共享卷,另一个容器读取共享卷中的文件。::cite[372]::cite[505]
一个简单的双容器协作示意
下面的例子中,主容器持续写日志到共享卷,辅助容器持续读取同一份日志:
apiVersion: v1
kind: Pod
metadata:
name: two-containers-demo
labels:
app: two-containers-demo
spec:
volumes:
- name: shared-data
emptyDir: {}
containers:
- name: app
image: busybox:1.36
command:
- /bin/sh
- -c
- |
i=0
while true; do
echo "$(date '+%F %T') app log line ${i}" >> /data/app.log
i=$((i+1))
sleep 5
done
volumeMounts:
- name: shared-data
mountPath: /data
- name: tailer
image: busybox:1.36
command:
- /bin/sh
- -c
- |
touch /data/app.log
tail -f /data/app.log
volumeMounts:
- name: shared-data
mountPath: /data
实操步骤
- 创建 Pod。
kubectl apply -f two-containers-demo.yaml
- 查看 Pod 状态。
kubectl get pod two-containers-demo -o wide
- 查看辅助容器输出。
kubectl logs two-containers-demo -c tailer
- 进入主容器确认共享卷文件。
kubectl exec -it two-containers-demo -c app -- sh
观察重点:两个容器虽然进程隔离,但通过
emptyDir卷共享了文件数据;同时它们也共享同一个 Pod 网络。::cite[372]
2.1.3 Init Container、Sidecar 与生命周期钩子
这一部分是 Pod 真正“像工程系统”的地方。很多初始化逻辑、依赖等待、优雅下线控制,都是靠这里完成的。
1)Init Container:先做准备,再启动业务
Init Container 是一种先于应用容器运行的特殊容器。它们按顺序执行,必须前一个成功完成,后一个才会开始,直到全部成功后,主业务容器才会启动。::cite[515]
典型用途:
- 等待依赖服务就绪
- 初始化目录与权限
- 生成配置文件
- 执行数据库迁移前置检查
Init Container 很适合做“一次性准备工作”,不适合长期驻留。::cite[515]
2)Sidecar:陪跑型辅助容器
在 Kubernetes v1.32 中,原生 Sidecar 容器能力已经可用。它的实现方式是:在 initContainers 中定义一个 restartPolicy: Always 的容器。这样它既保留了初始化顺序控制,又能在 Pod 生命周期内持续运行。::cite[505]
Sidecar 的关键特点:
- 与主容器同 Pod 运行,天然共享网络和卷。
- 可以独立重启,而不影响主应用容器。
- 在 Pod 终止时,Kubernetes 会先等主应用容器结束,再按相反顺序关闭 Sidecar。::cite[505]
3)生命周期钩子:在关键节点插入动作
Kubernetes 提供两种常用的容器生命周期钩子:
| 钩子 | 触发时机 | 典型用途 |
|---|---|---|
postStart |
容器启动后 | 初始化注册、生成运行时文件 |
preStop |
容器终止前 | 优雅下线、摘流、刷新缓冲 |
需要注意:postStart 并不保证先于容器主进程执行完;而 preStop 会在终止流程里先执行,如果执行时间较长,你需要相应增大 terminationGracePeriodSeconds。::cite[519]::cite[373]
综合示例:Init + Sidecar + 生命周期钩子
下面是一个完整示例,包含:
- 一个普通 Init Container:准备初始页面
- 一个原生 Sidecar:持续跟踪共享目录内容
- 一个主容器:运行 Nginx
postStart/preStop生命周期钩子- 三类 Probe
apiVersion: v1
kind: Pod
metadata:
name: pod-lifecycle-demo
labels:
app: pod-lifecycle-demo
spec:
restartPolicy: Always
terminationGracePeriodSeconds: 30
volumes:
- name: workdir
emptyDir: {}
initContainers:
- name: init-content
image: busybox:1.36
command:
- /bin/sh
- -c
- |
mkdir -p /work
echo 'hello from init container' > /work/index.html
volumeMounts:
- name: workdir
mountPath: /work
- name: file-watcher-sidecar
image: busybox:1.36
restartPolicy: Always
command:
- /bin/sh
- -c
- |
touch /work/index.html
tail -f /work/index.html
volumeMounts:
- name: workdir
mountPath: /work
startupProbe:
exec:
command:
- /bin/sh
- -c
- test -f /work/index.html
failureThreshold: 30
periodSeconds: 2
readinessProbe:
exec:
command:
- /bin/sh
- -c
- test -f /work/index.html
periodSeconds: 5
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
volumeMounts:
- name: workdir
mountPath: /usr/share/nginx/html
lifecycle:
postStart:
exec:
command:
- /bin/sh
- -c
- echo 'postStart executed' >> /usr/share/nginx/html/index.html
preStop:
exec:
command:
- /bin/sh
- -c
- sleep 10
startupProbe:
httpGet:
path: /
port: 80
failureThreshold: 30
periodSeconds: 2
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
实操步骤
- 创建 Pod。
kubectl apply -f pod-lifecycle-demo.yaml
- 查看 Init Container 与主容器状态。
kubectl describe pod pod-lifecycle-demo
- 查看 Sidecar 输出。
kubectl logs pod-lifecycle-demo -c file-watcher-sidecar
- 验证页面内容。
kubectl exec -it pod-lifecycle-demo -c nginx -- cat /usr/share/nginx/html/index.html
- 删除 Pod,观察
preStop带来的优雅终止。
kubectl delete pod pod-lifecycle-demo
2.1.4 Probe 机制:Liveness、Readiness、Startup
Probe 是 kubelet 定期对容器做的诊断检查。它可以通过 exec、httpGet、tcpSocket、grpc 等方式执行。检查结果有三种:Success、Failure、Unknown。::cite[373]
很多线上问题都和 Probe 配置不合理有关。一个经验法则是:Probe 不是“有没有配”,而是“配得对不对”。
三种 Probe 的职责分工
| Probe 类型 | 关注点 | 失败后会怎样 | 最典型的使用场景 |
|---|---|---|---|
livenessProbe |
容器是否还活着 | kubelet 杀掉容器并按重启策略处理 | 进程死锁、线程卡死、内部状态不可恢复 |
readinessProbe |
容器是否准备好接流量 | Pod 会从 Service 后端地址中移除 | 应用启动中、依赖未就绪、临时摘流 |
startupProbe |
容器是否完成启动 | 失败会触发重启;成功前禁用其他 Probe | 启动慢、预热时间长、需要加载大配置 |
官方文档明确说明:如果配置了 startupProbe,那么在它成功之前,livenessProbe 和 readinessProbe 都不会生效。这样做是为了避免“应用其实还在启动中,却被 liveness 误杀”。::cite[373]
常见配置思路
场景 1:普通 Web 应用
readinessProbe检查/readylivenessProbe检查/healthz- 通常不一定需要
startupProbe
场景 2:启动慢的 Java / AI / 大模型推理服务
startupProbe给足预热时间readinessProbe在依赖准备完后再放流量livenessProbe仅负责发现真正的死锁或卡死
场景 3:需要优雅摘流
- 删除 Pod 时,Kubernetes 会先把 Pod 变为不 Ready,再执行终止流程
- 如果还需要额外清理,可配合
preStop完成优雅关闭。::cite[373]
Probe 配置误区
- 把 liveness 和 readiness 检查写成完全一样:容易导致暂时不可用也被误判成要重启。
- 启动慢却没有 startupProbe:应用还没启动完就被 liveness 打死,形成
CrashLoopBackOff。 - 检查逻辑太重:特别是
exec探针,会带来额外进程创建开销。::cite[373]
2.1.5 Pod 常见状态与重启策略
Pod Phase:高层生命周期摘要
Pod 的 phase 是一个高层摘要,不等于你在 kubectl get pod 里看到的所有展示状态。官方定义的常见 phase 如下:::cite[373]
| Phase | 含义 |
|---|---|
Pending |
Pod 已被接受,但还没完成调度、拉镜像或容器准备 |
Running |
Pod 已绑定节点,且至少有一个容器在运行或启动中 |
Succeeded |
所有容器成功退出,且不再重启 |
Failed |
所有容器都已退出,且至少有一个失败退出 |
Unknown |
无法获取 Pod 状态,通常与节点通信异常有关 |
注意:CrashLoopBackOff 不是 Pod Phase
这是一个非常容易混淆的点。
CrashLoopBackOff 是 kubectl 展示给你的用户友好状态,不是 Pod API 中定义的 phase。它表示容器反复失败、并进入指数退避重启。::cite[373]
容器状态 vs Pod 状态
容器级别还有三种常见状态:
| 容器状态 | 含义 |
|---|---|
Waiting |
容器还在等待启动,如拉镜像、挂载 Secret |
Running |
容器已运行 |
Terminated |
容器已退出,可能成功也可能失败 |
定位问题时,要同时看:
kubectl get podkubectl describe podkubectl logs
restartPolicy:容器退出后的处理方式
Pod 级别的 restartPolicy 取值有三种:
| 策略 | 含义 | 常见场景 |
|---|---|---|
Always |
任何退出都自动重启 | 长期运行服务,默认值 |
OnFailure |
仅异常退出时重启 | 批处理任务、一次性任务 |
Never |
退出后不重启 | 手动调试、只跑一次的任务 |
需要注意两点:
- Pod 级别
restartPolicy适用于应用容器和普通 Init Container。 - 原生 Sidecar 容器会忽略 Pod 级别的这个字段,因为它自己在
initContainers中通过restartPolicy: Always定义行为。::cite[373]::cite[505]
当容器连续崩溃时,kubelet 会采用指数退避方式重启:10 秒、20 秒、40 秒……上限 300 秒;如果容器稳定运行约 10 分钟后再失败,这个退避计时会被重置。::cite[373]
2.1.6 实战排错清单
当一个 Pod 启不来时,建议按这个顺序排查:
- 看整体状态。
kubectl get pod
- 看详细事件。
kubectl describe pod <pod-name>
- 看容器日志。
kubectl logs <pod-name> -c <container-name>
- 如果是多容器 Pod,逐个容器看日志。
kubectl logs <pod-name> -c <sidecar-name>
- 如果怀疑 Probe 配置问题,重点检查:
- 端口是否正确
- 路径是否正确
- 应用是否真的在那个时间点可用
initialDelaySeconds/failureThreshold/periodSeconds是否合理
2.1.7 本章小结
这一章最重要的结论有四个:
- Pod 不是容器,而是最小调度单元。
- 一个 Pod 内多个容器共享网络和卷,适合紧耦合协作。
- Init Container 负责启动前准备,Sidecar 负责运行期辅助,生命周期钩子负责关键时机动作。
- Probe 与重启策略直接决定服务稳定性,配置错误会制造大量伪故障。
真正写业务 YAML 时,建议优先保持 Pod 简单:
- 单容器优先
- 多容器只在确有强协作关系时使用
- Probe 配置从保守开始
- 对终止流程有要求时,一定明确
preStop和terminationGracePeriodSeconds
📝 版权声明:本文为原创技术博客,转载请注明出处。
如文章中存在错误或不准确之处,欢迎在评论区指正,感谢您的阅读与支持!