如何有效测试吊舱记忆
作者:王英浩(混沌网格贡献者)
编辑:黄了,汤姆·德万
混乱的网是一个云本地混沌工程平台,在Kubernetes环境中协调混沌。在它的各种工具中,混沌网格提供了压力混沌,它允许您将CPU和内存压力注入Pod。当您测试或基准测试CPU敏感或内存敏感的程序,并希望了解其在压力下的行为时,此工具非常有用。
然而,当我们测试和使用应力schaos时,我们发现了一些可用性和性能方面的问题。例如,应力schaos使用的内存比我们配置的要少得多。
为了纠正这些问题,我们开发了一组新的测试。在本文中,我将描述我们如何解决这些问题并纠正它们。这些信息可能会帮助你从混乱中获得最大的好处。
向目标舱注入压力
在继续之前,您需要安装混乱网在你的群里。
首先,我将指导您如何向目标舱中注入应力混沌。为了演示,我将使用hello-kubernetes,一个演示应用程序管理执掌图表.第一步是克隆hello-kubernetes
回购并修改图表,以给它一个资源限制。
git克隆[https://github.com/paulbouwer/hello-kubernetes.git](https://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
输出以字节显示并转换为500*1024*500 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_adj
988股。这是相当危险的,因为这是得分最高的过程。
要阻止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库.