首页 » K8s » 第3章 Pod 深入

第3章 Pod 深入

 

本章目录(点击跳转):

本章目标:彻底理解 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 深入,转载请注明来源!

赞 0