Kubernetes AutoScaler 或更改 AWS 中的所需节点会过早终止 Docker Pods

Kubernetes AutoScaler or changing Desired Nodes in AWS prematurely terminates Docker Pods

我构建了一个利用 docker pods 来处理数据的服务。所需时间少则 15 分钟,多则 1 小时。

我的应用程序捕获 SIGTERM 以确保在 Pods 和节点退役时需求下降时正常关闭。

在每个 docker 图像中,我放置了代码以报告它是否因为它完成了工作而关闭以及是否发生了 SIGTERM 事件并因此完成了它的处理并终止。

我的系统使用 EKS 部署在 AWS 中。我使用 EKS 在需求上升时管理节点部署,在需求下降时管理节点停转。我使用 KEDA 来管理 POD 部署,这有助于触发是否需要额外的节点。在 KEDA 中,我将 cooldownPeriod 定义为 2 小时,这是我希望 pod 花费的最大值,即使它花费的最大值是 1 小时。

在 AWS EKS 中,我也定义了 2 小时的 terminationGracePeriodSeconds。

我在节点缩减期间隔离了一个问题,即当节点终止时,terminationGracePeriodSeconds 没有得到遵守,我的 Pods 将在大约 30 分钟内关闭。因为 Pods 被突然删除,我无法查看他们的日志以了解发生了什么。

我试图通过发布 kubernetes 节点排水来模拟这个问题并保留我的 pod 运行

kubectl drain <MY NODE>

我看到 SIGTERM 出现了,我也注意到 pod 只在 2 小时后而不是之前终止。

所以有那么一小会儿我想也许我没有正确配置 terminationGracePeriod,所以我检查了:

kubectl get deployment test-mypod -o yaml|grep terminationGracePeriodSeconds
  terminationGracePeriodSeconds: 7200

我什至重新部署了配置,但这没有任何区别。

但是,我能够通过修改节点组的 desiredSize 来重现该问题。我可以通过这样做以编程方式在 Python 中重现它:

        resp = self.eks_client.update_nodegroup_config(clusterName=EKS_CLUSTER_NAME,
                                                       nodegroupName=EKS_NODE_GROUP_NAME,
                                                       scalingConfig={'desiredSize': configured_desired_size})

或者直接转到 AWS 控制台并在那里修改 desiredSize。

我看到 EKS 选择一个节点,如果碰巧有一个 pod 处理数据大约需要一个小时,这个 pod 有时会提前终止。

我已经登录到正在缩小的那个节点,但在日志中没有发现 Pod 过早终止的证据。

我曾经能够捕获此信息

kubectl get events | grep test-mypod-b8dfc4665-zp87t
54m         Normal    Pulling    pod/test-mypod-b8dfc4665-zp87t         Pulling image ...
54m         Normal    Pulled     pod/test-mypod-b8dfc4665-zp87t         Successfully pulled image ...
54m         Normal    Created    pod/test-mypod-b8dfc4665-zp87t         Created container mypod
54m         Normal    Started    pod/test-mypod-b8dfc4665-zp87t         Started container mypod
23m         Normal    ScaleDown  pod/test-mypod-b8dfc4665-zp87t         deleting pod for node scale down
23m         Normal    Killing    pod/test-mypod-b8dfc4665-zp87t         Stopping container mypod
13m         Warning   FailedKillPod   pod/test-po-b8dfc4665-zp87t       error killing pod: failed to "KillContainer" for "mypod" with KillContainerError: "rpc error: code = Unknown desc = operation timeout: context deadline exceeded"

我曾经看到一个 pod 被无缘无故地删除,当 scaledown 被禁用时,它决定删除我的 pod:

kubectl get events | grep test-mypod-b8dfc4665-vxqhv
45m         Normal    Pulling    pod/test-mypod-b8dfc4665-vxqhv Pulling image ...
45m         Normal    Pulled     pod/test-mypod-b8dfc4665-vxqhv Successfully pulled image ...
45m         Normal    Created    pod/test-mypod-b8dfc4665-vxqhv Created container mypod
45m         Normal    Started    pod/test-mypod-b8dfc4665-vxqhv Started container mypod
40m         Normal    Killing    pod/test-mypod-b8dfc4665-vxqhv Stopping container mypod

这是我拥有的 kuberenets 版本

Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.0" GitCommit:"9e991415386e4cf155a24b1da15becaa390438d8", GitTreeState:"clean", BuildDate:"2020-03-25T14:58:59Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18+", GitVersion:"v1.18.20-eks-8c49e2", GitCommit:"8c49e2efc3cfbb7788a58025e679787daed22018", GitTreeState:"clean", BuildDate:"2021-10-17T05:13:46Z", GoVersion:"go1.13.15", Compiler:"gc", Platform:"linux/amd64"}

为了尽量减少这个问题,我在高峰时段部署了 Pod 中断预算以阻止缩减,并在晚上需求低的时候删除了启动缩减的 PDB。但是,这不是正确的解决方案,即使在低峰期间,仍然有 pods 过早停止。

使用 Amazon EKS 时,节点自动缩放器不遵守 terminationGracePeriodSeconds。每

https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/FAQ.md#does-ca-respect-gracefultermination-in-scale-down

Node Autoscaler 仅提供 10 分钟的宽限期。我在这里提取了相关文本:

Cluster Autoscaler 有多快?

默认情况下,scale-up 在 pod 被标记为不可调度后最多 10 秒被认为是,在节点变得不需要后 scale-down 10 分钟。有多个标志可用于配置这些阈值。例如,在某些环境中,您可能希望为 k8s 调度程序提供比 CA scan-interval 更多的时间来调度 pod。一种方法是设置 --new-pod-scale-up-delay,这会导致 CA 忽略不可调度的 pods,直到它们达到某个“年龄”,而不管 scan-interval.如果 k8s 在该延迟结束时还没有安排它们,那么 CA 可能会考虑它们 scale-up.

另一个相关link:https://github.com/kubernetes/autoscaler/issues/147

我实现了一个脚本作为 preStop Hook 来调用,它有望阻止发出 SIGTERM 的下一个状态并开始 10 分钟倒计时,让我有机会优雅地关闭我的服务。但是,preStopHook 不会延迟 10 分钟计时器。

对该设置的一些引用:

https://www.ithands-on.com/2021/07/kubernetes-101-pods-lifecycle-hooks_30.html

https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/

相反,我根据以下参考向我的 pod 部署配置添加了以下注释:

https://aws.github.io/aws-eks-best-practices/cluster-autoscaling/#prevent-scale-down-eviction

template:
  metadata:
    labels:
      annotations:
        cluster-autoscaler.kubernetes.io/safe-to-evict: 'false'

然后我确保我的 pods 变成按需 pods,即没有 pods 部署为空闲,因为空闲 pods 影响 EKS 缩小并且仅在以下情况下生成需要并在完成任务后关闭。这会减慢我对作业的响应时间,但相对于在昂贵的计算中关闭 Pod 而言,这是一个较小的代价。

如果有人对如何部署 AWS Cluster Autoscaler 感到好奇: https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html#cluster-autoscaler

它有一个关于禁用驱逐的参考 Pods

在负载下,我们仍然看到 safe-to-evict 注释未被遵守并将此报告回 Amazon AWS。通过额外的调试,我发现 EKS 看到托管 pods 的节点正在消失,尽管 EKS 忽略了安全驱逐的节点。 EKS 和 EC2 之间可能存在互操作性问题。在解决此问题之前,我正在考虑使用 Fargate 作为备用自动缩放器。