3. Pod深入理解:Pod生命周期、静态Pod、Init容器、容器探针

大家好,我是你们的云原生架构师。今天我们来聊聊Pod——这个Kubernetes世界里最基础的调度单元。说实话,很多新手把Pod当成“一组容器的集合”就完事了,但真正在生产环境里踩过坑的人都知道,Pod的生命周期管理、初始化流程、健康检查,每一个细节都可能让你半夜被报警电话叫醒。

我个人习惯把Pod比作一个“微型服务器”。它有自己的生命周期,从出生到死亡;它需要初始化配置,就像服务器上电后要加载BIOS;它还需要健康检查,就像运维人员每天巡检服务器状态。好,我们一个一个来看。

3.1 Pod生命周期

Pod的生命周期,说白了就是它从创建到销毁的全过程。Kubernetes用一组状态来跟踪这个过程:Pending、Running、Succeeded、Failed、Unknown

为什么会这样设计?你想想看,一个Pod里可能跑着多个容器,每个容器的启动速度不一样,有的容器可能启动失败,有的容器可能一直在运行。Kubernetes需要一种统一的方式来描述Pod的整体状态。

状态 说明 常见场景
Pending Pod已被集群接受,但容器尚未全部启动 镜像拉取中、调度等待中
Running Pod已绑定到节点,所有容器已创建 至少一个容器正在运行或正在启动
Succeeded Pod中所有容器成功退出 Job任务执行完毕
Failed Pod中至少一个容器以非零状态退出 应用崩溃、配置错误
Unknown 无法获取Pod状态 节点失联、网络分区

我在项目中遇到过这样一个案例:一个微服务Pod一直卡在Pending状态,排查了半天,发现是镜像仓库的认证信息过期了。嗯,这里要注意,Pending状态不等于“正在启动”,它可能意味着调度失败、资源不足、或者镜像拉取失败。

避坑指南:我曾经在生产环境遇到过Pod状态显示Running,但应用实际不可用的情况。原因是容器启动了,但应用进程还在初始化。所以,只看Pod状态是不够的,必须结合探针来确认应用是否真正就绪。

3.2 静态Pod

静态Pod,是Kubernetes里一个比较特殊的存在。它不由API Server管理,而是由kubelet直接管理。说白了,就是kubelet在本地节点上根据配置文件直接创建的Pod。

为什么会需要静态Pod?我举个例子:Kubernetes集群自身的核心组件,比如kube-apiserver、kube-controller-manager,它们本身也是Pod。但如果API Server挂了,谁来创建这些Pod?这时候静态Pod就派上用场了——kubelet可以直接从本地文件读取Pod定义,不需要依赖API Server。

静态Pod的配置文件通常放在kubelet的--pod-manifest-path指定的目录下,默认是/etc/kubernetes/manifests

# 示例:静态Pod配置文件
# /etc/kubernetes/manifests/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    role: myrole
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
          protocol: TCP

你想想看,kubelet会定期扫描这个目录,发现新的yaml文件就创建Pod,文件被删除就销毁Pod。但注意,静态Pod不能被API Server直接删除,你只能通过删除本地文件来停止它。

小技巧:我个人习惯用静态Pod来部署集群的“救生艇”组件,比如日志收集器、节点监控代理。这些组件即使集群挂了,也要保证能运行。

3.3 Init容器

Init容器,顾名思义,是用于初始化的容器。它在应用容器启动之前运行,并且必须成功退出后,应用容器才会启动。

为什么需要Init容器?我遇到过这样一个场景:一个Java应用启动前需要等待MySQL数据库就绪,还需要从Git仓库拉取配置文件。这些初始化任务如果放在应用容器里,会让应用镜像变得臃肿,而且初始化逻辑和业务逻辑耦合在一起。

Init容器的典型用法:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  initContainers:
    - name: init-myservice
      image: busybox:1.28
      command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
    - name: init-mydb
      image: busybox:1.28
      command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
  containers:
    - name: myapp
      image: myapp:1.0
      ports:
        - containerPort: 8080

这里有几个关键点:

  • Init容器总是运行到成功完成为止
  • 如果Init容器失败,Kubernetes会重启它(取决于Pod的restartPolicy)
  • 所有Init容器都成功完成后,应用容器才会启动
  • Init容器不支持readiness探针,因为它们必须在启动前完成
核心要点:Init容器是串行执行的,一个接一个运行。如果你有多个Init容器,它们会按顺序执行,前一个成功后才启动下一个。这在需要依赖顺序的场景下非常有用。

我曾经用Init容器解决过一个棘手的问题:一个遗留应用需要从S3下载一个几百MB的模型文件,下载过程耗时5分钟。如果把这个逻辑放在应用容器里,会导致Pod启动后长时间无法响应readiness探针,被Service剔除。用Init容器来处理下载,应用容器启动时模型文件已经就绪,完美解决。

3.4 容器探针(Liveness/Readiness/Startup)

容器探针,是Kubernetes用来检测容器健康状态的机制。说白了,就是Kubernetes定期给容器做“体检”,根据体检结果决定下一步操作。

三种探针各有分工:

探针类型 作用 失败后的行为
Liveness Probe 检测容器是否存活 重启容器
Readiness Probe 检测容器是否就绪 从Service Endpoints中移除
Startup Probe 检测容器是否启动完成 延迟其他探针的检查

你想想看,如果没有探针,Kubernetes怎么知道你的应用是活着还是死了?它只能看到容器进程是否在运行。但进程在运行不代表应用可用——比如Java应用发生了Full GC导致长时间停顿,或者应用陷入了死锁。

3.4.1 Liveness Probe(存活探针)

Liveness探针用于判断容器是否“活着”。如果探针失败,kubelet会杀死容器并重启它。

apiVersion: v1
kind: Pod
metadata:
  name: liveness-demo
spec:
  containers:
    - name: liveness
      image: busybox
      args:
        - /bin/sh
        - -c
        - touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
      livenessProbe:
        exec:
          command:
            - cat
            - /tmp/healthy
        initialDelaySeconds: 5
        periodSeconds: 5

这个例子中,容器启动后创建了一个文件,30秒后删除。Liveness探针每5秒检查一次文件是否存在。30秒后文件被删除,探针失败,容器被重启。

注意:我曾经见过有人把Liveness探针设置得过于敏感,比如每1秒检查一次,结果应用稍微有点负载波动就被重启了。建议initialDelaySeconds设置得宽松一些,给应用足够的启动时间。

3.4.2 Readiness Probe(就绪探针)

Readiness探针用于判断容器是否“准备好”接收流量。如果探针失败,Pod会被从Service的Endpoints列表中移除,但容器不会被重启。

apiVersion: v1
kind: Pod
metadata:
  name: readiness-demo
spec:
  containers:
    - name: readiness
      image: nginx
      readinessProbe:
        httpGet:
          path: /healthz
          port: 80
        initialDelaySeconds: 3
        periodSeconds: 5

这个例子中,Readiness探针通过HTTP GET请求检查/healthz端点。如果返回200-399状态码,则认为就绪;否则认为未就绪。

我个人习惯在应用里专门实现一个/healthz端点,不仅检查进程是否存活,还检查依赖的服务(比如数据库、缓存)是否可用。这样Readiness探针才能真正反映应用的就绪状态。

3.4.3 Startup Probe(启动探针)

Startup探针是Kubernetes 1.16引入的,专门解决慢启动容器的问题。你想想看,有些Java应用启动需要2-3分钟,如果Liveness探针设置得太早,应用还没启动完成就被判定为不健康,反复重启,陷入死循环。

apiVersion: v1
kind: Pod
metadata:
  name: startup-demo
spec:
  containers:
    - name: startup
      image: myapp:1.0
      startupProbe:
        httpGet:
          path: /healthz
          port: 8080
        failureThreshold: 30
        periodSeconds: 10
      livenessProbe:
        httpGet:
          path: /healthz
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5

Startup探针的工作原理:在Startup探针成功之前,其他探针(Liveness和Readiness)不会启动。Startup探针失败30次(failureThreshold: 30,每次间隔10秒)后,容器被重启。也就是说,这个应用最多有300秒的启动时间。

最佳实践:对于启动时间不确定的应用,我建议优先使用Startup探针。把failureThreshold设大一些,periodSeconds设长一些,给应用充足的启动时间。一旦Startup探针成功,Liveness探针接管,用更短的周期来检测运行时的健康状态。

3.5 三种探针的协同工作

三种探针不是孤立的,它们协同工作,形成一个完整的健康检查体系:

  1. 启动阶段:Startup探针负责检测应用是否启动完成。在此期间,Liveness和Readiness探针被禁用。
  2. 运行阶段:Startup探针成功后,Liveness探针开始工作,确保应用持续存活。如果应用挂掉,Liveness探针触发重启。
  3. 流量管理:Readiness探针全程工作,控制Pod是否接收流量。即使应用存活,如果Readiness探针失败,流量也会被切断。

我曾经遇到过一个典型案例:一个内存密集型的应用,在GC时CPU飙升,导致Liveness探针超时,容器被重启。但实际上应用只是在进行正常的GC操作。解决方案是:把Liveness探针的超时时间设长一些,同时增加一个更轻量级的Readiness探针,只检查应用的核心功能是否可用。

总结一下:
  • Liveness探针:保命用的,失败就重启
  • Readiness探针:控流用的,失败就切流量
  • Startup探针:护启动用的,给慢应用争取时间
三者配合使用,才能构建一个健壮的Pod健康检查体系。

好了,关于Pod的深入理解就讲到这里。下一章我们会聊聊Pod的资源管理和调度策略,到时候我会分享一些我在生产环境中做资源限制和QoS配置的实战经验。记得动手实践,光看不练是学不会Kubernetes的。