如何有效测试吊舱记忆

2021-07-13 王英浩 社区

作者王英浩(混沌网格贡献者)

编辑:黄了,汤姆·德万

如何在Kubernetes中对Pod内存进行有效的压力测试

混乱的网是一个云本地混沌工程平台,在Kubernetes环境中协调混沌。在它的各种工具中,混沌网格提供了压力混沌,它允许您将CPU和内存压力注入Pod。当您测试或基准测试CPU敏感或内存敏感的程序,并希望了解其在压力下的行为时,此工具非常有用。

然而,当我们测试和使用应力schaos时,我们发现了一些可用性和性能方面的问题。例如,应力schaos使用的内存比我们配置的要少得多。

为了纠正这些问题,我们开发了一组新的测试。在本文中,我将描述我们如何解决这些问题并纠正它们。这些信息可能会帮助你从混乱中获得最大的好处。

向目标舱注入压力

在继续之前,您需要安装混乱网在你的群里。

首先,我将指导您如何向目标舱中注入应力混沌。为了演示,我将使用hello-kubernetes,一个演示应用程序管理执掌图表.第一步是克隆hello-kubernetes回购并修改图表,以给它一个资源限制。

git克隆https://github.com/paulbouwer/hello-kubernetes.githttps://github.com/paulbouwer/hello-kubernetes.git代码deploy/helm/hello kubernetes/values.yaml#或任何你喜欢的编辑器

找到资源配置,并按如下方式进行修改:

资源请求内存“200英里”限制内存“500米”

在我们注入混沌之前,让我们看看目标吊舱当前消耗了多少内存。进入吊舱,开始发射炮弹。用Pod的名称输入以下命令:

kubectl执行-- n hello-kubernetes hello-kubernetes-hello-world-b55bfcf68-8mln6

通过输入以下内容显示内存使用情况摘要:

/usr/src/app $ free -m /usr/src/app

从下面的输出中可以看到,Pod消耗了4269 MB内存:

/usr/src/app$free-m used Mem:4269 Swap:0/usr/src/app$top Mem:12742432K used PID PPID USER STAT VSZ%VSZ CPU%CPU COMMAND 1 0 node S 285m 2%0 0%npm start 18 1 node S 284m 2%3 0%node server.js 29 0 node S 1636 0%2 0%/bin/sh 36 29 node R 1568 0%3 0%top

顶部免费的命令给出了类似的答案,但这些数字没有达到我们的预期。我们已经将Pod的内存使用限制在500米,但现在它似乎使用了几gb。

为了找出原因,我们可以在Pod上进行压力schaos实验,看看会发生什么。下面是我们使用的YAML文件:

apiVersion混乱-mesh.org/v1alpha1种类压力混沌元数据的名字mem-强调名称空间混乱-测试规范模式所有选择器名称空间-你好-kubernetes压力内存工人4大小50 mib选项""持续时间“1小时”

将上述文件保存到memory.yaml.通过运行进行混沌实验:

~ kubectl应用-f内存。yaml stresschaos.chaos-mesh.org/mem-stress创建

让我们再次检查内存使用情况:

used Mem: 4332 Swap: 0 Mem:12805568 k使用PID PPID用户统计VSZ % VSZ CPU % CPU命令54 50根R 53252 0% 24% {stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 50000000 57 52根R 53252 0% 0 22% {stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 50000000 55 53根R 53252 2 21% {stress-ng-vm} stress-ng——vm 4 0%——vm-keep vm-bytes 50000000 56 51根R 532520% 3 21% {stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 50000000 18 1节点289年代2% 2节点server.js 1 0 0%节点285年代2% 0 0% npm开始51 49根41048年代0% 0 0% {stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 41048 49根50年代50000000 2 0% {stress-ng-vm} stress-ng——vm 4 0%——vm-keep vm-bytes 50000000 52 49根41048年代0% 0 0%{stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 50000000 53 49根41048 0% 0% {stress-ng-vm} stress-ng——vm 4——vm-keep vm-bytes 50000000 49根年代41044年0% 0 0% stress-ng——vm 4——vm-keep vm-bytes 50000000 29节点0年代/bin/sh 48 R 29节点1568 1636 0% 0% 0% 1 0%

你可以看到stress-ng实例被注入到Pod中。在Pod中上升了60 MiB,这是我们没有预料到的。的stress-ng文档表示增加200 MiB (4 * 50 MiB)。

让我们通过将内存压力从50 MiB更改为3000 MiB来增加压力。这应该会打破Pod的内存限制。我将删除混沌实验,修改内存大小,并重新应用它。

然后,轰!shell以代码137退出。稍后,我重新连接到容器,内存使用恢复正常。没有stress-ng找到实例!怎么搞的?

为什么压力综合症会消失?

在上面的混沌实验中,我们看到了异常行为:内存使用量没有增加,Shell退出。在本节中,我们将找出所有的原因。

Kubernetes通过C组机制。要查看Pod中的500 MiB限制,请转到容器并输入:

/usr/src/应用程序$/ sys / fs / cgroup /内存/ memory.limit_in_bytes524288000

输出以字节显示并转换为5001024500 1024 (MiB)。

请求仅用于安排放置Pod的位置。Pod没有内存限制或请求,但可以将其视为所有容器的总和。

所以,我们从一开始就犯错误。免费的顶部不是“群体化”的。他们依赖于/过程/内存信息(procfs)数据。不幸的是,/过程/内存信息是老的,老到比cgroup还早。他们为你提供宿主内存信息而不是容器内存信息。

记住这一点,让我们重新开始,看看这次得到了什么内存使用量。

要获取“cgrouped”内存使用情况,输入:

/usr/src/应用程序$/sys/fs/cgroup/memory/memory.usage(字节)39821312

应用50个MiB应力列表,得到如下结果:

/usr/src/应用程序$/sys/fs/cgroup/memory/memory.usage(字节)93577216

这比不使用应力schaos时多占用了51 MiB。

下一个问题:为什么shell退出了?退出码137表示“容器接收到SIGKILL失败”。这让我们去检查花苞。注意Pod的状态和事件:

~ kubectl描述豆荚-n hello-kubernetes......上次状态:终止原因:错误退出代码:1......事件:原因年龄从消息类型  ---- ------ ---- ---- -------......警告不健康的10米x4超过16米kubelet准备探测失败:获取“http://10.244.1.19:8080”上下文期限超过客户端。超过超时等待头正常死亡10米x2超过16米容器hello-kubernetes激活探针失败,将重新启动......

这些事件告诉我们为什么炮弹会坠毁。hello-kubernetes有一个活性探针。当容器内存达到极限时,应用程序开始失败,Kubernetes决定终止并重新启动Pod。当Pod重启时,压力schaos停止。到目前为止,你可以说,压力schaos实验效果不错:它在你的Pod中找到了一个漏洞。您现在可以修复它,并重新应用混乱。

现在一切似乎都很完美,除了一件事。为什么四个50个MiB vm工作线程总共产生51个MiB?在我们深入调查之前,答案不会显露出来stress-ng源代码

vm\字节/ =参数->num_instances

哦!所以文件是错误的。多个虚拟机工作者占用指定的总大小,而不是mmap每个工人都有那么多内存。最后,我们得到了答案。在下面的部分中,我们将讨论一些涉及记忆压力的其他情况。

如果没有活体探测器呢?

为了弄清楚如果没有活性探针会发生什么,让我们删除探针并再试一次。

在中查找以下行部署/舵/ hello-kubernetes /模板/ deployment.yaml和删除它们。

活度探针httpGet路径/港口http就绪探测httpGet路径/港口http

之后,升级部署。

有趣的是,在这个场景中,内存使用量不断上升,然后急剧下降;它来回移动。现在发生了什么?让我们检查内核日志。注意最后两行。

/usr/src/app $ dmesg…[189937.363092] [441060] 1000 441060 63955 3791 80 3030 988 node [189937.363145] [443265] 0 443265 193367 84215 197 9179 1000 file -ng-vm…[189937.363148] Kill process 443160 (stressed -ng-vm), UID 0, total-vm:773468kB, anon-rss:152704kB, file-rss:164kB, shmems -rss:0kB

从输出中可以清楚地看出压力ng vm由于内存不足(OOM)错误,进程被终止。

如果进程不能获得所需的内存,它们很可能会失败。与其等待进程崩溃,不如杀死一些进程来释放更多内存。OOM killer按顺序停止进程,并试图恢复最多的内存,同时造成最少的麻烦。有关此过程的详细信息,请参阅介绍给凶手。

查看上面的输出,您可以看到这一点节点,我们的申请程序永远不应该被终止,有一个oom_score_adj988股。这是相当危险的,因为这是得分最高的过程。

要阻止OOM杀手杀死特定的进程,您可以尝试一个简单的技巧。当你创建一个Pod,分配Pod服务质量(QoS)类.通常,如果用精确指定的资源请求创建Pod,则它被分类为保证豆荚。OOM杀手不杀死容器在保证Pod如果有其他选择杀死。这些选项包括non-保证豆荚和stress-ng工人。没有资源请求的Pod被标记为BestEffort,很可能会被OOM杀手首先杀死。

旅游到此为止。当你把压力注入你的豆荚时,我们有两个建议:

  • 不要使用免费的顶部评估容器中的内存。
  • 为Pod分配资源限制时要小心,并选择正确的QoS。

将来,我们将创建一个更详细的文档。

深入研究Kubernetes内存管理

Kubernetes试图驱逐那些使用太多内存的pod(但内存不会超过它们的限制)。Kubernetes得到您的Pod内存使用/sys/fs/cgroup/memory/memory.usage(字节)然后减去total_inactive_file内存.stat

记住Kubernetes支持交换。即使您有一个启用了交换的节点,Kubernetes也会使用swappiness = 0,这意味着交换实际上是禁用的。这主要是出于对性能的考虑。

memory.usage_in_bytes=驻留集+缓存,total_inactive_file是内存中缓存当内存耗尽时,操作系统可以检索的。memory.usage_in_bytes-total_inactive_file被称为working_set. 你可以得到这个working_set价值Kubectl top pod <你的pod>——容器.Kubernetes使用这个值来决定是否驱逐你的豆荚。

Kubernetes定期检查内存使用情况。如果容器的内存使用增长过快或无法逐出容器,则调用OOM killer。Kubernetes有保护自己进程的方法,所以OOM杀手总是选择容器。终止容器后,它可能会重新启动,也可能不会重新启动,具体取决于您的重新启动策略。如果它被杀了,当你执行死刑的时候Kubectl描述pod <你的pod>,你会看到它重新启动,原因是OOMKilled

另一个值得一提的是内核内存。从v1.9开始,Kubernetes默认启用内核内存支持。这也是cgroup内存子系统的一个特性。您可以限制容器内核内存的使用。不幸的是,这会导致v4.2之前的内核版本出现cgroup泄漏。您可以将内核升级到v4.3或禁用该功能。

我们如何实施压力schaos

当容器内存不足时,stress schaos是一种测试容器行为的简单方法。stress schaos使用了一个强大的工具stress-ng分配内存并继续写入分配的内存。因为容器有内存限制,并且容器限制绑定到一个cgroup,所以我们必须找到运行的方法stress-ng在特定的cgroup中。幸运的是,这部分很简单。有了足够的权限,我们可以通过写入中的文件将任何进程分配给任何cgroup/ sys / fs / cgroup /

如果您对混沌网格感兴趣,并愿意帮助我们改进它,欢迎您加入我们的松弛通道(# project-chaos-mesh) !或提交您的拉请求或问题到我们的GitHub库

混沌工程

准备好开始TiDB了吗?