长文解析:作为容器底层技术的半壁江山, cgroup如何突破并发创建瓶颈?

简介: io_uring 作为一种新型高性能异步编程框架,代表着 Linux 内核未来的方向,当前仍处于快速发展中。阿里云联合 InfoQ 发起《io_uring 介绍及应用实践》的技术公开课,围绕 OpenAnolis 龙蜥社区 Anolis OS 8 全方位解析高性能存储场景。

IMG_2018.jpeg

写在前面

cgroup 作为容器底层技术的半壁江山,很多文章已经介绍并总结得很好了,关于 cgroup 是什么、有什么用以及一些相关概念,这些内容并不是本文的重点所以也将不再赘述。

友情提醒:以下内容默认读者已经初步了解 task、cgroup、subsys、hierarchy 是什么及它们之间的关系。

我们为啥关注 cgroup 控制平面性能?

云原生目前是云计算领域的重点发展方向,其中的函数计算场景中,函数执行的速度是重要的性能指标,要求能够快速、高并发地创建和销毁实例。在此场景下的隔离特性普遍都会涉及到大量 cgroup 的相关操作,而现有的cgroup框架设计并发性很差,或许在设计之初并未考虑到大规模的控制平面操作(例如:创建和销毁)。而随着技术的演进,大规模的控制平面操作场景逐渐增多,也促使我们开始更加关注控制平面的性能。

本文的阐述是基于4.19版本的内核源代码,旨在分析cgroup提供给用户的接口背后的实现原理,并基于实现原理给出一些用户态使用cgroup的建议,最后在文章的结尾分享了一些内核态优化的思路。

原理分析

IMG_2019.png

图一

IMG_2020.png

图二

以上两张图,是4.19版本的内核中cgroup中最主要的几个数据结构之间的连接关系和cgroup层次结构的连接关系。

cgroup:字面意思

cgroup_root:hierarchy

cgroup_subsys: 子系统,缩写的变量一般为ss

cgroup_subsys_state: 当指向某个subsys时,代表该subsys在某个cgroup中一个实体

css_set、cset_cgrp_link:用于建立task_struct和cgroup之间多对多的关系

这些数据结构抽象之后是这张图:

IMG_2021.png

图三

其实也很好理解,本质上cgroup框架要解决的是:一个cgroup管哪些task,一个task归哪些cgroup管的问题,在实现上可通过cset作为中介来建立这层关系。相比于task和cgroup直连,这种做法可以简化复杂的关系。这是因为在实际使用的场景中,task基本都以组为单位进行管理,对某一组task的资源管控方案都大概率是一致的。

对于cgroup的各类操作围绕着这三类实体展开:

  • 创建:在图二所示的树形结构中增加一个叶节点
  • 绑定:本质上是迁移,子进程被fork出来时连接父进程指向的cset,绑定即是从一个cset(如果不再有task指向则删除)迁移到了另一个cset(如果指向的是新的cgroup集合则新创建)
  • 删除:在图二所示的树形结构中删除一个不管控任何task的叶节点

对于cgroup的各类操作的访问控制也围绕这三类实体的展开:

  • task: cgroup_threadgroup_rwsem锁
  • cset: css_set_lock锁
  • cgroup: cgroup_mutex锁

具体的这三类锁有什么作用,将在优化思路里进行分析。

优化方案

问题出在哪?

问题在于三个锁上:cgroup_mutex、cgroup_threadgroup_rwsem、css_set_lock。

cgroup_mutex保护cgroup的整个层级结构。cgroup的层级结构是一个森林,我们需要用这个一个锁来保护整个森林。改变层级结构比如常见的mount、mkdir、rmdir就不必多说了,肯定是需要持有这个锁的;除此之外对cgroup的任何一个其他的操作也需要持有这个锁,比如attach task、以及其他的读或写cgroup提供的接口。同时,因为rmdir的操作是随时都有可能发生的,任何操作都需要与rmdir都互斥。

css_set_lock保护和css_set相关的一切操作。任意进程随时都有可能exit,导致某个css_set释放,从而影响css_set的哈希表。除此之外,对cgroup的绝大多数的操作也会涉及到css_set_lock,这是因为对cgroup的绝大多数的操作(创建除外)都会引起css_set的变化。

cgroup_threadgroup_rwsem保护和cgroup相关的线程组操作,现实中随时都有可能的fork和exit操作导致线程组发生变化。这里用读写锁的原因是,进程自身的行为可能包括改变线程组的组成和持有读锁,这是可以并行的;当进程attach的时候,需要一个稳定的线程组视图,此时如果进程在fork或者exit的话会导致线程组的改变,而attach又是可以以线程组为单位的,不可并行。这里用读写锁并不是说是真的在读什么或写什么,只是恰好符合读者并行,写者需与其他写者互斥这个特性而已。也就是说,fork、exec、exit之间可以并行,类似于读者;attach与其他的都互斥,类似于写者。

这三个锁会受到进程fork和exit的影响,并且也会导致对cgroup的任何操作之间几乎不可并行。笔者在对cgroup进行深入的研究前,觉得是最开始的设计者偷懒,使用如此大粒度的锁,直到把cgroup的框架摸索明白后才发现,临界区就是有这么大,各种会异步发生的事件都需要操作这些数据,所以这些锁被设计成这样也很合理。

这里试着对问题进行抽象,思考一下问题的本质在哪。

对于cgroup_mutex,问题本质是树形(节点是cgroup)结构的并发访问。

对于css_set_lock,问题其实是二部图(一边是css_set,一边是cgroup)结构的并发访问。

对于cgroup_threadgroup_rwsem,问题其实是集合(线程组作为集合的元素)结构的并发访问。

问题的定义已经清楚了,怎么解决呢?以我目前的能力,我没法解。

是的,分析了这么多给的结论是此题无解,或者说暂时无解,可以有的解法也会对cgroup的框架造成刮骨疗毒式的改动。这背后的风险、稳定性的影响、投入产出比的痛能不能承受的住,我给不出一个确定的结论。如果读者有什么想法,欢迎在留言区提出,一起交流。

虽然治本难治,但治标还是可以有点想法的。

用户态优化:减少cgroup操作

这个方案很好理解,提前把cgroup创建和配置好,等需要用的时候直接取就行。这个方案效果极好,简直是降维打击。这里贴一下实验数据,这里的测试模拟袋鼠容器启动时的创建与读写——

线程数循环次数优化后时间/s优化前时间/s
120000.092.18
102000.081.78
40500.121.89
200100.142.22

这个方案达到了90%以上的优化率,将本来需要创建配置后attach进程最后删除的情况变成了只需要attach,工作量少了,自然也就变快了。

但这个方案存在一些弊端。一方面,池子里不用的cgroup对于系统来讲依然是可见的,需要进行管理,因此会存在一定的负载;另一方面是数据残留问题,并不是所有的subsys都提供类似于clear的操作接口,如果对监控数据有要求的话cgroup就是用一次就废,需要对池子进行补充,增加控制逻辑的同时又出现了竞争,效果会打折扣。最后便是需要明确cgroup的层次结构,毕竟要提前创建和配置,如果对运行时的层次结构无法掌控的话,池子都没法建立。

减少cgroup数量

systemd在默认情况下会把大多数subsys都挂在独立的一个hierarchy下,如果业务的进程都需要受同一些subsys管控的话,可以把这些subsys都挂载在同一个hierarchy下,比如把cpu、memory、blkio挂载在一起。

这时候可能有同学要问了,原本在cpu、memory、blkio下各创建一个cgroup,和在cpu_memory_blkio下创建一个cgroup能有多少区别?该有的逻辑都得有,一个都跑不了,最多就是少了几个cgroup自身这个结构体,能有多少区别?

这里要回归到最开始的场景,cgroup的问题出在场景是高并发,而本质上各类操作却是串行的。我们知道,衡量性能有主要的两个维度:吞吐和延迟。cgroup本质的串行无法直接提高吞吐,各个subsys独立在hierarchy下等于是被拆解成子任务,反而提高了延迟。

下面是测试数据:

线程数循环次数优化后时间/s优化前时间/s
120000.992.18
102000.891.78
40500.981.89
200101.002.22

内核态优化

对上述三把锁动不了,只能对临界区内的那部分内容下手了。想要缩小临界区,那就需要找出临界区内耗时的部分进行优化。

下图是各个子系统创建cgroup时各个部分的耗时:

IMG_2022.png

image.gif

这里简单解释下各个部分做了些什么:

  • cgroup:创建和初始化cgroup结构体
  • kernfs:创建cgroup的目录
  • populoate:创建cgroup控制用的文件接口
  • cssalloc:分配css
  • cssonline:css在各个子系统中的online逻辑
  • csspopulate:创建子系统控制用的文件接口

从图中可以发现cpu、cpuacct、memory的耗时相对于其他的子系统延迟高很多,其中css alloc和css populate占大头。下面我们将研究一下这个“主要矛盾“究竟在做些什么。

通过分析我们发现,css alloc上延迟高是因为给一些percpu的成员分配内存,这一过程比较耗时。css populate上是因为部分子系统的接口文件比较多,需要依次一个个地创建从而消耗更多的时间。

分析过后发现,这些逻辑都是必须没有冗余,怎么优化?做缓存呗。percpu成员变量记录下地址不释放下次重复使用,子系统接口文件在释放时以文件夹为单位移到一个指定的地方,需要时再移回来,只涉及目录文件上一个目录项的读写,开销低且是常数。

通过两种方式,各个创建cgroup的延时优化结果如下:

IMG_2023.png

cpu子系统css alloc部分依然比较耗时的原因在于初始化操作比较多,但相比于原先的160us,延时已经降到了50us。

缩小临界区后虽然并不能对并发度有什么影响,但至少延迟降下来了,下面是测试数据。

t个线程并发,每个线程在cpu、cpuacct、cpuset、memory、blkio下创建n个cgroup:

tn使用缓存平均时间不使用缓存平均时间优化率
201000.23s0.71s67.6%
2010002.66s8.12s67.3%
200100.18s0.54s66.7%
2001002.00s7.30s72.4%

tn使用缓存长尾时间不使用缓存长尾时间优化率
201000.29s0.88s67.1%
2010003.25s9.37s65.3%
200100.32s0.94s66.0%
2001003.40s9.53s64.3%

一些假想

如果无视各种限制因素,抛弃现有的框架,不考虑向下兼容,实现一个用于管控进程资源且支持高并发的框架,可以怎么设计?

现在cgroup的机制提供了相当高的灵活性,子系统之间的关系可以随意绑定,task可以随意绑在任意一个cgroup上,如果牺牲一下这些灵活性,对问题的解释是不是就可以变得简单点,下面谈谈我的几个想法。

第一,前文提到的为了减少cgroup数量,把所有的子系统都绑定在一起的想法,是否可以固化在内核当中,或者说不提供子系统独立挂载和绑定挂载的特性?这样,进程组与cgroup变成了一一对应的关系,cset就没有了存在的意义,css_set_lock带来的问题也不攻自破。但是对应的弊端是,一个进程组内的所有进程在每个子系统上资源控制都是一致的。

第二,cgroup层级结构是否有存在的必要?现在cgroup以树形结构组织,确实在逻辑上更加符合现实。比如,在第一层给业务分配总资源,在第二层给业务的各个组件分配资源。但在操作系统分配资源的视角上,以及业务进程具体获得资源的视角上,第一层的存在并没什么作用,只是给用户提供了逻辑更清晰的运维管理。如果把cgroup v2提出的no internal process特性也应用上,可以把cgroup层级扁平化到只有一层。

cgroup只有一层的好处是,可以很方便地把cgroup_mutex粒度细化,细化到每个cgroup一把锁,不会存在好几层的树形结构——改动一个cgroup需要从祖先开始持锁的问题。锁的粒度细化后,在并发启动容器实例的时候,因为对应不同的cgroup,也就不会存在竞争的问题。

第三,cgroup的删除能否加以限制?现在是用户异步手动删除空的cgroup,如果可以在cgroup不再管理进程(exit,move)时隐藏,后续找个时机触发删除,便可以少一个竞争场景。这种方法会造成空的cgroup没法再利用,现在有对空cgroup再利用的需求吗?

最后,绑定进程能否加以限制?task绑定cgroup的本质是移动,从一个cgroup到另一个cgroup。cgroup_mutex粒度细化后会存在ABBA的死锁问题。有一个问题是,task存在绑定到一个cgroup后再绑定的需求吗?理想情况是绑定一个后顺利运行然后退出。基于这种假设就可以做一个限制,只允许task在绑定时,src与dst内必须包含default cgroup、default cgroup起一个跳板作用。

上面这些都是我一些不成熟的想法,欢迎讨论。

原文链接
本文为阿里云原创内容,未经允许不得转载。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/512711.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Orion:谷歌的新一代SDN控制器

作者 | 魏煌松来源 | 鲜枣课堂时至今日,谷歌在2015年公布的成果,“利用SDN将广域网带宽利用率提升至接近100%”,仍然是SDN的一个标杆案列,也是难以逾越的巅峰。但事实上,当时使用的SDN控制器Onix,早已退出了…

移动云正式发布基于龙蜥 Anolis OS 的 BC-Linux V8.2 通用版操作系统

简介: 2020年12月CentOS项目组宣布CentOS 8将于2021年12月31日结束支持,这意味着从2022年开始,使用CentOS 8的用户,将无法得到来自官方的新硬件支持、bug修复和安全补丁。针对这一情况,移动云大云操作系统团队基于国内…

干掉讨厌的 CPU 限流,让容器跑得更快

简介: 让人讨厌的 CPU 限流影响容器运行,有时人们不得不牺牲容器部署密度来避免 CPU 限流出现。本文介绍的 CPU Burst 技术可以帮助您既能保证容器运行服务质量,又不降低容器部署密度。文章分为上下两篇,该文为上篇,下…

微弱信号检测_机动车检测线常用传感器介绍

机动车检测线中经常会运用到各种传感器,这些传感器相当于车辆检测系统的“眼睛”、“鼻子”和“耳朵”,通过台体装置和装在台体中的传感器,能够把车辆的性能数据转换成计算机系统能够识别的信号,供计算机处理和计算,最…

赋能开发者,英特尔发布oneAPI 2022工具包

英特尔发布了oneAPI 2022工具包。此次发布的最新增强版工具包扩展了跨架构开发的特性,为开发者提供更强的实用性和更丰富的架构选择,用以加速计算。 英特尔公司首席技术官、高级副总裁、软件和先进技术事业部总经理 Greg Lavender表示:“我十…

Quick BI V4.0功能“炸弹”来袭,重磅推出即席分析、模板市场、企业微信免密登录等强势功能

简介: 2021年7月,Quick BI公共云版本迭代新功能:重磅推出即席分析、模板市场,分析门槛再降低;推出企业微信无缝对接,移动端类目个性配置及管理提升多端能力;数据建模配置交互升级至拖拽模式提升…

打印速度快点的打印机_瞒着领导偷偷给你们发两台打印机

前几次小粉笔组织的活动都被“投诉”!说我们打印机太少~小粉笔心领神会,在这个月的活动预算费用上悄咪咪加了【两台打印机】~(看小粉笔多疼你们!)希望知道的笔芯不要把这条推文转发给我领导(要不然你们以后就没有打印机了~哼!)现在…

数据库误操作后悔药来了:AnalyticDB PostgreSQL教你实现分布式一致性备份恢复

简介: 本文将介绍AnalyticDB PostgreSQL版备份恢复的原理与使用方法。 一、背景 AnalyticDB PostgreSQL版(简称ADB PG)是阿里云数据库团队基于PostgreSQL内核(简称PG)打造的一款云原生数据仓库产品。在数据实时交互式…

与变异风险词赛跑 阿里探索AI治理网络风险

最近,阿里安全一线风控小二可粒发现,在禁售的风险防控库里,有人试图“上新”新品种,不法份子借助在社交媒体上走红的“魔法改运”等说辞,引人入玄学骗局。 尽量提前发现风险问题,提早布防是阿里安全风控部…

高效研发运维体系构建的流程和方法论

简介: 云计算产品大多都会与云原生发生关联,云原生正在重塑整个软件的生命周期。但到底什么是云原生?云原生带来的最大技术创新和未来机会是什么?围绕云原生,是否可以构建出一套云上的开发&运维体系,打…

Colima:MacOS 上的极简容器运行时和 Kubernetes

作者 | Addo Zhang来源 | 云原生指北Colima 是一个以最小化设置来在MacOS上运行容器运行时和 Kubernetes 的工具。支持 m1,同样也支持 Linux。Colima 的名字取自 Container on Lima。Lima 是一个虚拟机工具,可以实现自动的文件共享、端口转发以及 contai…

当容器应用越发广泛,我们又该如何监测容器?

简介: 随着容器技术蓬勃发展与落地推行,越来越多企业的业务运行于容器中。作为主流部署方式之一,容器将团队的任务和关注点分割开,开发团队只需关注应用程序逻辑和依赖项,而运维团队只需关注部署和管理,无需…

内含福利|CSDN携手字节跳动:云原生Meetup北京站报名热烈启动,1月8日见!

伴随云原生技术的成熟与落地,越来越多框架、中间件等开源项目相继涌现,帮助开发者和企业有效解决业务问题。2022年1月8日,CSDN携手字节跳动基础架构,将在北京举办第四场云原生线下Meetup。在这里,您可以与众多开源技术…

Flink CDC 2.0 正式发布,详解核心改进

简介: 本文由社区志愿者陈政羽整理,内容来源自阿里巴巴高级开发工程师徐榜江 (雪尽) 7 月 10 日在北京站 Flink Meetup 分享的《详解 Flink-CDC》。深入讲解了最新发布的 Flink CDC 2.0.0 版本带来的核心特性,包括:全量数据的并发…

unity三维地图的经纬度如何在二维地图上表示_接入C++版本recastnavigation寻路库到Unity/服务端中...

前言因为Unity版本的更新迭代,老版本的A*插件在新版本Unity已经无法正常使用,包括一些运行时代码也已经过时,重新接入要花费很多时间,干脆接入一个新的寻路方案吧。这里选择的是久负盛名的https://github.com/recastnavigation/re…

Dataphin功能:集成——如何将业务系统的数据抽取汇聚到数据中台

简介: 数据集成是简单高效的数据同步平台,致力于提供具有强大的数据预处理能力、丰富的异构数据源之间数据高速稳定的同步能力,为数据中台的建设打好坚实的数据基座。 数据中台是当下大数据领域最前沿的数据建设体系, 它并不是从零开始, 无中…

5G专网,路在何方?

作者 | 蜉蝣采采来源 | 无线深海话说你平常打电话、刷视频、玩游戏的4G和5G,一般也被叫做“公网”。这个“公”字的含义正是公开,公用的意思。也就是说,这个网络,不但你能用,你隔壁的张三也能用,张三的老乡…

如何开发 Node.js Native Add-on?

简介: 来一起为 Node.js 的 add-on 生态做贡献吧~ 作者 | 吴成忠(昭朗)这篇文章是由 Chengzhong Wu (legendecas),Gabriel Schulhof (gabrielschulhof) ,Jim Schlight (jimschlight),Kevin Eady,Michael Dawson (mhdaw…

xxl子任务_XXL-JOB v2.1.2 发布,分布式任务调度平台

v2.1.2 Release Notes1、方法任务支持:由原来基于JobHandler类任务开发方式,优化为支持基于方法的任务开发方式;因此,可以支持单个类中开发多个任务方法,进行类复用XxlJob("demoJobHandler")public ReturnT …

程序员如何在业余时间提升自己?

简介: 在自省过程中,我们经常会问自己这么几个问题,这段时间我尝试了什么新事物、有了什么变化、得到什么成果。 近年来,出现越来越多“自主学习”、“业余提升" 的相关话题。 我们经常收到一些同学提问:程序员…