4、Pod深入理解:Pod的生命周期、Init容器、静态Pod、Pod的调度策略

好,咱们今天来聊聊Pod。说实话,Pod是Kubernetes里最基础但也最容易被轻视的概念。很多人觉得Pod不就是一组容器的组合嘛,跑起来就行了。但我在生产环境摸爬滚打这几年,发现很多线上故障,根源就是对Pod的生命周期和调度机制理解不够深。

这一节,我会把Pod的几个核心机制掰开揉碎讲清楚。包括它从生到死经历了什么、Init容器到底有什么用、静态Pod是什么鬼、以及Pod是怎么被调度到节点上的。嗯,咱们一个一个来。

4.1 Pod的生命周期

Pod的生命周期,说白了就是它从创建到销毁的全过程。Kubernetes把Pod的状态分成了几个阶段,我习惯用一张表来记:

状态 说明
Pending Pod已被集群接受,但容器还没完全启动。可能是镜像在拉取,或者调度器还没找到合适的节点。
Running Pod已经绑定到节点,所有容器都创建成功,至少有一个容器在运行中。
Succeeded Pod中的所有容器都正常退出,不会再重启。一般用于Job任务。
Failed Pod中至少有一个容器以非零状态退出,表示任务失败了。
Unknown API Server联系不上Pod所在的节点,状态不明。通常是网络或节点故障。

你可能会问:「Pod的状态变化,我怎么监控?」我个人习惯用 kubectl get pods -w 来实时观察。有一次排查一个Pod一直Pending的问题,发现是镜像拉取策略设成了Always,而镜像仓库认证过期了。你看,一个小细节就能卡半天。

核心要点:Pod的生命周期不仅仅是容器的生命周期。Pod本身是一个逻辑概念,它的状态由所有容器的状态综合决定。任何一个容器挂了,Pod都可能进入Failed或Unknown状态。

4.2 Init容器

Init容器,说白了就是「前置任务容器」。它在Pod的主容器启动之前运行,跑完就退出。你想想看,有些场景下,主容器启动前需要做一些初始化工作,比如等待数据库就绪、初始化数据卷、或者做一些权限检查。这些工作如果放在主容器里做,会让镜像变得臃肿,而且不好管理。

我曾经在一个微服务项目中,遇到主容器启动时连不上数据库,导致不断重启。后来用了一个Init容器,专门写了个脚本轮询数据库端口,等数据库就绪了再启动主容器。问题一下子就解决了。

Init容器的几个特点,我总结一下:

  • Init容器总是运行到成功完成(即退出码为0)为止。
  • 如果Pod有多个Init容器,它们会按定义的顺序依次执行。
  • 如果某个Init容器失败,Kubernetes会重启整个Pod(取决于restartPolicy)。
  • Init容器不支持readinessProbe和livenessProbe,因为它们必须成功退出。

来看一个实际的YAML示例:

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;']
  containers:
  - name: myapp-container
    image: myapp:latest
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']

小技巧:Init容器里尽量用轻量级镜像,比如busybox或alpine。别把主容器的镜像拿来当Init容器用,那样会浪费拉取时间和存储空间。

4.3 静态Pod

静态Pod,这个名字听起来有点怪。其实它指的是不由API Server管理,而是由kubelet直接管理的Pod。你想想看,kubelet在节点上会监控一个特定的目录(默认是 /etc/kubernetes/manifests),只要这个目录下有Pod的YAML文件,kubelet就会自动创建并运行对应的Pod。

静态Pod有什么用?我告诉你,Kubernetes控制平面的核心组件,比如kube-apiserver、kube-controller-manager、kube-scheduler,很多集群就是用静态Pod来部署的。为什么?因为静态Pod不依赖API Server,即使API Server挂了,静态Pod依然能运行。说白了,这是集群自举的一种方式。

我记得有一次,一个客户的API Server证书过期了,整个集群都瘫痪了。但控制面组件是用静态Pod跑的,我们直接登录到节点上,修改了静态Pod的YAML文件,重新加载了证书,然后kubelet自动重启了Pod。整个过程没用到kubectl,全靠静态Pod的机制。

静态Pod的几个关键点:

  • 你不能用kubectl delete来删除静态Pod,只能去节点上删YAML文件。
  • kubelet会定期扫描目录,如果YAML文件被修改,Pod会自动更新。
  • 静态Pod的镜像名称会带上节点名称,方便识别。

注意:静态Pod不支持Horizontal Pod Autoscaler(HPA),也不支持Deployment的滚动更新。它就是一个「死」的Pod,适合跑系统级组件,不适合跑业务应用。

4.4 Pod的调度策略

Pod的调度,说白了就是决定Pod跑在哪个节点上。默认情况下,调度器会综合考虑资源需求、节点亲和性、污点容忍度等因素。但有时候,我们需要手动干预调度策略。

我把它分成三类来讲:

4.4.1 nodeSelector

这是最简单的调度方式。你给节点打上标签,然后在Pod的YAML里指定 nodeSelector。比如:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeSelector:
    disktype: ssd
  containers:
  - name: nginx
    image: nginx

这样,Pod只会被调度到带有 disktype=ssd 标签的节点上。简单粗暴,但不够灵活。

4.4.2 节点亲和性(nodeAffinity)

nodeAffinity比nodeSelector强大得多。它支持两种模式:

  • requiredDuringSchedulingIgnoredDuringExecution:硬性要求,调度时必须满足,否则Pod不会被调度。
  • preferredDuringSchedulingIgnoredDuringExecution:软性要求,调度器会尽量满足,但不强制。

举个例子:

spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/arch
            operator: In
            values:
            - amd64
            - arm64
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        preference:
          matchExpressions:
          - key: disktype
            operator: In
            values:
            - ssd

你看,这个Pod必须跑在amd64或arm64架构的节点上,同时尽量跑在有SSD磁盘的节点上。权重越高,调度器越优先考虑。

4.4.3 污点与容忍度(Taints & Tolerations)

污点,是打在节点上的「排斥标记」。容忍度,是Pod上的「通行证」。只有Pod容忍了节点的污点,才能被调度到该节点上。

我举个例子。假设有一个节点专门用来跑GPU任务,你可以给它打上污点:

kubectl taint nodes gpu-node gpu=true:NoSchedule

然后,只有那些声明了容忍度的Pod才能调度上去:

spec:
  tolerations:
  - key: "gpu"
    operator: "Equal"
    value: "true"
    effect: "NoSchedule"

我曾经在混合部署的场景中,用污点把线上服务和离线任务隔离开。线上节点打上 production=true:NoSchedule,离线任务不容忍这个污点,自然就不会跑上去。效果非常好。

避坑指南:我曾经遇到过一个问题,给节点打了污点后,发现系统组件(如CoreDNS)也调度不上去了。后来才想起来,系统组件默认没有容忍这些污点。解决办法是给系统组件所在的命名空间加上容忍度,或者用 NoExecute 效果时小心处理。

4.5 小结

嗯,这一节内容不少。Pod的生命周期让你知道Pod从生到死经历了什么;Init容器帮你解决启动依赖问题;静态Pod让你理解控制面组件的自举机制;调度策略则让你能灵活控制Pod的部署位置。

我个人觉得,理解Pod的这些机制,是深入Kubernetes的第一步。你想想看,如果连Pod都搞不清楚,后面学Deployment、StatefulSet、DaemonSet这些高级资源,肯定会一头雾水。

下一节,咱们聊聊Pod的进阶话题——资源限制与服务质量(QoS)。到时候我会分享一些我在压测场景中踩过的坑,敬请期待。