容器可以使用Swap空间吗?

网友投稿 240 2022-10-20

容器可以使用Swap空间吗?

大家应该都知道,Swap空间,简单地说其实就是磁盘开辟的一块空间。

当内存写满的时候,就可以把内存中不常用的数据暂时置换到Swap空间上,如此,内存空间就可以释放出来,用来满足新内存申请的需求。

它的好处是能应对一些瞬时突发的内存增大需求,不至于内存一时不够而导致OOM Killer,从而导致进程被杀死。

那么问题来了,对于一个容器,特别是被设置了Memory Cgroup 之后,它还可以使用 Swap 空间吗?会不会出现什么问题呢?

一、情景复现

1、在一个有swap空间的节点启动一个容器,设置好它的Memory Cgroup的限制。

如果没有swap可以新建一个

#!/bin/bashfallocate -l 20G ./swapfiledd if=/dev/zero of=./swapfile bs=1024 count=20971520chmod 600 ./swapfilemkswap ./swapfileswapon swapfile

例子中,swap空间大小为20G,free命令结果看到也是20G。

2、再启动一个容器,容器的 Memory Cgroup 限制为 512MB,容器中的 mem_alloc 程序去申请 2GB 内存。

#!/bin/bashdocker stop mem_alloc;docker rm mem_allocdocker run -d --name mem_alloc shijuliu/mem_alloc:v1sleep 2CONTAINER_ID=$(sudo docker ps --format "{{.ID}}\t{{.Names}}" | grep -i mem_alloc | awk '{print $1}')echo $CONTAINER_IDCGROUP_CONTAINER_PATH=$(find /sys/fs/cgroup/memory/ -name "*$CONTAINER_ID*")echo $CGROUP_CONTAINER_PATHecho 536870912 > $CGROUP_CONTAINER_PATH/memory.limit_in_bytescat $CGROUP_CONTAINER_PATH/memory.limit_in_bytes

上图中可以看到,mem_alloc 进程的 RSS 内存一直在 512MB(RES: 515596)左右。并没有发生OOM导致容器退出的情况。

再看一下 Swap 空间,使用了 1.5GB (used 1542144KB)。简单计算一下,1.5GB + 512MB,结果正好是 mem_alloc 这个程序申请的 2GB 内存。

通过例子可以知道,因为swap空间的使用,本来会被OOM Killer的容器,还可以运行得好好的,如此看来似乎也挺好,但是这样一来,Memory Cgroup对内存的限制不就失去作用了?

如果一个容器中的程序发生了内存泄漏(Memory leak),那么本来 Memory Cgroup 可以及时杀死这个进程,让它不影响整个节点中的其他应用程序。结果现在这个内存泄漏的进程没被杀死,还会不断地读写 Swap 磁盘,反而影响了整个节点的性能。

这样一分析,似乎又得禁用swap了,到底用还是不用呢?以实际场景出发,具体情况具体分析。

比如说,某一类程序就是需要 Swap 空间,才能防止因为偶尔的内存突然增加而被 OOM Killer 杀死。因为这类程序重新启动的初始化时间会很长,这样程序重启的代价就很大了,也就是说,打开 Swap 对这类程序是有意义的。

这一类程序一旦放到容器中运行,就意味着它会和“别的容器”在同一个宿主机上共同运行,那如果这个“别的容器” 如果不需要 Swap,而是希望 Memory Cgroup 的严格内存限制。

这样一来,在这一个宿主机上的两个容器就会有冲突了,有应该如何解决?

二、理解swappiness参数

在普通 Linux 系统上,如果使用过 Swap 空间,那么可能配置过 proc 文件系统下的 swappiness 参数 (/proc/sys/vm/swappiness)。swappiness的定义可以在​​Linux内核文档​​中找到。

从文档中解释可知,swappiness 可以决定系统将会有多频繁地使用交换分区。

一个较高的值会使得内核更频繁地使用交换分区,而一个较低的取值,则代表着内核会尽量避免使用交换分区。swappiness 的取值范围是 0–100,缺省值 60。

是不是可以理解为,当这个值是 100 的时候,哪怕还有空闲内存,也会去做内存交换,尽量把内存数据写入到 Swap 空间里;值是 0 的时候,基本上就不做内存交换了,也就不写 Swap 空间了呢?没那么简单。

在有磁盘文件访问的时候,Linux 会尽量把系统的空闲内存用作 Page Cache 来提高文件的读写性能。在没有打开 Swap 空间的情况下,一旦内存不够,这种情况下就只能把 Page Cache 释放了,而 RSS 内存是不能释放的。

在 RSS 里的内存,大部分都是没有对应磁盘文件的内存,比如用 malloc() 申请得到的内存,这种内存也被称为匿名内存(Anonymous memory)。那么当 Swap 空间打开后,可以写入 Swap 空间的,就是这些匿名内存。

所以在 Swap 空间打开的时候,问题也就来了,在内存紧张的时候,Linux 系统怎么决定是先释放 Page Cache,还是先把匿名内存释放并写入到 Swap 空间里呢?

可能发生的情况:

如果系统先把 Page Cache 都释放了,那么一旦节点里有频繁的文件读写操作,系统的性能就会下降。如果 Linux 系统先把匿名内存都释放并写入到 Swap,那么一旦这些被释放的匿名内存马上需要使用,又需要从 Swap 空间读回到内存中,这样又会让 Swap(其实也是磁盘)的读写频繁,导致系统性能下降。

显然,在释放内存的时候,需要平衡 Page Cache 的释放和匿名内存的释放,而 swappiness,就是用来定义这个平衡的参数。

上面说了swappiness值的范围是0到100,其实应该说它是用来定义 Page Cache 内存和匿名内存的释放的一个比例,权重。

来看一段代码:

/* * With swappiness at 100, anonymous and file have the same priority. * This scanning priority is essentially the inverse of IO cost. */ anon_prio = swappiness; file_prio = 200 - anon_prio;

从代码中可以看到,这个比例是 anon_prio: file_prio,这里 anon_prio 的值就等于 swappiness。

分三种情况讨论:

1、当 swappiness 的值是 100 的时候,匿名内存和 Page Cache 内存的释放比例就是 100: 100,也就是等比例释放了。

2、就是 swappiness 缺省值是 60 的时候,匿名内存和 Page Cache 内存的释放比例就是 60 : 140,Page Cache 内存的释放要优先于匿名内存。

3、当 swappiness 的值是 0 时,当空闲内存少于内存一个 zone 的"high water mark"中的值的时候,Linux 还是会做内存交换,也就是把匿名内存写入到 Swap 空间后释放内存。

zone 是 Linux 划分物理内存的一个区域,里面有 3 个水位线(water mark),水位线可以用来警示空闲内存的紧张程度。

来做个实验验证下:

先运行​​echo 0 > /proc/sys/vm/swappiness​​ 命令把 swappiness 设置为 0,然后用前面例子mem_alloc程序申请内存。

调用mem_alloc 之后,Swap 空间就被使用了。

因为 mem_alloc 申请 12GB 内存已经和节点最大内存差不多了,如果查看 cat /proc/zoneinfo ,也可以看到 normal zone 里 high (water mark)的值和 free 的值差不多,这样在 free

总结:swappiness 的取值范围在 0 到 100,值为 100 的时候系统平等回收匿名内存和 Page Cache 内存;一般缺省值为 60,就是优先回收 Page Cache;即使 swappiness 为 0,也不能完全禁止 Swap 分区的使用,就是说在内存紧张的时候,也会使用 Swap 来回收匿名内存。

二、解决问题 & 验证

回顾前面问题,运行了容器,使用了Memory Cgroup 之后,swappiness 怎么工作呢?

查看一下 Memory Cgroup 控制组下面的参数,会看到有一个 memory.swappiness 参数。

memory.swappiness 可以控制这个 Memroy Cgroup 控制组下面匿名内存和 page cache 的回收,取值的范围和工作方式和全局的 swappiness 差不多。这里有一个优先顺序,在 Memory Cgorup 的控制组里,如果你设置了 memory.swappiness 参数,它就会覆盖全局的 swappiness,让全局的 swappiness 在这个控制组里不起作用。

注意:当 memory.swappiness = 0 的时候,对匿名页的回收是始终禁止的,也就是始终都不会使用 Swap 空间。

这时 Linux 系统不会再去比较 free 内存和 zone 里的 high water mark 的值,再决定一个 Memory Cgroup 中的匿名内存要不要回收了。

在swap空间的节点上再跑个容器,设置容器对应 Memory Cgroup 里的 memory.swappiness 设置为 0。

这次在容器中申请内存之后,Swap 空间就没有被使用了,而当容器申请的内存超过 memory.limit_in_bytes 之后,就发生了 OOM Kill。

到这里,其实就解决了上述问题:在同一个宿主机上,假设同时存在容器 A 和其他容器,容器 A 上运行着需要使用 Swap 空间的应用,而别的容器不需要使用 Swap 空间。

根据实验知道,还是可以在宿主机节点上打开 Swap 空间,同时在其他容器对应的 Memory Cgroups 控制组里,把 memory.swappiness 这个参数设置为 0。这样一来,不但满足了容器 A 的需求,而且别的容器也不会受到影响,仍然可以严格按照 Memory Cgroups 里的 memory.limit_in_bytes 来限制内存的使用。

三、总结

问题:

对于一个容器,特别是被设置了Memory Cgroup 之后,它还可以使用 Swap 空间吗?会不会出现什么问题呢?如何正确理解swapiness参数?只要在宿主机节点上打开 Swap 空间,在容器中就是可以用到 Swap 的。但出现的问题是在同一个宿主机上,对于不需要使用 swap 的容器, 它的 Memory Cgroups 的限制也失去了作用。

swappiness 参数值的作用是,在系统里有 Swap 空间之后,当系统需要回收内存的时候,是优先释放 Page Cache 中的内存,还是优先释放匿名内存(也就是写入 Swap)。

swappiness 的取值范围在 0 到 100 之间,需要记住下面三个值:

值为100时,释放Page Cache 和匿名内存是同等优先级的。值为 60,这是大多数 Linux 系统的缺省值,这时候 Page Cache 的释放优先级高于匿名内存的释放。值为 0 的时候,当系统中空闲内存低于一个临界值的时候,仍然会释放匿名内存并把页面写入 Swap 空间。

swappiness 参数除了在 proc 文件系统下有个全局的值外,在每个 Memory Cgroup 控制组里也有一个 memory.swappiness,那它们有什么不同呢?

每个 Memory Cgroup 控制组里的 swappiness 参数值为 0 的时候,就可以让控制组里的内存停止写入 Swap。这样一来,有了 memory.swappiness 这个参数后,需要使用 Swap 和不需要 Swap 的容器就可以在同一个宿主机上同时运行了,这样对于硬件资源的利用率也就更高了。

注意理解:memory.swappiness 和/proc/sys/vm/swappiness

四、补充

k8s开启swapkubelet缺省不能再打开swap的节点上运行,配置”failSwapOn: false”参数,kubelet可以在swap enabled的节点上运行。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:java性能调优System的gc垃圾回收方法
下一篇:使用 Veth Pair 虚拟网卡对不同的网络空间进行通信
相关文章

 发表评论

暂时没有评论,来抢沙发吧~