阿里巴巴搜索事业部几乎负责了阿里所有的搜索业务,hippo 调度系统为这些业务服务,提供资源抽象和分配,业务管理与调度的能力。提供了可靠、简单、低成本的资源及应用托管方案,已经在搜索,蚂蚁,广告,菜鸟等BU稳定运行5年多。
对上, 调度系统提供资源抽象能力,computers as computer。
对下, 调度系统提供资源隔离与超卖能力,又让马儿跑,又让马儿少吃草。
所以在16,17年之间,hippo团队提供了单机Mem Qos功能,简单说提供了资源超卖和保障资源质量的功能。通过实时资源使用预估,在离线Mem超卖,在离线Mem比例控制,用户态OOM,资源通信协议等手段,在保证Mem质量的情况下,最大化单机Mem资源数量,达到省钱的目的。这篇文章主要涉及近期冷内存相关的工作,这个工作是Mem Qos的良性补充,从更合理的角度考量内存的使用和回收机制。
Qos泥泞路
自打mem超卖上线以来,就遇到了很多问题,绕不开的两个问题是:
第一个:超卖带来的资源质量问题
第二个:Mem资源使用审计/数数问题
质量问题
资源质量主要描述一份资源的可服务能力。以cpu为例,可以用单位时间处理的指令数衡量,以disk,mem为例可以用访问延时衡量。举例来说同样一份80G内存的分配计划,落到一个很idle的机器上,和落到一个很busy的机器上完全是两个世界。
在Qos生效的这一年里,我们不断的遇到bad case。最典型的就是:mem紧张,导致mem和disk频繁颠簸。这样mem访问延时比正常的差几个数量级,资源质量极速下降。而且整机颠簸影响的不只这一个app,还会影响旁边的人。做人力争利人利己,万不得已,尽量不要损人,这是我们行走一生的哲学,但在这里,在机器世界里,不同app之间,明目张胆的战争和私底下的偷偷摸摸才是生活的全部。另一个例子是我们没有很好的刻画app mem使用的成分情况,有可能会出现几个基本都是匿名页的app堆在一起,流量高峰的时候,匿名页是大户,page cache很少,没有reclaim的余地,系统只能OOM。
造成这个问题的典型场景就是:一个hippo管理之外的程序用了很多mem,和hippo的分配计划撞到一起了。资源质量下降的一个影响是业务超时,另一个影响是单机Qos模块会频繁生效,会杀离线,但部分离线任务的failover代价巨大,而且在机器mem严重短缺的时候,Qos模块能不能100%工作都是谜。
数量问题
调度系统做调度决策需要数据支持,根据单机资源使用情况决定任务的上与下,可以说资源使用审计质量从很大程度上影响了调度决策的质量。cpu,disk的使用审计是简单明确的,但在mem资源使用审计上我们遇到了一些问题。
传统linux风格的系统在管理mem时,基本采取激进分配,lazy回收的做法,倾向于把更多的mem分配出去当作cache,这给实际working set的统计带来困难。传统的working set采集方式,一般会按根据page的类型和所处位置来计算,比如:
active_anon + inactive_anon + active_file + inactive_file * p + unevictable
这个方法里有几个问题:anon的就一定得算在working set里? 这个p该取何值?unevitable里的就一定很“被需要”?
先暂且搁置这些问题,我们快速非严谨的看看linux是如何管理page的。内核里用LRU链表管理page,有active和inactive两个list。但你觉的内核里的LRU list是你想象的样子,按照时钟,长时间没用就挪到inactive list里?内核里是用相对排序和一些启发式策略决定一个page是应该在active里还是在inactive里,而且绝对不是实时排序的,只在那么几个特殊的埋点挪一挪。所以可能会出现经常访问的page呆在inactive list里,很久没有访问的page呆在active list里,而且一个page在list中的顺序基本不会因为这个page被access而改变。
如果没有内存回收动作,你可以认为LRU list是锁死的,那玩意不会实时update的,充分实践lazy策略,这给我们判断哪部分page是hot的带来了困惑。举个例子,短时间集中访问的文件页,假如这些page在active file list里,在这波访问结束后,这些page不再被使用的话,这些page是事实上free的。但在目前的策略下,这些page会长时间呆在active file list里,直到下次内存回收,才会触发迁移,有可能搬移到inactive file list里。
内核里用一些bit来记录某个page是否被访问,这些bit记录能力有限,它记录的不是整个page生命周期的使用情况,更多的是回收前的一次访问或者回收扫描周期内的访问情况。这样LRU的语义就大打折扣,用这个active和inactive来决定working set,是不合适的。
cgroup 和 proc统计都有类似的问题,你只能得到一个粗略的数字,但真正有用的其是一个类似访问分布的东西,我们站在使用者的层面,站在上帝视角,抛开内核的沟沟坎坎,可以在脑海中想象这样的一幅图:
横坐标是page的index,纵坐标是一个时间窗口内page被访问的频率或次数,拿着这个图,你能得到一些信息,然后搞搞小动作:
- 如果要选择一些页牺牲掉,傻子都知道咋选,如果你跟我杠,说这个page很快就要被大量使用了,我选择投降。
- 在这个图上,横着画一条线,纵轴超过这个线的page可以认为是很hot的,这些page就可以当作working set,管他是anon的,还是file的,管他在active的list里,还是在inactive的list里,管他有没有加lock。比如上图的1,3,5,7号page就可以认为是很hot的,0,2,4,6 是比较idle的。
- 这个线的高低可以per group的决定,是不是感觉很美好?其实不光可以画一条线,你可以画两条,比如在上图中,在25那个位置再画一条线,超过50的就是这app最hot部分,要保证这些page的安全。25到50之间的是warm的page,尽量留在RAM里好了,比如6号page。低于25的,就是混日子的,换到磁盘上也没啥事,比如0,2,4号page。
所以我们需要把这些低水位下的page当作是“free”的,可以给别人用的,这是我们“冷内存”项目最初的motivation
冷内存项目
“当成free的”主要有两个事情要做:
- 第一个:这些free的是谁,有多少。涉及到统计问题
- 第二个:这些free的page在需要腾地方的时候要真的能free掉。涉及到回收问题
统计相关:
统计问题是2017年开始的。简单说就是利用和改造“Idle Page Tracking”技术,为每个物理页定义 idle age,保存物理页空闲年龄,然后周期性的扫描RAM,查看这个page最近是否被access过,再通过page结构的cg指针找到这个page是charge到哪个group上的,这样就能得到每个mem cg的mem cold统计直方图。
利用这个数据,我们可以根据业务特点给每个mem cg设置不同的水位,就能知道每个group的cold内存到底有多少,working set到底有多大。
回收相关:
另一个很重要的问题是识别之后的回收动作。
这些水位线以下的,长时间不访问的page如何回收?传统的kswap肯定不好使,因为他没有按照我们的算法pick页,效率也不好。另一个,我们是站在实际访问的角度来判断一个page是hot,warm,还是cold的,和这个page的类型[anon的还是file的],所处位置[在active list里,还是在inactive list里],一些特殊标记[比如lock标记]等都没有关系。为了达到这个目的,我们需要做很多工作,比如我们需要打破mlock的语义。
也要支持不同mem cg定义自己的水线,是否参与这个过程,也要结合mem cg的优先级。在回收模型上,我们考虑了周期性一刀切模型,梯度回收模型,也讨论了复用已有mem cg参数的可能。内核回收模块也经历了“target machine”的设计和“target mem cg”的设计,一切都在有序的开展。在这个过程中,也顺带发现了不少内核的bug:比较典型的是page回写和jdb2导致的死锁问题;mem cg 读频繁导致个别业务超时问题;2018年11前夕为安全计,我们灰度了2000台一刀切模型,其中随机挑选的一批500台数据简单总结如下:
单机free变化 | iowait变化 | 磁盘读写变化 | major page fault变化 | 业务延迟变化 |
---|---|---|---|---|
增加110G | 不明显 | 不明显 | 略有增加 | 不明显 |
没感受到回收冷内存会给业务延时造成影响。
后续工作
这个项目还没有结束,还有想象空间,比如:
- 目前我们的版本还比较粗糙,因为idle page统计需要扫描整个RAM,所以实时性不好,用这个来计算working set还是不合适,我们需要一个实时的,准确的按照实际访问频率角度,或其他有意义的working set计算方式,目前我们依然采用anon+lock的当作used,file page都算做是free的。但如果我们站在另一个角度,使用一刀切模型,如果这个模型工作的很好,能留在RAM里的基本都是hot的,就可以算作是working set。
- 目前从现象上看冷内存回收会导致iowait突高,需要后续修复
- 目前我们只考虑了clean file page,anon page,dirty file page也可以考虑起来
- 存储计算分离有可能会使用tempfs,可能后续也需要考虑这种情况
- swap和冷内存回收如何相处?考虑anon,tempfs,aep或者optane之后呢?
小结
mem单机资源控制诞生大概两年的时间了,bad case 不少。有些能在单机端解决,比如尽早回收cold page,缓解单机压力。有些需要在scheduler端解决,比如简单的:单机available少的机器降权,也能避免个别单机mem压力大导致的问题。总之还有很多事情可以考虑和尝试,没有什么是一开始就设计好的,跌跌撞撞的前行吧。