Docker核心原理解读-cgroups

2,432 阅读6分钟

cgroups

1.control groups,可以根据需求把一系列系统任务整合到按资源划分等级的不同组里(可以限制、记录任务组所使用的物理资源,CPU、Memory、IO等)。它不仅可以限制被namespace隔离起来的资源,还可以为资源设置权重、计算使用量、操控任务(进程或线程)启停等。本质上来说,cgroups是内核附加在程序上的一系列钩子,通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。

2.特点:
  • cgroup的api是以一个为文件系统的方式实现,用户态的程序可以通过文件操作实现cgroup的组织管理
  • 组织管理操作单元可以细粒度到线程级别,可以创建和销毁cgroup从而实现资源再分配和管理
  • 所有资源管理的功能都以子系统的方式实现,接口统一
  • 子任务创建之初与父任务处于同一个cgroups控制组

3.cgroups提供四大功能:
  • 资源限制(Resource Limitation):cgroups可以对任务使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出OOM(Out of Memory)。
  • 优先级分配(Prioritization):通过分配的CPU时间片数量及硬盘IO带宽大小,实际上就相当于控制了进程运行的优先级。
  • 资源统计(Accounting): cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等等,这个功能非常适用于计费。
  • 进程控制(Control):cgroups可以对进程组执行挂起、恢复等操作。
4.cgroups术语表
  • task(任务):cgroups的术语中,task就表示系统的一个进程或线程。
  • cgroup(控制组):cgroups 中的资源控制都以cgroup为单位实现。cgroup表示按某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。
  • subsystem(子系统):cgroups中的subsystem就是一个资源调度控制器(Resource Controller)。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。
  • hierarchy(层级树):hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy。
5.cgroups、任务、子系统、层级之间的关系和基本规则:
  • 规则1:同一个层级可以附加一个或多个子系统
  • 一个子系统可以附加到多个层级,当且仅当目标层级只有唯一一个子系统时。图中小圈中的数字表示子系统附加的时间顺序,CPU子系统附加到层级A的同时不能再附加到层级B,因为层级B已经附加了内存子系统。
  • 系统每次新建一个层级时,该系统上的所有任务默认加入这个新建层级的初始化cgroup,这个sgroup也就是root cgroup。对于创建的每个层级,任务只能存在于其中一个cgroup中任务只能存在于其中一个cgroup中,即一个任务不能存在于同一个层级的不同cgroup中,但一个任务可以存在于不同层级中的多个cgroup中。如果操作时把一个任务添加到同一个层级的另一个cgroup中,则会将它从第一个cgroup中移除。
  • 任务在fork/clone自身时创建的子任务默认与原任务在同一个cgroup中,但是子任务允许被移动到不同的cgroup中。即fork/clone完成后,父子任务间在cgroup方面是互不影响的。
6.子系统简介
实际上就是cgroups的资源控制系统,每种子系统独立的控制一种资源,目前Docker使用如下9种子系统,具体如下:
  • blkio:为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。
  • cpu:使用调度程序控制任务对CPU的使用。
  • cpuacct:自动生成cgroup中任务对CPU资源使用情况的报告。
  • cpuset:可以为cgroup中的任务分配独立的CPU和内存。
  • devices:可以开启或关闭cgroup中任务对设备的访问。
  • freezer:可以挂起或恢复cgroup中的任务。
  • memory:可以设定cgroup中任务对内存使用量的限定,并且自动生成这些任务对内存资源使用情况的报告。
  • perfevent:使用后使得cgroup中的任务可以进行统一的性能测试。
  • net_cls:Docker没有直接使用,它通过使用等级识别符(classid)标记网络数据包,从而允许Linux流量控制程序(TC:Traffic Controller)识别从具体cgroup中生成的数据包。 
7.cgroups的实现本质上是给任务挂上钩子,当任务运行的过程中涉及某种资源时,就会触发钩子上所附带的子系统进行检测,然后根据资源类别的不同使用对应的技术进行资源限制和优先级分配。
  • 判断资源超出限额:对于不同的系统资源,cgroups提供了统一的接口对资源进行控制和统计,但限制的具体方式则不尽相同。如memory子系统,会在描述内存状态的“mm_struct”结构体中记录它所属的cgroup,当进程需要申请更多内存时,就会触发cgroup用量检测,用量超过cgroup规定的限额,则拒绝用户的内存申请,否则就给予相应内存并在cgroup的统计信息中记录。
  • 实现上,cgroup与任务之间是多对多的关系,因此它们并不直接关联,而是通过一个中间结构把双向的关联信息记录起来。每个任务结构体task_struct都包含了一个指针,可以查询到对应cgroup的情况,同时也可以查询到各个子系统的状态,这些子系统状态中也包含了找到任务的指针,不同类型的子系统按需定义本身的控制信息结构体,最终在自定义的结构体中把子系统状态指针包含进去,然后内核通过container_of等宏定义来获取对应的结构体,关联到任务,以此达到资源限制的目的。