K8s Pod 何时会被删除?

最近在梳理 Kubelet 的源码实现,从源码入手整理了下 Kubelet 以及 kube-controller 删除 Pod 的场景。

本文K8s版本参考v1.20.6

Kube-Controller

workload controller 触发删除

  • job

    • 如果 job 处于 failed 状态,删除所有 activePod(deleteJobPods
    • 如果 activePod 大于 job.Spec.Parallelism 并行数,删除多余 Pod(manageJob
  • daemonset

    • 根据 taints、资源、selector 等条件判断 Pod 是否需要运行在某节点上,如果不需要则调用 syncNodes 删除
  • replicaset

    • 根据实际的 Pod 数与 Spec 的 Pod 数比较,如果多了就调用删除 Pod(manageReplicas
  • replication

    • rc 已基本不使用
  • statefulset(主要在更新函数updateStatefulSet中)

    • 状态是 failed 的 Pod 删除重建
    • 缩容时按照顺序删除 Pod
    • 如果版本号旧的 Pod 也删除

NodeController 触发删除

nodeController主要通过污点导致Pod的删除

  • K8s Admission 为所有 Pod 打上Toleration

    • DefaultTolerationSeconds admission 为所有 Pod 打上 NotReady 的 NoExecute 容忍,容忍时间是 5 分钟(如果之前没有相同 KEY 的污点的话)
    • 源码参考:plugin/pkg/admission/defaulttolerationseconds/admission.go
  • 节点不可用时

    • 对于 NotReady 的 Node,NodeController 会自动打上污点

      1
      2
      3
      4
      5
      6
      7
      taints:
      - effect: NoSchedule
      key: node.kubernetes.io/not-ready
      timeAdded: "2021-12-02T10:08:44Z"
      - effect: NoExecute
      key: node.kubernetes.io/not-ready
      timeAdded: "2021-12-02T10:08:47Z"
    • NoExecute 污点与 TaintBasedEvictions 特性相关;monitorNodeHealth 会长期检查 Node 状态,如果 NotReady,会调用 processTaintBaseEviction.markNodeForTainting 将该 Node 加入 zoneNoExecuteTainter 队列,doNoExecuteTaintingPass 会周期循环这个队列打上对应的污点

    • NoSchedule 污点为 doNoScheduleTaintingPass 函数打的,启用了 TaintNodeByCondition feature-gates 后,nodeUpdate 后会根据 Node 的 Condition 打上对应的污点,映射关系如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      v1.NodeReady: {
      v1.ConditionFalse: v1.TaintNodeNotReady,
      v1.ConditionUnknown: v1.TaintNodeUnreachable,
      },
      v1.NodeMemoryPressure: {
      v1.ConditionTrue: v1.TaintNodeMemoryPressure,
      },
      v1.NodeDiskPressure: {
      v1.ConditionTrue: v1.TaintNodeDiskPressure,
      },
      v1.NodeNetworkUnavailable: {
      v1.ConditionTrue: v1.TaintNodeNetworkUnavailable,
      },
      v1.NodePIDPressure: {
      v1.ConditionTrue: v1.TaintNodePIDPressure,
      },
    • 源码参考:pkg/controller/nodelifecycle/node_lifecycle_controller.go

  • Pod 容忍时间达到5分钟后,Pod 被 taintManager 驱逐

Pod GC Controller

  • pkg/controller/podgc/gc_controller.go
    • gcTerminated
      • 根据创建时间排序,删除大于 kube-controller-manager terminated-pod-gc-threshold 配置(我们数据面集群配置 12500 )个数的 Pod
    • gcOrphaned
      • 删除 bind node 不存在的 Pod
    • gcUnscheduledTerminating
      • 遍历所有pods,过滤那些terminating(pod.DeletionTimestamp != nil)并且未调度成功的(pod.Spec.NodeName为空)的pods

Kubelet

Kubelet 不会主动调用 client-go 删除 Pod,只会在 APIServer 收到 Pod 优雅删除请求后,如果 Pod 所占用的 cgroups、volume 等资源都释放了,则会调用强制删除接口(GracePeriodSeconds 为 0)删除 Pod

以下三种情况会触发 SyncPodKill 事件,此时 Kubelet 会调用 CRI stop running container

  • 驱逐https://kubernetes.io/zh/docs/concepts/scheduling-eviction/node-pressure-eviction/)

    • kubelet 具有以下默认硬驱逐条件:
      • memory.available<100Mi
      • nodefs.available<10%
      • nodefs.available<10%
      • imagefs.available<15%
      • nodefs.inodesFree<5%(Linux 节点)
      • imagefs.inodesFree、pid.available 未配置
  • 抢占(https://kubernetes.io/zh/docs/concepts/scheduling-eviction/pod-priority-preemption/)

    • PriorityClass 还有两个可选字段:globalDefaultdescriptionglobalDefault 字段表示这个 PriorityClass 的值应该用于没有 priorityClassName 的 Pod。 系统中只能存在一个 globalDefault 设置为 true 的 PriorityClass。 如果不存在设置了 globalDefault 的 PriorityClass, 则没有 priorityClassName 的 Pod 的优先级为零。
    • 当没有悬决 Pod,或者悬决 Pod 的优先级等于或低于牺牲者时,不得发生抢占。
    • Kube-controller-manager 等三大件优先级高。
  • 节点 shutdown (pkg/kubelet/nodeshutdown/nodeshutdown_manager_linux.go processShutdownEvent)