【问题标题】:k8s pods stuck in failed/shutdown state after preemption (gke v1.20)k8s pod 在抢占后卡在失败/关闭状态(gke v1.20)
【发布时间】:2021-10-11 17:02:39
【问题描述】:

TL;DR - gke 1.20 抢占式节点导致 pod 僵尸进入失败/关闭

几年来,我们一直在使用 GKE,其中包含混合了稳定和抢占式节点池的集群。最近,从 gke v1.20 开始,我们开始看到被抢占的 pod 进入了一种奇怪的僵尸状态,它们被描述为:

状态:失败

原因:关机

消息:节点正在关闭,正在驱逐 pod

当这种情况开始发生时,我们确信这与我们的 Pod 未能在抢占时正确处理 SIGTERM 有关。我们决定通过将服务软件归结为一个大部分时间处于休眠状态的简单服务来消除我们的服务软件作为问题的根源:

/* eslint-disable no-console */
let exitNow = false

process.on( 'SIGINT', () => {
  console.log( 'INT shutting down gracefully' )
  exitNow = true
} )

process.on( 'SIGTERM', () => {
  console.log( 'TERM shutting down gracefully' )
  exitNow = true
} )

const sleep = ( seconds ) => {
  return new Promise( ( resolve ) => {
    setTimeout( resolve, seconds * 1000 )
  } )
}

const Main = async ( cycles = 120, delaySec = 5 ) => {
  console.log( `Starting ${cycles}, ${delaySec} second cycles` )

  for ( let i = 1; i <= cycles && !exitNow; i++ ) {
    console.log( `---> ${i} of ${cycles}` )
    await sleep( delaySec ) // eslint-disable-line
  }

  console.log( '*** Cycle Complete - exiting' )
  process.exit( 0 )
}

Main()

此代码使用 tini init 构建到 docker 映像中,以生成在 nodejs 下运行的 pod 进程(fermium-alpine 映像)。无论我们如何调整信号处理,似乎 Pod 都不会真正干净地关闭,即使日志表明它们确实如此。

另一个奇怪的是,根据 Kubernetes Pod 日志,我们看到 pod 终止开始,然后被取消:

2021-08-06 17:00:08.000 EDT 停止容器 preempt-pod

2021-08-06 17:02:41.000 EDT 取消删除 Pod preempt-pod

我们还尝试添加一个 15 秒的 preStop 延迟,看看这是否有任何影响,但我们尝试的一切似乎都无关紧要 - 豆荚变成了僵尸。新副本在池中可用的其他节点上启动,因此它始终保持系统上成功运行的 pod 的最小数量。

我们还在使用 sim 维护事件测试抢占周期:

gcloud 计算实例模拟维护事件节点 ID

【问题讨论】:

    标签: node.js kubernetes google-kubernetes-engine


    【解决方案1】:

    在浏览了各种帖子后,我终于放弃了每 9 分钟运行一次 cronjob,以避免在 pod 被卡在关闭状态超过 10 分钟后发生的 alertManager 触发器。这对我来说仍然感觉像是一种 hack,但它确实有效,它迫使我深入研究 k8s cronjob 和 RBAC。

    这篇文章让我走上了这条道路: How to remove Kubernetes 'shutdown' pods

    以及由此产生的 cronjob 规范:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      name: pod-accessor-role
      namespace: default
    rules:
    - apiGroups: [""] # "" indicates the core API group
      resources: ["pods"]
      verbs: ["get", "delete", "watch", "list"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: RoleBinding
    metadata:
      name: pod-access
      namespace: default
    subjects:
    - kind: ServiceAccount
      name: cronjob-sa
      namespace: default
    roleRef:
      kind: Role
      name: pod-accessor-role
      apiGroup: ""
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: cronjob-sa
      namespace: default
    ---
    apiVersion: batch/v1beta1
    kind: CronJob
    metadata:
      name: cron-zombie-killer
      namespace: default
    spec:
      schedule: "*/9 * * * *"
      successfulJobsHistoryLimit: 1
      jobTemplate:
        spec:
          template:
            metadata:
              name: cron-zombie-killer
              namespace: default
            spec:
              serviceAccountName: cronjob-sa
              restartPolicy: Never
              containers:
              - name: cron-zombie-killer
                image: bitnami/kubectl
                command:
                  - "/bin/sh"
                args:
                  - "-c"
                  - "kubectl get pods -n default --field-selector='status.phase==Failed' -o name | xargs kubectl delete -n default 2> /dev/null"
    status: {}
    

    注意,stderr 重定向到 /dev/null 只是为了避免 kubectl get 找不到任何处于失败状态的 pod 时 kubectl delete 的错误输出。

    更新从角色中添加了缺失的“删除”动词,并添加了缺失的 RoleBinding

    【讨论】:

    • 另一个提示:如果您需要跨命名空间删除 pod,例如通过在 kubectl-command 中使用 --all-namespaces-flag,您必须使用“ClusterRole”和“ClusterRoleBinding”类型。
    【解决方案2】:

    从 GKE 1.20.5 及更高版本开始,kubelet 优雅关闭节点featureenabled 抢占式节点。来自功能页面上的注释:

    当 pod 在正常节点关闭期间被逐出时,它们是 标记为失败。运行 kubectl get pods 会显示 将 pod 驱逐为 Shutdown。 kubectl describe pod 表示 pod 因节点关闭而被驱逐:

    状态:失败原因:关闭消息:节点 正在关闭,驱逐 pod 失败的 pod 对象将被保留,直到 被 GC 显式删除或清理。这是一个变化 与节点突然终止相比的行为。

    这些 pod 最终应该被垃圾回收,尽管我不确定阈值。

    【讨论】:

    • 是的,没错,我可以运行一个脚本来剔除处于该状态的 pod。问题是我们收到了来自 prometheus 的警报,告诉我们我们的 pod 陷入了这种不确定状态。这是一个很好的功能,能够对失败的 pod 执行取证,但是不应该以这种方式处理遵守终止规则并干净退出的 pod,恕我直言。此外,由于自动缩放而来来去去的 pod 被清理得很好。感觉好像 taint mgmt 和调度程序出了点问题,但我无法解决问题。
    • 我查过了 => --terminated-pod-gc-threshold int32 默认值:12500
    • 我们也遇到了这个问题,我浪费时间试图弄清楚为什么这些 pod 没有被删除,即使它们似乎已正常关闭。就像@jbphelps 的情况一样,我们的 pod 行为正常,并在自动缩放时被删除。这些 pod 也应该在抢占式节点关闭时被删除。
    • 这个问题似乎也阻止了节点本身被删除,因此,如果我们不手动删除 Pod,可能会导致多余的计费。此外,--terminated-pod-gc-threshold 值似乎是 GC 开始删除它们之前允许的 pod 数量。当然,在我们的例子中,我们不想留下 12,500 个终止的 pod,但似乎我们无法更改 GKE 上的值。
    • @JustinSchell - 关于挥之不去的节点,这是一个很好的观点,我什至没有考虑过这种行为的这一方面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-30
    • 2020-03-18
    • 2016-01-02
    • 2016-05-28
    • 2020-10-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多