本章目录(点击跳转):
- 3.1 Pod 到底是什么
- 3.1.1 一个 Pod 的解剖
- 3.2 Pod 的完整生命周期状态
- 3.2.1 动手实验:看崩溃的 Pod 长什么样
- 3.3 多容器 Pod 与边车
- 3.4 Init 容器:在主容器之前跑的"准备容器"
- 3.5 探针(Probes):K8s 的"健康监测"
- 3.5.1 三种探针的 YAML 写法
- 3.5.2 探针参数详解(常考常错)
- 3.5.3 动手实验:观察 readiness 摘流量
- 3.6 资源限制:requests 与 limits
- 3.7 环境与命令:给容器"下指令"
- 3.7.1 工作目录与端口
- 3.8 Pod 排障工具箱
- 3.9 本章小结
本章目标:彻底理解 Pod——它是什么、有哪些状态、怎么定义多容器/Init 容器/探针,以及 Pod 出问题怎么排查。
3.1 Pod 到底是什么
很多小白把 Pod 理解成"容器",这是最常见的误区。准确说法是:
Pod 是一组(通常 1 个)共享网络、存储等资源的容器的组合,是 K8s 调度的最小单位。
"共享"意味着什么?
- 同一个 Pod 里的容器共享 IP 地址:互相访问用
localhost。 - 同一个 Pod 里的容器可以共享 卷(目录)。
- 它们被一起调度到同一个节点,一起创建、一起销毁。
什么场景需要一个 Pod 里放多个容器?典型例子是"边车模式(sidecar)":
┌────────────────── Pod ──────────────────┐
│ [nginx 主容器] ←共享IP/卷→ [日志收集容器] │
└──────────────────────────────────────────┘
主容器跑业务,边车容器把日志文件转发到远端,业务代码完全不用改。
3.1.1 一个 Pod 的解剖
apiVersion: v1
kind: Pod
metadata:
name: web-pod # 名字(命名空间内唯一)
labels: # 标签:K8s 里最重要的"筛选器"
app: web
env: dev
annotations:
note: "for demo" # 注解:给人看的元数据,不参与筛选
spec: # 期望状态
containers: # 主容器列表(必填)
- name: web
image: nginx:1.27
ports:
- containerPort: 80
关键心智模型:**metadata.labels 是 K8s 世界的"索引"**。Service 靠标签找 Pod,kubectl 靠标签筛 Pod,几乎所有"分组"操作都基于标签。
3.2 Pod 的完整生命周期状态
看 kubectl get pods 的 STATUS 列,小白必须认识这些状态:
| 状态 | 含义 | 正常吗 |
|---|---|---|
Pending |
还没被分配到节点(调度中/资源不够/PVC 没绑定) | 短暂出现正常,一直卡住就不正常 |
Running |
已分配到节点,容器在运行 | ✅ |
Succeeded |
所有容器成功退出(Job 类) | ✅ |
Failed |
所有容器异常退出 | ❌ |
CrashLoopBackOff |
容器反复崩溃重启,K8s 在退避等待 | ❌ 看日志! |
ImagePullBackOff / ErrImagePull |
镜像拉取失败 | ❌ 检查镜像名和网络 |
Evicted |
节点资源不足被驱逐 | 见 3.6 |
Terminating |
正在删除,卡住说明有异常 | 需要排查 |
容器级别还有更细的状态(kubectl describe pod 里看):Waiting(等待中,带 reason)、Running、Terminated(已退出,带 exit code)。
3.2.1 动手实验:看崩溃的 Pod 长什么样
# crash-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: crash-demo
spec:
restartPolicy: Always
containers:
- name: crash
image: busybox:1.36
command: ["sh", "-c", "echo hello; exit 1"]
kubectl apply -f crash-demo.yaml
kubectl get pods -w
# 观察:容器不断重启,RESTARTS 递增,最终状态 CrashLoopBackOff
kubectl logs crash-demo -p # -p 看上一次(已崩溃的)日志
kubectl describe pod crash-demo # 看 Events 段的重启原因
kubectl delete -f crash-demo.yaml
3.3 多容器 Pod 与边车
完整示例:nginx 主容器 + 一个持续输出时间戳的"日志边车",共享一个目录:
# sidecar-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
labels:
app: sidecar-demo
spec:
containers:
- name: main
image: nginx:1.27
ports:
- containerPort: 80
volumeMounts:
- name: shared-log
mountPath: /var/log/app
- name: logger
image: busybox:1.36
command: ["sh", "-c", "while true; do date >> /var/log/app/access.log; sleep 1; done"]
volumeMounts:
- name: shared-log
mountPath: /var/log/app
volumes:
- name: shared-log
emptyDir: {}
kubectl apply -f sidecar-demo.yaml
# 进主容器看共享目录里有没有日志文件
kubectl exec -it sidecar-demo -c main -- ls -l /var/log/app
# 分别看两个容器的日志
kubectl logs sidecar-demo -c main
kubectl logs sidecar-demo -c logger --tail=5
# 两个容器共享同一个 IP,互相可以用 localhost
kubectl exec -it sidecar-demo -c logger -- wget -qO- localhost
kubectl delete -f sidecar-demo.yaml
3.4 Init 容器:在主容器之前跑的"准备容器"
Init 容器按顺序执行,全部成功后主容器才会启动。常见用途:等数据库就绪、下载配置、初始化数据。
# init-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: wait-db
image: busybox:1.36
command:
- sh
- -c
- |
echo "等待数据库 5 秒(模拟)..."
sleep 5
echo "准备完成"
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "echo 应用启动; sleep 3600"]
kubectl apply -f init-demo.yaml
kubectl logs init-demo -c wait-db # 只有 init 日志
kubectl logs init-demo -c app # init 完成后才有主容器日志
kubectl describe pod init-demo | grep -A5 "Events"
kubectl delete -f init-demo.yaml
⚠️ 注意:init 容器失败了,主容器永远不启动,Pod 状态会显示
Init:0/1。
3.5 探针(Probes):K8s 的"健康监测"
探针是 K8s 判断容器健康的方式,共三种,都可选,都是执行一个检查命令/HTTP/TCP:
| 探针 | 作用 | 失败后果 |
|---|---|---|
| livenessProbe(存活) | 容器是否还活着 | 失败 → 重启容器 |
| readinessProbe(就绪) | 容器能否接流量 | 失败 → 从 Service 摘除,不重启 |
| startupProbe(启动) | 慢启动应用是否启动完成 | 失败且超时 → 杀死容器;成功前屏蔽另两种探针 |
3.5.1 三种探针的 YAML 写法
apiVersion: v1
kind: Pod
metadata:
name: probe-demo
spec:
containers:
- name: app
image: nginx:1.27
ports:
- containerPort: 80
startupProbe: # 应用最慢 30 秒内必须启动成功
httpGet:
path: /
port: 80
failureThreshold: 30 # 允许 30 次失败
periodSeconds: 1
livenessProbe: # 启动后每 10 秒检查一次
httpGet:
path: /
port: 80
periodSeconds: 10
failureThreshold: 3 # 连续 3 次失败就重启
readinessProbe: # 每 5 秒检查一次
httpGet:
path: /
port: 80
periodSeconds: 5
failureThreshold: 2
三种检查方式的写法对比:
# 1) HTTP 检查
httpGet:
path: /healthz
port: 8080
httpHeader:
- name: X-Check
value: "k8s"
# 2) TCP 检查(只要端口通就行)
tcpSocket:
port: 5432
# 3) 命令检查(在容器里执行命令,exit 0 为成功)
exec:
command:
- cat
- /tmp/healthy
3.5.2 探针参数详解(常考常错)
initialDelaySeconds: 5 # 容器启动后等多久开始检查
periodSeconds: 10 # 检查间隔
timeoutSeconds: 2 # 单次检查超时
successThreshold: 1 # 连续成功几次算成功(一般 1)
failureThreshold: 3 # 连续失败几次算失败
小白最常见的坑:
- 应用启动要 30 秒,但
initialDelaySeconds只给了 5 秒 → 被 liveness 反复重启。解决:加startupProbe或调大 initialDelay。 - 把 readiness 配成 liveness:本来是"暂时别给我流量"的事,变成了"反复重启"。
3.5.3 动手实验:观察 readiness 摘流量
# 用第 2 章的 hello-app(2 副本 Deployment + Service)
# 停掉其中一个 Pod 里的 nginx,模拟"不健康"
kubectl exec -it $(kubectl get pod -l app=hello-web -o jsonpath='{.items[0].metadata.name}') -- sh -c "nginx -s quit"
kubectl get endpoints hello-web # 观察:该 Pod IP 从 endpoints 里消失(被摘流量)
# 容器会被重启(没有 liveness 的话就退出,K8s 重启它)
3.6 资源限制:requests 与 limits
每个容器都可以声明资源需求,这直接影响调度和稳定性:
spec:
containers:
- name: app
image: myapp:1.0
resources:
requests: # "我至少需要这么多",调度依据
cpu: 250m # 250m = 0.25 核
memory: 256Mi
limits: # "我最多只能用这么多",超出会被限制/杀死
cpu: 500m # 0.5 核
memory: 512Mi
规则:
- 超过 CPU limit:被"限速"(变慢,不会死)。
- 超过 memory limit:被 OOMKilled(直接杀,
kubectl describe里可见)。
# 查看 Pod 的资源设置
kubectl describe pod mypod | grep -A5 "Limits"
# 事后修改(实际生产建议改 YAML 再 apply)
kubectl set resources deployment web -c web --requests=cpu=250m,memory=256Mi --limits=cpu=500m,memory=512Mi
3.7 环境与命令:给容器"下指令"
容器里跑什么、怎么跑,由这几项控制:
spec:
containers:
- name: app
image: myapp:1.0
command: ["python", "app.py"] # 覆盖镜像默认入口
args: ["--port=9000", "--debug"] # 追加参数
env:
- name: APP_ENV
value: "production" # 直接写值
- name: DB_HOST
valueFrom:
configMapKeyRef: # 从 ConfigMap 读
name: app-config
key: db-host
- name: API_KEY
valueFrom:
secretKeyRef: # 从 Secret 读
name: app-secret
key: api-key
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP # 读 Pod 自身信息
3.7.1 工作目录与端口
workingDir: /app
ports:
- name: http
containerPort: 8080
# 注意:containerPort 只是"声明",不声明也不会拦流量,
# 真正拦流量的是 NetworkPolicy(高级话题)。
3.8 Pod 排障工具箱
遇到 Pod 不正常,按这个顺序排查(全书通用方法论):
# 第 1 步:看状态
kubectl get pods -o wide
# 关注 STATUS 和 RESTARTS
# 第 2 步:看事件(为什么没起来/为什么重启)
kubectl describe pod <pod-name>
# 重点看末尾 Events 段和 Containers 段的 State/Last State
# 第 3 步:看日志
kubectl logs <pod-name> # 当前
kubectl logs <pod-name> -p # 上一次崩溃
kubectl logs <pod-name> -c <容器名> # 多容器
# 第 4 步:进容器(Running 的 Pod 才能 exec)
kubectl exec -it <pod-name> -- sh
# 进去后:ps、ls、cat 配置、ping/telnet 依赖、curl 自身接口
# 第 5 步:起一个"调试 Pod"(原 Pod 起不来时用)
kubectl debug -it <pod-name> --image=busybox:1.36 --target=<容器名>
# 或者起一个共享网络命名空间的调试容器:
kubectl debug <pod-name> --image=busybox:1.36 -- sh
常见症状速查表:
| 症状 | 常见原因 | 第一步 |
|---|---|---|
Pending |
资源不足 / 节点选择器不匹配 / PVC 未绑定 | describe 看 Events |
ImagePullBackOff |
镜像名错 / 网络 / 私有仓库没配凭证 | 手工 docker pull 试一下 |
CrashLoopBackOff |
应用启动就崩 | logs -p 看崩溃日志 |
OOMKilled |
内存超 limit | 看监控,调大 limit 或修内存泄漏 |
Terminating 卡住 |
优雅退出超时 | kubectl delete pod xxx --grace-period=0 --force(谨慎) |
| Running 但访问不通 | 端口配错 / 应用没监听 / readiness 没过 | exec 进容器 curl 自己 |
3.9 本章小结
- Pod ≠ 容器:Pod 是一组共享网络/存储的容器,是调度最小单位。
- 必须认识的状态:Pending、Running、CrashLoopBackOff、ImagePullBackOff、OOMKilled。
- 三类探针:liveness(重启它)、readiness(摘流量)、startup(保护慢启动)。
- requests 决定调度,limits 决定生死(内存)。
- 排障顺序:
get→describe→logs→exec→debug。
下一章:用 Deployment 等工作负载真正管理应用。
原文链接:第3章 Pod 深入,转载请注明来源!
