一、一句话理解 Pod

Pod = K8s 的最小调度单元,是一个“逻辑主机”。

它里面可以跑一个或多个容器,这些容器共享:

  • 同一个 IP

  • 同一个 hostname

  • 同一个 Network / IPC / UTS 命名空间

  • 同一份 Volume(按需挂载)

所以 Pod 里的容器之间可以用 localhost 直接通信,像在同一台机器上一样。


二、Pod 和容器到底是什么关系?

### 1. 容器是“进程”,Pod 是“进程组 + 共享环境”

对比项

容器

Pod

定位

单进程运行环境

一个“逻辑主机”

网络

每个容器独立 IP

所有容器共享一个 IP

通信

跨容器要走网络

容器间 localhost 直连

存储

各自挂载

可共享 Volume

调度

不直接调度容器

调度器以 Pod 为单位调度

生命周期

容器重启

Pod 内 Pause 容器维持命名空间,IP 不变

2. Pause 容器:Pod 的“二房东”

Pod 启动时,第一个创建的是 Pause 容器(也叫 Infra 容器)。

它不干业务,只做一件事:创建并持有 Network / IPC / UTS 命名空间。

业务容器(nginx、fluentd 等)启动时,是“加入”Pause 容器已经建好的命名空间。

所以:

  • 业务容器崩溃重启 → 重新加入同一个命名空间 → IP 不变、hostname 不变

  • 这就是为什么 Pod 内容器重启,对外表现却很稳定

3. 一个 Pod 里典型有哪些容器?

 Pod
 ├── Pause 容器(Infra,持有命名空间)
 ├── Init 容器(按顺序执行,全部成功后才启动主容器)
 ├── 主容器(nginx / 业务应用)
 └── Sidecar 容器(日志采集、配置热更新、服务网格代理等)


三、为什么最小运行单位是 Pod,而不是容器?

这是 K8s 最核心的设计决策,原因有三个:

理由一:紧密协作的容器需要共享 localhost 和 Volume

比如:

  • nginx 写日志到 /var/log/nginx

  • fluentd 读同一个日志文件

如果它们是两个独立容器、被调度到不同 Node:

  • 只能走网络存储 → 延迟高、成本高

  • 只能走网络通信 → 不能 localhost

放进同一个 Pod:

  • 共享 emptyDir Volume → 本地磁盘速度

  • 共享 Network 命名空间 → localhost:80 直连,零网络延迟

理由二:Pod 是资源调度的原子单位

调度器以 Pod 为单位分配 Node。

如果以容器为单位:

  • nginx 和 fluentd 可能被分到不同 Node

  • 通信、存储、调度复杂度都会大幅上升

理由三:Pause 容器维持命名空间,保证 IP 稳定

业务容器重启时,Pause 容器还在,命名空间还在,IP 和 hostname 都不变。


四、Pod 内三个关键共享

共享内容

实际效果

Network 命名空间

同一个 IP、同一个 localhost,容器间零网络延迟

IPC 命名空间

进程间可用信号量/共享内存通信

Volume

nginx 写日志,fluentd 从同一目录读

注意:Mount、Cgroup、User 命名空间是各自独立的,Pod 只共享与网络通信密切相关的部分。


五、三种探针(重点)

K8s 用探针检查容器健康状态,三种探针各司其职。

1. 三种探针的核心区别

探针

检查什么

失败后果

典型场景

livenessProbe 存活探针

容器还活着吗?

杀掉容器,重启

死锁、进程无响应

readinessProbe 就绪探针

能接流量吗?

从 Service 摘除,不转发流量

启动慢、依赖未就绪

startupProbe 启动探针

启动完成了吗?

liveness 暂不生效,失败则重启

Java 等慢启动应用

2. 为什么需要 startupProbe?

Java 应用启动要 60 秒。

如果只用 liveness(15 秒开始检查,连续失败 3 次):

  • 45 秒就被判定失败 → 重启 → 永远起不来 → CrashLoopBackOff

startupProbe 在 liveness 之前执行,给足启动时间:

 startupProbe:
   httpGet: { path: /start, port: 8080 }
   failureThreshold: 30
   periodSeconds: 10
 # 最多等 30 × 10 = 300 秒

3. 三种探测方式

 # HTTP GET(最常用)
 livenessProbe:
   httpGet:
     path: /healthz
     port: 80
 ​
 # TCP Socket(MySQL、Redis 等)
 readinessProbe:
   tcpSocket:
     port: 3306
 ​
 # Exec 命令(检查文件等)
 livenessProbe:
   exec:
     command: ["cat", "/tmp/healthy"]

4. 关键参数速查

参数

含义

推荐值

initialDelaySeconds

首次检查前等待

liveness: 15~30s;readiness: 5~10s

periodSeconds

检查间隔

10s(通用),5s(关键服务)

timeoutSeconds

单次检查超时

3~5s

failureThreshold

连续失败次数

liveness: 3;startup: 30

successThreshold

连续成功才恢复

1(默认即可)

调优公式:

 liveness 最长容忍窗口 = initialDelaySeconds + (periodSeconds × failureThreshold)
 示例:15 + (10 × 3) = 45 秒
 ​
 startupProbe 保护窗口 = periodSeconds × failureThreshold
 示例:10 × 30 = 300 秒(给 Java 5 分钟启动时间)

5. 探针端点设计规范(很重要)

探针

端点

应该检查什么

不应该检查什么

startupProbe

/start

进程已启动、基本初始化完成

外部依赖

livenessProbe

/healthz

进程未死锁、线程池可用

外部依赖(DB 挂了不该杀容器)

readinessProbe

/ready

外部依赖就绪、连接池可用

内部死锁(那是 liveness 的事)

错误示范:

 // ❌ liveness 检查了 DB
 if err := db.Ping(); err != nil {
     w.WriteHeader(500)  // DB 挂了 → 杀容器 → 重启也没用 → CrashLoopBackOff
 }

正确做法:

 // ✅ liveness 只检查自身
 if isDeadlocked() {
     w.WriteHeader(503)
     return
 }
 w.WriteHeader(200)


六、Pod 生命周期与重启策略

1. 生命周期阶段

阶段

说明

Pending

已创建,等待调度/拉镜像/等 PVC

Running

至少一个容器运行中

Succeeded

所有容器成功终止(Job/CronJob)

Failed

所有容器终止,至少一个失败

Unknown

无法获取状态(节点失联)

2. 重启策略

策略

行为

适用场景

Always

总是重启(默认)

Deployment、StatefulSet、DaemonSet

OnFailure

退出码非 0 才重启

Job

Never

永不重启

一次性任务、调试 Pod

3. 优雅停机流程

 kubectl delete pod
   → 执行 preStop hook
   → 发送 SIGTERM
   → 等待 terminationGracePeriodSeconds(默认 30 秒)
   → 超时发送 SIGKILL 强制终止


七、Init Container 的核心用途

Init 容器在主容器之前运行,按顺序执行,全部成功后才启动主容器。

用途

示例

等待依赖

until nslookup mysql-svc; do sleep 2; done

数据库迁移

alembic upgrade head

初始化配置

从 Vault/Consul 拉密钥

权限设置

chown -R 1000:1000 /data

预下载数据

从 S3 下载模型文件


八、QoS 与资源管理

 resources:
   requests:          # 调度保障:决定 Pod 放哪个 Node
     cpu: "100m"
     memory: "128Mi"
   limits:            # 运行时上限
     cpu: "500m"      # CPU 超了限速,不杀进程
     memory: "256Mi"  # Memory 超了 OOMKill

QoS 等级

条件

资源不够时谁先被杀

Guaranteed

requests == limits

最后(最安全)

Burstable

requests < limits

中等

BestEffort

没设任何 requests/limits

最先被杀

生产建议:至少 Burstable,关键服务 Guaranteed。


九、最容易记混的点(笔记重点)

  1. Pod 不是容器,Pod 是“逻辑主机”,容器是里面的“进程”。

  2. Pause 容器不干活,只持有命名空间,业务容器重启 IP 不变。

  3. 最小单位是 Pod,因为紧密协作的容器需要共享 localhost + Volume。

  4. liveness 失败 → 重启容器;readiness 失败 → 摘流量;startup 失败 → 保护慢启动。

  5. liveness 不要检查外部依赖,否则 DB 挂了会引发无意义重启。

  6. readiness 是发布新版本时最重要的探针,能防止错误版本接入流量。

  7. Init 容器按顺序执行,全部成功才启动主容器。

  8. QoS 决定 OOM 时谁先被杀:Guaranteed > Burstable > BestEffort。


十、一张图记住 Pod 与容器关系

 Pod(逻辑主机,共享 IP / hostname / Network NS)
 ├── Pause 容器(Infra,持有命名空间,IP 不变的关键)
 ├── Init 容器(按顺序执行,全部成功才启动主容器)
 ├── 主容器(业务应用)
 └── Sidecar 容器(日志、配置、代理)
      ↑
   共享 localhost + Volume