Kubernetes集群中OOMKill警报指南

2020-11-24 07:00:20

RAM很可能是服务器上最先耗尽的最稀缺的资源。如果您真的想在Linux / Unix下运行软件,那么您肯定会知道OOMKill是什么。

简短的更新:当程序从内核请求一个新的内存页时,可能会发生两种情况。

有一个空闲的内存页面:内核页面将页面分配给进程,一切都很好。

系统内存不足(OOM):内核根据其“不良”(主要取决于其使用的内存量)选择进程。它将SIGKILL发送到该进程。这迫使接收进程退出,并带有退出代码137。属于该进程的所有内存页面都是空闲的,现在内核可以满足内存请求。

最近,我有一项任务是向大型Kubernetes集群添加警报。群集具有约100个活动部署,在高峰时间可自动缩放节点,最多可扩展约50个节点。集群维护良好,并具有强大的自动扩展策略。所有部署都定义了资源限制。有时,某些已部署的Pod会违反内存限制。在这些情况下,最好找出何时发生并调查原因。

Prometheus和Alertmanager已经部署。因此,我认为针对OOMKills发出警报将非常容易。我只需要找到表明OOMKill已经发生的正确度量标准,并为此编写警报规则。鉴于这篇文章的篇幅,您可以想象我有多错误!

简短的Google搜索使我了解了kube pod状态指标。事实证明,它具有一个称为kube_pod_container_status_last_terminated_reason的度量。当容器中的容器因错误终止时,度量标准的值为1。根据退出代码,如果退出代码为137,则原因标签将设置为OOMKilled。这听起来很有希望!因此,我为此创建了一个警报。

像往常一样,事情很少是简单的。容器重新启动后,此度量标准的值将为1。出于警报目的,必须将其与另一个在容器重启时会更改的度量标准结合起来。 kube_pod_container_status_restarts_total做到了。结合两个-和宾果游戏!有效!

有一小会儿,我以为我做完了。我正要宣布在生产中击败OOMKills!但后来我困惑了:我们的一位软件开发人员挺身而出。他声称自己的一个吊舱内存不足,因此看不到任何警报。

起初,我不倾向于相信他关于内存不足的诊断是正确的。主要是因为他的Pod甚至没有重启!但是随后我查看了Pod的内存使用情况图。它确实显示出通常的模式:内存使用率将增长,在内存限制时达到峰值,然后突然下降。

我已要求开发人员提供实施的详细信息。事实证明,容器中的init进程将启动一个子进程并等待其结果。如果子进程退出并出现错误,它将向请求者返回错误而不终止(因为-为什么要这样做?)。

那是对我的曙光-我的警报仅在容器退出时才有效。当容器的初始化过程为OOMKilled时,通常就是这种情况。但是不能保证如果init的子代被OOMKilled会发生。如果容器的init尝试自行处理OOMKill,则不会触发我的警报!

鉴于OOMKills与Unix一样古老,我想:肯定会有人为此提供解决方案。

为此,我疯狂地寻找某种度量标准导出器。我只需要一个Pod或至少一个Docker容器中的OOMKill事件数。这是我发现的:

我的第一站是cAdvisor本身。事实证明,cAdvisor正在获取OOMKill事件,但未将其导出为Prometheus指标,似乎没有人真正在乎。所以那是死胡同。

我尝试了最新版本的Docker映像,但是一旦启动,它就会崩溃并烧毁:

F1120 22:04:21.571246 1 main.go:73]无法创建日志监视程序I1120 22:04:21.572066 1 main.go:64]启动Prometheus指标

似乎一年来没有人提交任何代码。它的星星数量少(14)。所有这些意味着我回到了第一位。

很难找到一个现有的解决方案只意味着一件事:我将不得不编写自己的解决方案。

粗略地看一下Docker的事件,就提供了我所需的一切。有一个称为oom的事件。每当OOMKiller进程在容器中处于活动状态时,Docker都会发出此事件。现在,我只缺少一段代码来监听那些事件并将它们导出为Prometheus指标。

这就是缺少容器度量标准的诞生方式。它的作用是连接到本地Docker实例(通过/var/run/docker.sock)。它列出了所有现有容器作为起点。然后,它侦听Docker事件。使用这些事件,它可以跟踪当前正在运行的容器。它还收集它知道的每个容器的基本统计信息:

根据设计,它不是特定于Kubernetes的。这意味着它可以与普通Docker一起使用。但是它还具有几个非常方便的Kubernetes特定功能。

只要找到容器名称或名称空间的容器标签,就会将它们作为标签添加到导出的指标。同样,标签命名与kube-state-metrics兼容。

在Kubernetes集群中,容器丢失指标需要在每个节点上运行。实现此目的的最简单方法是使用守护程序集。源代码附带一个示例守护程序集部署。

我发现的最有趣的问题是我最不期望的问题:流利!

将节点/ pod / kubelet日志的流利日志转发器发送到日志聚合器。当日志量很高时,Fluentd被OOM杀死。

查看Fluentd如何工作的细节,可以清楚了解发生了什么。

Fluentd有一个主要进程(最终是容器中的init进程)。这个主进程派生一个转发日志的工作进程。当工作进程由于某种原因(例如OOMKill)而死时,主进程将启动一个新进程。这导致无休止的生成/ OOMKill循环。

Fluentd是日志转发器这一事实非常不幸。 OOMKill循环将停止日志转发,因此您无法通过检查日志来“查看”正在发生的情况。

如果要确保Kubernetes集群运行状况良好,请务必对OOMKills发出警报。这使您可以知道进程何时达到其内存限制。是因为内存泄漏或错误配置的内存限制。

事实证明,对Kubernetes中的OOMKills进行监视并不像人们想象的那样容易。但是,使用漏失容器度量标准会使其变得更加容易。

因此,继续将缺少容器的指标部署到您的集群。您可能会惊讶于您没有注意到多少OOMKills。

希望对您有用,并可以节省您寻找解决方案所花费的时间。