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 映像)。无论我们如何调整信号处理,吊舱似乎永远不会真正完全关闭,即使日志表明它们是这样的。
另一个奇怪的地方是,根据 Kubernetes Pod 日志,我们看到 Pod 终止开始,然后被取消:
2021-08-06 17:00:08.000 EDT 停止容器 preempt-pod
2021-08-06 17:02:41.000 EDT 取消删除 Pod preempt-pod
我们还尝试添加 preStop 15 秒延迟,只是为了看看是否有任何效果,但我们尝试的任何操作似乎都不重要 - 豆荚变成了僵尸。新副本在池中可用的其他节点上启动,因此它始终保持系统上成功运行的 Pod 的最小数量。
我们还使用 sim 维护事件来测试抢占周期:
gcloud 计算实例模拟维护事件节点 ID
在浏览了各种帖子后,我最终决定每 9 分钟运行一次 cronjob,以避免 pod 处于关闭状态超过 10 分钟后触发 AlertManager。对我来说,这仍然感觉像是一种 hack,但它确实有效,并且它迫使我深入研究 k8s cronjob 和 RBAC。
这篇文章让我走上了这条道路:如何删除 Kubernetes“关闭”pod https://stackoverflow.com/questions/68344231/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
imagePullPolicy: IfNotPresent
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 的错误输出。
Update添加了角色中缺少的“删除”动词,并添加了缺少的 RoleBinding
Update添加了 imagePullPolicy
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)