架构鹅结合TencentOS团队在混部方面的落地实战经验,重点推送了TencentOS Server大规模容器集群混部服务器QoS产品“如意”相关内容。
TencentOS “如意”产品主要应用在离、在线混部场景,目标是为了提高服务器的资源利用率,进而降低业务及客户的服务器成本。“如意”作为一套OS侧的资源隔离方案,主要包括CPU、IO、内存和网络四大离模块。针对这四种服务器资源进行QoS(Quality of Service,服务质量),能够将用户的在线、离线业务部署到同一台服务器上,既能保证在线业务的服务质量,又能有效提升资源利用率。
针对“如意”特性中CPU QoS模块、网络资源隔离及IO隔离分别推出深度技术解析文章,本篇,架构鹅重点介绍如意内存隔离方案的应用和实践效果。
1.内存隔离应用背景
随着容器的大量使用,资源隔离的需求越来越强烈,每个容器的内存使用都会有一定的限额,不能超过这个限制。
Linux的设计哲学是尽量充分利用系统内存。比如:将被访问过的硬盘数据加载到内存(page cache),Page Cache以Page为单位,缓存文件内容。缓存在Page Cache中的文件数据,能够更快的被用户读取。同时对于带buffer的写入操作,数据在写入到Page Cache中即可立即返回,而不需等待数据被实际持久化到磁盘,进而提高了上层应用读写文件的整体性能。由于Linux的设计哲学,这个内存占用(page cache)是没有限制的,即只要有空闲内存,就可以占用。
但是这也带来一个问题,page cache占用过多,导致系统内存不足时,会影响业务正常内存分配(内存不足时,内存分配要先回收,再分配),使得业务内存分配延时增大。
因此,容器时代,限制page cache的占用,有着非常重要的意义。
2.解决方案
2.1.
社区解决方案
那社区中针对这个问题有解决方案么?答案是有,而且很早就有了,看社区中的讨论:https://lwn.net/Articles/602424/
这个patch是要限制page cache,使得page cache占用的内存保持在一定比例。这个patch在社区中讨论了很久,但是最终也没能进入社区,争论的主要焦点是:目前已有的回收逻辑是否已经足够?社区希望通过已有方式,比如提高内存low、min水位线使得kswapd尽早开始回收内存以及通过调节脏页回写比例来限制page cache。
通过memory cgroup来限制内存占用,但是这个只能限制总的内存占用,不能精确的控制page cache的占用。而且在cgroup v2上,kernel memory的限制功能已经被删掉,不能通过限制kernel memory的方式来限制page cache了。
针对dentry cache的限制,社区中也有相应的实现:
https://lkml.org/lkml/2018/7/12/586
这个patch可以限制negative dentry的内存占比,记录、显示系统中negative dentry的功能已经合入到社区 5.0版本中,限制功能则未能进入社区。
至于未能进入的原因同样是不想把内核搞得更复杂,能否用已有功能实现。
Redhat在RHEL 7中加入了这个功能,但是在RHEL 8中又删除了该功能,原因说是这个功能会导致系统崩溃,还说RHEL 8中的upstream代码已经可以处理negative dentry,但是,社区中目前并没有针对negative dentry的限制。
后面TencentOS “如意”会继续调研、完善这个功能,并将其合入,实现对negative dentry的限制,提高系统性能。
接下来,我们重点看下page cache的限制。
虽然page cache limit的功能没能进入upstream,但是,有些厂商还是合入了page cache limit功能,比如SUSE:
https://www.suse.com/support/kb/doc/?id=000019008
TencentOS也加入了自己的全局page cache limit的实现。
全局page cache limit使得page cache的占用限制在一定比例,保证了系统空闲内存,降低内存分配延时,同时使得一些不能进入回收的场景(比如中断上下文)的内存分配得以成功。
但是随着容器以及混部的流行,全局page cache限制还能满足业务需求么?
容器场景下,每个容器的可用内存都是有限的,超过这个限额就会发生OOM,这会严重的影响业务性能。而其中经常出现由于pagecache占用了大量内存,导致发生OOM的情况。因此限制cgroup内pagecache占用具有重要意义。
2.2.
“如意”内存QoS隔离方案
TencentOS “如意”调研了限制容器page cache占用的几种实现。下面我们分别看一下。
2.2.1. 方案1
实现一个cgroup级别的dirty_background_ratio/dirty_ratio。
首先看下第一个方案,目前系统中有一个全局的dirty_background_ratio和dirty_ratio来控制脏页的内存占比,当脏页比例高于dirty_background_ratio设定的值后,唤醒后台flush进程写回磁盘。但这是个全局的值,所有优先级的容器共享同一个值,如果实现cgroup级别的dirty_background_ratio/dirty_ratio,每个容器可以有自己的脏页回刷比例,超过这个比例就回刷,使得各自page cache占用保持在一定比例。但是,这个功能会导致TencentOS “如意”的另一个特性IO QoS控制不准。另外,对于非脏页占用的page cache,可能需要单独的统计。因此,我们暂时没有实现这个方案。
2.2.2. 方案2
扩展TencentOS原有的全局page cache limit功能,使其支持cgroup级别的page cache限制。
目前TencentOS已经支持全局的page cache limit功能,进一步扩展使之支持基于cgroup级别的限制,看起来是个可行的方案。
memory cgroup打开的情况下,cgroup内每一次内存申请/释放(包括用户空间申请的内存、内核空间申请的内存,page cache, slab等等)都会被记录下来,我们为每个memory cgroup新增一个page counter来专门记录该cgroup内page cache的占用,如果本次内存更新的类型为page cache,则更新该page counter。
非direct IO模式下读写一个文件的时候,首先会到page cache中查找,看看要读的内容是否已经在page cache中了,即调用pagecache_get_page函数用来查找一个page cache页,如果发现没有,就会新分配一页并且加入到page cache slot中,我们在此加入限制检查功能,检查当前分配的页所在的memory cgroup,如果其page cache占用已经超过了限额,则先尝试回收,回收多少可以配置,回收成功后,进程就可以继续使用page cache了,这样操作后,可以使得cgroup内page cache占用的内存保持在一定范围内,不至于占用太多内存,影响业务内存分配性能:
看到这里,有同学可能会问,那如果一直回收不回来怎么办?理论上来说,page cache占用的内存都是可以回收的,如果万一回收不了,比如,多线程程序疯狂的读写文件,pagecache占用一直超额,那么我们还有终极处理方法:OOM,我们支持设置重试次数,如果这么多次的回收还是不能达到要求的话,就会OOM,因为这个程序可能出问题了。
用户可以查看当前pagecache占用了多少内存:
如果想一次清掉本cgroup的pagecache占用的内存,而不影响其它cgroup,该怎么做?
有办法,执行一个本cgroup的pagecache清除操作即可:
memory.pagecache.reclaim_ratio设置pagecache超额后,回收的比例,当该比例设置成100后,表示马上清除该cgroup的pagecache占用。
我们同时加入了pagecache超过限额以及被OOM的统计计数,业务可以查看memory cgroup下的memory.events文件查看:
我们新增了sysctl参数:vm.pagecache_limit_global来控制使用全局pagecache限制功能,还是基于cgroup的限制。
同时支持原来的vm.pagecache_limit_ignore_dirty以及vm.pagecache_limit_ignore_slab来决定回收时是否回收脏页以及是否回收slab。
该功能在cgroup v1和v2下都支持。
3.page cache限制效果
没有限制的时候,page cache的内存占用一直在增加,直到占满所有内存,page cache限制后,可以看到,page cache占用最后会达到一个稳定值。
4.问题
有的同学可能会说,这不是违反了Linux的设计哲学了么?没有充分利用内存。是的,用户需要自己权衡利弊,打开page cache限制,可以预留一些内存给业务使用,不至于在分配内存的时候没有内存,而要先回收,增大了内存分配延时。但是,限制page cache使用也会降低文件的读写效率。
腾讯TencentOS荣获OSCAR尖峰奖,创新突破节省上亿度电资源
云原生下,TencentOS“如意” IO隔离之BPS. IOPS. 权重隔离
本期出品:腾讯系统研发中心团队
前往“发现”-“看一看”浏览“朋友在看”