Pod
这一课你将能做到
- 手写一份合法的 Pod YAML 并 apply,用 get/describe/logs 观察它的完整生命周期
- 说清「K8s 调度的是 Pod 而非容器」的原因,并演示多容器 Pod 共享 localhost
- 正确配置 liveness/readiness 探针,亲手制造一次「探针失败 → 容器重启」与「未就绪 → 不接流量」
为什么要学
Pod 是集群里最小调度单元:一组共享网络与存储的容器。生产中你几乎不会手搓裸 Pod,但 Deployment 的 template 字段本质上就是一份 Pod 模板——Pod 不懂,后面每一课都是空中楼阁。
面试必考「Pod 生命周期」「三种探针的区别」。对本站:Spring Boot backend 打进镜像后,靠什么告诉 K8s「我启动好了」「我挂了」?答案就是 Actuator 的 /actuator/health + K8s 探针,这是 backend 能被滚动发布、被自动摘流的前提。
先建立图像
Pod(逻辑主机)
├─ 共享 network namespace → 容器间 127.0.0.1 互访、共享端口空间
├─ 可共享存储卷(volume)
└─ 一起调度、一起生死(通常)
对照 Docker 心智:
docker run 一个容器 ≈ 单容器 Pod
Compose 里多个服务 ≈ 多个 Pod(再靠 Service 串起来)
一个 Pod 塞前后端 + DB ✗ 错误模型——该拆成三个 Pod
生命周期相位(面试要背):
| 阶段 | 含义 | 常见原因 |
|---|
| Pending | 已创建未运行 | 调度不下去 / 拉镜像中 |
| Running | 主进程运行中 | 正常运行 / 即将崩溃 |
| Succeeded | 全部容器成功退出 | Job 跑完 |
| Failed | 容器失败退出 | 应用崩溃 |
| Unknown | 状态失联 | 节点失联 |
跟着做
步骤 1:写最小 Pod 并 apply
pod-hello.yaml:
apiVersion: v1
kind: Pod
metadata:
name: hello
labels:
app: hello
spec:
containers:
- name: web
image: nginx:1.27-alpine
ports:
- containerPort: 80
kubectl apply -f pod-hello.yaml
kubectl get pod hello -o wide
kubectl describe pod hello
注意:containerPort 只是信息性声明,不是 Docker 的 -p,不会把端口映射到宿主机。
步骤 2:观察生命周期与事件
kubectl get pods -w # 另开终端盯着状态变化
状态迁移:Pending(拉镜像/调度)→ ContainerCreating → Running。describe 的 Events 段记录每一步的时间戳——这就是以后排错的主战场。
步骤 3:验证裸 Pod「删了就没了」
kubectl delete pod hello
kubectl get pods
删掉后不会重生——没有任何控制器管它。这就是「生产用 Deployment 管副本」的第一层理由(第二层是滚动更新,下一课)。
步骤 4:liveness 探针实验(制造一次自愈)
apiVersion: v1
kind: Pod
metadata:
name: probe-demo
labels:
app: probe
spec:
containers:
- name: app
image: busybox:1.36
command: ["/bin/sh", "-c", "touch /tmp/healthy; while true; do sleep 30; done"]
livenessProbe:
exec:
command: ["sh", "-c", "test -f /tmp/healthy"]
initialDelaySeconds: 5
periodSeconds: 5
kubectl apply -f pod-probe.yaml
kubectl get pods -w
Ready 之后删掉健康标记文件,模拟进程「僵死」:
kubectl exec probe-demo -- rm /tmp/healthy
约 10-15 秒内观察:探针失败 → 容器被重启 → RESTARTS 列 +1,且新容器里文件重新生成(启动命令会重建它)。这就是 K8s 的「进程级自愈」。
步骤 5:readiness 探针实验(制造一次摘流)
pod-ready.yaml(给 nginx 打一个不存在的路径):
apiVersion: v1
kind: Pod
metadata:
name: ready-demo
spec:
containers:
- name: web
image: nginx:1.27-alpine
readinessProbe:
httpGet:
path: /not-exist
port: 80
periodSeconds: 5
kubectl apply -f pod-ready.yaml
kubectl get pods
看到 READY 0/1 且 STATUS 仍是 Running——进程活着,但不接客。若它属于某个 Service,此刻 Endpoints 里不会有它。把 path 改成 / 再 apply,READY 变 1/1。
这解释了本站 backend 探针为何指向 /actuator/health:Spring 还在初始化(连 MySQL/Redis)时,readiness 失败能挡住流量,避免用户打到「半死」实例。
步骤 6:多容器 Pod 验证共享 localhost
apiVersion: v1
kind: Pod
metadata:
name: two
spec:
containers:
- name: web
image: nginx:1.27-alpine
- name: sidecar
image: busybox:1.36
command: ["/bin/sh", "-c", "sleep 3600"]
kubectl apply -f pod-two.yaml
kubectl exec -it two -c sidecar -- wget -qO- 127.0.0.1:80 | head -3
sidecar 容器里通过 127.0.0.1 访问到了 nginx——同一 Pod 的容器共享网络命名空间。收尾清理:
kubectl delete pod probe-demo ready-demo two
讲清楚要点
- 调度单位是 Pod:同 Pod 容器天然同网络、可共享卷、同调度同生死。sidecar(日志收集、服务网格代理)都建立在这个模型上。
- 裸 Pod 没有自愈与发布能力:删了不重建、改镜像要删了重跑。一切常驻工作负载都应交给 Deployment / StatefulSet / Job 管理。
- restartPolicy 作用于 Pod 内容器:默认 Always(容器退出就重启);Job 场景用 OnFailure / Never。注意它是「重启容器」,不是「重建 Pod」。
- 三种探针分工:liveness 失败 → 重启容器(治僵死);readiness 失败 → 摘出 Service 流量(保护发布与未就绪实例);startup 失败 → 先别启动前两者(给慢启动应用如 JVM 缓冲)。
- 探针过严是事故源:
initialDelaySeconds 不足会把「正在启动」误判为「已僵死」,制造重启风暴。本站 Spring Boot 启动慢,务必给足延迟或用 startupProbe。
- 镜像问题都会显形在 Pod 事件里:imagePullSecrets 缺失、架构不匹配(arm/amd64)、仓库网络不通,报错全在 describe 的 Events 里。
你可能会懵的地方
-
一个 Pod 里跑多个容器 = 多副本吗?
完全不是。多副本 = 多个 Pod(水平扩);同 Pod 多容器 = 强耦合的「主程序 + 帮手」。把前后端塞一个 Pod 是经典错误——它们要独立扩缩与发布。
-
Pod 的 IP 是什么?能直接用吗?
是集群虚拟网络里的地址,Pod 之间可直接互通。但它随重建而变,任何配置里都不要写死 Pod IP,一律用 Service 名。
-
CrashLoopBackOff 怎么定位?
退避重启循环。kubectl logs <pod> --previous 看崩溃现场;常见原因:启动命令错、环境变量缺、连不上 MySQL/Redis、liveness 探针太严。
-
K8s 有没有类似 Compose depends_on 的启动顺序保证?
没有。K8s 的哲学是「不保证顺序,但保证最终可用」:依赖没好时 readiness 失败自动摘流,应用自己重试连接。你的 Spring Boot 应容忍依赖晚到(连接池重试),而不是启动即退出。
-
Pod 和容器的区别,一句话版?
容器是进程隔离技术,Pod 是「一组共生容器的调度与网络封装」——K8s 世界里的「逻辑主机」,容器只是里面跑的进程。
错法对照
| 错法 | 问题 | 正解 |
|---|
| 生产环境手建大量裸 Pod | 无自愈、无发布策略 | 一律交给 Deployment 托管 |
| 只配 liveness 不配 readiness | 启动期流量打到未就绪实例 | 两探针分工配置 |
| 探针路径写错(打 404) | 被重启/摘流,看起来像故障 | 先 curl 通再写进 YAML |
| 一个 Pod 塞 frontend+backend+DB | 无法独立扩缩,一损俱损 | 按服务拆 Pod |
| 不设 initialDelaySeconds | 慢启动应用被误杀重启 | 给足延迟或用 startupProbe |
卡点急救
- ImagePullBackOff:describe 看 Events——镜像名拼写、tag 是否存在、私有仓库凭证(imagePullSecrets)。本地镜像先
minikube image load。
- CrashLoopBackOff:
logs --previous 看崩溃输出;逐项排查 command/args、必需环境变量、依赖可达性、探针配置。
- Pod 一直 Pending:describe 的 Events 会点名原因——
Insufficient cpu/memory(资源不够,第 10 课讲 requests)、节点污点、PVC 未绑定,对号入座。
面试常问
-
讲讲 Pod 的生命周期。
创建后先 Pending(调度 + 拉镜像),容器全部启动进入 Running,readiness 通过进入 Ready 才接流量;正常退出到 Succeeded、失败到 Failed(Job 类)、失联为 Unknown。生命周期内的关键机制:initContainers 串行跑完才起主容器;探针(liveness 失败重启容器、readiness 失败摘流量);优雅终止(SIGTERM + terminationGracePeriodSeconds,默认 30 秒)。
-
liveness、readiness、startup 探针的区别?
liveness:失败重启容器,处理进程僵死;readiness:失败把 Pod 从 Service Endpoints 摘除(不重启),处理「活着但没能力服务」;startup:启动期独占探针,成功后 liveness/readiness 才接管,解决慢启动(大堆 JVM)被误杀的问题。生产标配:readiness 必配,liveness 阈值放宽谨慎配,慢应用加 startupProbe。
-
为什么 Pod 内容器共享网络?有什么用?
同 Pod 共享一个 network namespace:同一个 IP 与端口空间,容器间 127.0.0.1 直接互访。用途:sidecar 模式——日志代理、网格 envoy、辅助进程与主容器零开销通信;也解释了为什么同 Pod 内端口不能冲突。
自测三问
- 删掉一个 Pod 后它又出现了——这说明什么?去哪找「真凶」?
- Pod 显示 Running 但 Service 访问不通,第一个查什么?
- 本站 backend 启动要 40 秒(要连 MySQL/Redis),探针应该怎么配?
小结一句话
Pod 是共享网络与存储的最小调度单元,生命周期由相位与探针共同刻画:liveness 管死活、readiness 管接客——下一课把 Pod 交给 Deployment,获得副本自愈与滚动发布能力。
延伸