2.3. Pod 与容器
Pod 是 Kubernetes 最小的调度单元,但它不是「一个容器」的同义词。共享网络、共享存储、共同生命周期——理解这三件事,才能写出正确的多容器应用和探针。
- 层级:
L2 · 核心
- 难度:
基础到进阶
- 预计:
60 分钟
- 前置:
架构与对象模型
2.3.1. 你将学到
Pod 的生命周期、容器状态与重启策略
Init Container、Sidecar、Ambassador、Adapter 四种模式
liveness / readiness / startup 探针的区别与常见误用
requests / limits 与 QoS 等级的关系
securityContext 的最小权限写法
用生命周期钩子、hostNetwork/hostPort 与 sysctls 处理特殊场景
用
describe、logs、debug定位 Pod 故障
2.3.2. Pod 到底是什么
Pod 是一个或多个容器的逻辑分组,组内容器共享:
网络命名空间:同一个 Pod IP,容器之间用
localhost通信;存储卷:同一份 volume 可以挂到多个容器;
生命周期:Pod 被删除时,所有容器一起终止。
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: demoapp
spec:
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
kubectl apply -f pod-basic.yaml
kubectl get pod pod-demo -o wide
kubectl describe pod pod-demo
预期:
NAME READY STATUS RESTARTS AGE IP NODE
pod-demo 1/1 Running 0 12s 10.244.1.15 k8s-node01
备注
自主式 Pod 不会被重建
直接创建的 Pod 没有控制器管理。节点故障或 Pod 被删除后,它不会自动回来。生产环境应使用 Deployment / StatefulSet / DaemonSet 管理 Pod。
2.3.3. 生命周期
2.3.3.1. Pod 阶段
阶段 |
含义 |
常见原因 |
|---|---|---|
|
已接受,但尚未运行 |
调度失败、拉镜像、挂卷 |
|
至少一个容器在运行 |
正常 |
|
所有容器成功退出 |
Job 完成 |
|
至少一个容器失败退出 |
应用崩溃、OOM |
|
无法获取状态 |
节点失联 |
2.3.3.2. 容器状态
状态 |
含义 |
排查 |
|---|---|---|
|
尚未启动 |
|
|
正在运行 |
看 readiness 是否通过 |
|
已退出 |
看 |
2.3.3.3. 重启策略
策略 |
行为 |
适用 |
|---|---|---|
|
容器退出就重启 |
长期服务(默认) |
|
非 0 退出才重启 |
Job |
|
不重启 |
一次性任务 |
重启策略作用于 Pod 内的容器,不是 Pod 本身。Deployment 的 Pod 被删除后由 ReplicaSet 重建,这是另一层机制。
图:Pod 阶段与容器重启的关系。
CrashLoopBackOff不是 Pod 阶段,而是容器 Waiting 的一种原因。
2.3.4. 多容器模式
2.3.4.1. Init Container
在主容器启动前运行,必须成功结束。适合做初始化:等依赖、拉配置、改权限、建目录。
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command:
- sh
- -c
- |
until nc -z mysql 3306; do
echo "waiting for mysql..."
sleep 2
done
containers:
- name: app
image: ikubernetes/demoapp:v1.0
Init Container 的特点:
按顺序执行,前一个成功才执行下一个;
失败时按
restartPolicy重试;不参与 readiness,不会暴露端口;
所有 Init Container 完成后,Pod 才进入
Initialized。
2.3.4.2. Sidecar
与主容器并行运行,扩展主容器的能力,例如日志收集、代理、证书刷新。Sidecar 共享网络和卷,所以常通过 localhost 与主容器通信。
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
containers:
- name: app
image: ikubernetes/demoapp:v1.0
env:
- name: HOST
value: "127.0.0.1"
- name: PORT
value: "8080"
- name: proxy
image: envoyproxy/envoy-alpine:v1.14.1
args:
- -c
- /etc/envoy/envoy.yaml
volumeMounts:
- name: envoy-config
mountPath: /etc/envoy
volumes:
- name: envoy-config
configMap:
name: envoy-config
小技巧
原生 Sidecar
Kubernetes 1.29+ 支持把 Init Container 的 restartPolicy 设为 Always,让它以 Sidecar 方式运行,并在主容器退出时自动终止。比传统 Sidecar 更可控,但需要确认集群版本与特性门控。
2.3.4.3. Ambassador 与 Adapter
模式 |
作用 |
例子 |
|---|---|---|
Ambassador |
代理外部访问,主容器只连 localhost |
本地 Envoy / HAProxy |
Adapter |
转换主容器的输出格式 |
把应用指标转成 Prometheus 格式 |
Sidecar |
扩展能力,与主容器并肩运行 |
日志收集、TLS 证书刷新 |
2.3.5. 探针
探针决定「容器是否健康」以及「是否接收流量」。三种探针的语义完全不同:
探针 |
失败后果 |
用途 |
|---|---|---|
|
重启容器 |
进程死锁、无法自愈 |
|
从 Service Endpoints 摘除 |
启动未完成、依赖不可用 |
|
重启容器(启动阶段) |
启动很慢的应用 |
apiVersion: v1
kind: Pod
metadata:
name: probe-demo
spec:
containers:
- name: app
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
startupProbe:
httpGet:
path: /livez
port: 80
failureThreshold: 30
periodSeconds: 2
livenessProbe:
httpGet:
path: /livez
port: 80
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 80
periodSeconds: 5
failureThreshold: 3
警告
最常见的探针错误
把 livenessProbe 指向一个依赖外部服务的接口。数据库一抖动,所有 Pod 一起被重启,反而放大故障。Liveness 只应判断「进程是否还能自愈」,依赖健康度交给 readiness。
探针支持的三种方式:
httpGet:返回 200–399 视为成功;tcpSocket:端口能建立连接;exec:命令退出码为 0。
调试探针失败:
kubectl describe pod probe-demo | sed -n '/Events/,$p'
kubectl exec probe-demo -- wget -qO- http://localhost/livez
2.3.6. 资源与 QoS
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
配置 |
QoS 等级 |
特点 |
|---|---|---|
所有容器都设置 requests=limits(CPU/内存) |
Guaranteed |
最稳定,最后被驱逐 |
部分设置 requests/limits |
Burstable |
常见,按 requests 调度 |
完全不设置 |
BestEffort |
最先被驱逐 |
关键事实:
requests 影响调度:节点没有足够可分配资源时,Pod 会
Pending;limits 影响运行:CPU 超限被限流(throttle),内存超限被 OOMKilled;
CPU 单位:
100m= 0.1 核;内存单位:Mi/Gi是二进制,M/G是十进制。
查看资源使用:
kubectl top pod -n demo
kubectl describe node k8s-node01 | sed -n '/Allocated resources/,/Events/p'
2.3.7. 安全上下文
最小权限的容器应默认满足:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: ikubernetes/demoapp:v1.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
注意:
readOnlyRootFilesystem: true时,应用需要写的目录要挂emptyDir;runAsNonRoot要求镜像内有非 root 用户,否则容器起不来;不要用
privileged: true解决权限问题,优先用CAP_NET_BIND_SERVICE等细粒度能力;Pod 级
securityContext与容器级是合并关系,容器级可覆盖。
2.3.8. 命令与参数
字段 |
作用 |
Docker 对照 |
|---|---|---|
|
覆盖镜像 ENTRYPOINT |
|
|
覆盖镜像 CMD |
命令行参数 |
|
注入环境变量 |
|
|
从 ConfigMap/Secret 批量注入 |
— |
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
command: ["/bin/sh", "-c"]
args: ["echo start; exec /usr/local/bin/demoapp"]
env:
- name: APP_MODE
value: "prod"
用 command 时记得 exec,否则 PID 1 是 shell,信号无法正确传递给应用,优雅终止会失败。
2.3.9. 生命周期钩子与节点级网络
2.3.9.1. postStart 与 preStop
容器除了探针,还有两个生命周期钩子:
钩子 |
执行时机 |
常见用途 |
|---|---|---|
|
容器创建后、主进程启动前并行触发 |
初始化文件、注册到外部系统 |
|
容器收到 TERM 之前同步执行 |
摘流量、等待连接排空、保存状态 |
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
terminationGracePeriodSeconds: 30
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo started > /tmp/started"]
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
preStop 在容器收到 SIGTERM 之前执行,但总时长受 terminationGracePeriodSeconds 限制。超时后 kubelet 发送 SIGKILL。如果应用不能快速优雅退出,preStop 里不要 sleep 太久,否则会拖慢滚动发布。
2.3.9.2. hostNetwork、hostPort 与 DNS
hostNetwork: true:Pod 直接使用节点网络命名空间,Pod IP 就是节点 IP。适合网络代理、监控采集等系统组件,不适合普通业务。hostPort:把容器端口映射到节点端口,依赖 CNI 的 portmap 能力;同一节点上端口不能冲突。它与hostNetwork不要同时使用,否则端口语义会重叠。dnsPolicy:hostNetworkPod 默认可能使用Default,需要访问集群 Service 时显式设置ClusterFirstWithHostNet。
apiVersion: v1
kind: Pod
metadata:
name: host-network-demo
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
ports:
- containerPort: 80
2.3.9.3. sysctls:安全与不安全参数
securityContext.sysctls 可以设置内核参数,但只有部分参数被视为 safe,例如 net.ipv4.ip_unprivileged_port_start;修改 net.core.somaxconn 等参数需要 kubelet 显式允许,且通常要求 privileged。
apiVersion: v1
kind: Pod
metadata:
name: sysctls-demo
spec:
securityContext:
sysctls:
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
containers:
- name: demo
image: ikubernetes/demoapp:v1.0
警告
不要用 hostNetwork 绕过网络策略
hostNetwork Pod 不经过 Pod 网络,NetworkPolicy 对它的约束方式与普通 Pod 不同。生产环境只在 CNI、日志、监控等基础组件中使用,并单独做节点级隔离。
对应原书第 4 章的 lifecycle-demo.yaml、pod-using-hostnetwork.yaml、pod-using-hostport.yaml 与 securitycontext-sysctls-demo.yaml。
2.3.10. 实验:制造一次探针故障
2.3.10.1. 1. 部署一个 readiness 永远失败的 Pod
apiVersion: v1
kind: Pod
metadata:
name: bad-readiness
labels:
app: bad
spec:
containers:
- name: app
image: ikubernetes/demoapp:v1.0
readinessProbe:
httpGet:
path: /not-exist
port: 80
periodSeconds: 3
kubectl apply -f bad-readiness.yaml
kubectl get pod bad-readiness -w
预期 READY 一直是 0/1,但 STATUS 是 Running。这说明容器活着,只是不接收流量。
kubectl describe pod bad-readiness | grep -A3 -i readiness
2.3.10.2. 2. 用 Service 验证流量被摘除
kubectl expose pod bad-readiness --name=bad-svc --port=80
kubectl get endpoints bad-svc
预期 Endpoints 为空(或没有 bad-readiness 的 IP)。
2.3.10.3. 3. 制造 OOM
apiVersion: v1
kind: Pod
metadata:
name: oom-demo
spec:
containers:
- name: stress
image: polinux/stress:1.0.4
command: ["stress"]
args: ["--vm", "1", "--vm-bytes", "200M", "--vm-hang", "1"]
resources:
limits:
memory: 64Mi
kubectl apply -f oom-demo.yaml
kubectl get pod oom-demo -w
kubectl describe pod oom-demo | grep -E 'OOMKilled|Reason|Exit Code'
预期容器反复重启,Last State 显示 OOMKilled,退出码 137。
2.3.10.4. 4. 用临时容器调试
kubectl debug -it bad-readiness --image=busybox:1.36 --target=app -- sh
在临时容器里可以共享进程命名空间(--target),用 wget、netstat 等工具排查,而不用改应用镜像。
2.3.11. 排错表
现象 |
原因 |
处理 |
|---|---|---|
|
资源不足 / 污点 / 未绑定 PVC |
|
|
镜像名错、私有仓库无凭据 |
|
|
应用启动失败 |
|
|
readiness 失败 |
进容器访问探针路径 |
反复重启,退出码 137 |
OOMKilled |
提高 memory limit 或查内存泄漏 |
|
ConfigMap/Secret 缺失 |
|
|
PVC / Secret 挂载失败 |
|
容器启动即退出,退出码 0 |
命令执行完就结束 |
用长期运行的前台进程 |
|
finalizer / 优雅终止超时 |
|
2.3.12. 进阶挑战
给一个慢启动应用配置
startupProbe,观察启动阶段 liveness 是否被禁用。用
emptyDir在两个容器之间共享文件,验证 Sidecar 能读到主容器的日志。把
readOnlyRootFilesystem打开,修复应用因无法写临时目录而启动失败的问题。用
kubectl get pod -o jsonpath提取所有容器的restartCount和lastState。对比
Guaranteed与BestEffortPod 在节点内存压力下的驱逐顺序。
2.3.13. 小结
Pod 共享网络、存储和生命周期;多容器是模式,不是目的。
Init 负责「启动前」,Sidecar 负责「运行中」,探针负责「是否健康」。
requests 决定调度,limits 决定运行边界;QoS 影响驱逐顺序。
排错从
describe的 Events 开始,再看logs,最后才进容器。生命周期钩子要配合优雅终止窗口;hostNetwork 只用于基础组件。
2.3.14. 延伸阅读
原仓库示例:examples/pods
下一章:工作负载