K8S Pod 删除逻辑的一个易错点#

1. 问题描述#

在 Kubernetes 控制器开发中,我们遇到了一个问题:当 Pod 进入 terminating 状态时,未被检查到,导致 Running 状态的列表中仍然存在该 Pod。

2. 原因分析#

这个问题的根本原因在于 Kubernetes Pod 生命周期的特殊性,以及我们对 Pod 状态判断的不完整:

  1. Pod 终止是一个过程,而非瞬时状态

    • 当一个 Pod 被标记为删除时,Kubernetes 首先会设置 Pod.DeletionTimestamp 字段
    • 此时 Pod 的 Status.Phase 字段仍然保持为 Running,直到容器实际停止
    • 这个过程可能持续数秒到数分钟
  2. 代码中的逻辑缺陷

    1
    2
    3
    4
    // 原代码只检查了 Phase 是否为 Running
    if !isPodRelatedToKinesisConsumer(&p, kcCopy) || p.Status.Phase != corev1.PodRunning {
    continue
    }

    这段代码的问题在于:它只检查了 Pod 的 Phase 是否为 Running,但没有检查 Pod 是否已经被标记为删除(即是否设置了 DeletionTimestamp)。在以前的开发过程中,遇到都是 Pod 很快被删除的场景,故为发现该问题。

3. 解决方案#

修改 Pod 筛选逻辑,同时检查 DeletionTimestamp

1
2
3
4
5
6
// 修改后的代码
if !isPodRelatedToKinesisConsumer(&p, kcCopy) ||
p.Status.Phase != corev1.PodRunning ||
p.DeletionTimestamp != nil { // 额外检查 DeletionTimestamp
continue
}

这样,即使 Pod 的 Phase 仍为 Running,但只要它被标记为删除(设置了 DeletionTimestamp),就会被排除在分片分配之外。

4. Kubernetes Pod 生命周期需注意#

  1. 终止状态判断

    • 不要仅依赖 Pod.Status.Phase 判断 Pod 是否在终止
    • 始终检查 Pod.DeletionTimestamp != nil 来确定 Pod 是否开始了删除流程
  2. 删除顺序与事件时机

    • Pod 删除是一个多阶段过程:设置 DeletionTimestamp → 执行 preStop hook → 发送 SIGTERM → 等待优雅终止时间 → 发送 SIGKILL
    • 控制器可能在这个过程的任何阶段被触发,需要稳健处理各种状态
  3. 资源清理时机

    • 对于依赖 Pod 的资源(如我们的分片分配),应当在 Pod 进入终止状态就立即更新相关状态
    • 不要等到 Pod 完全删除后再处理,这会导致不必要的延迟