Docker核心原理之cgroups

cgroups资源限制

上一篇文章中,我们了解了Docker的资源隔离技术namespace,通过系统调用构建了一个相对隔离的shell环境。也可以称之为一个简单的容器。接下来将讲解另一个强大的内核工具-cgroups。它不仅可以限制被namespace隔离起来的资源,还可以为资源设置权重、计算使用量、操控任务(进程或线程)启停等。

1.cgroups是什么

cgroups顾名思义就是把任务放到一个组里面统一加以控制。官方的定义如下:

cgroups是Linux内核提供的一种机制,这种机制可以根据需求吧一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组中,从而为系统资源管理提供一个统一的框架。

通俗地说,cgroups可以限制,记录任务组做使用的无力资源(包括CPU、Memory、IO等),为容器实现虚拟化提供了保证,是构建Docker等一系列虚拟化管理工具的基石。

对开发者来说,cgroups有以下4个特点:

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

本质上来说,cgroups是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的。

2.cgroups的作用

实现cgroups的主要目的是为了不同的用户层面的资源管理,提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,cgroups提供了以下四大功能:

  • 资源限制:cgroups可以对任务使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出OOM(Out Of Memory)提示。
  • 优先级分配:通过分配的CPU时间片数量及磁盘IO宽带大小,实际上就相当于控制了任务运行的优先级。
  • 资源统计:cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等,这个功能非常适用于计费。
  • 任务控制:cgroups可以对任务执行挂起、恢复等操作。

3.cgroups术语表

  • task(任务):在cgroups的术语中,任务表示一个进程或线程。
  • cgroup(控制组):cgroups中的资源控制都以cgroup为单位实现。cgroup表示按某种资源控制标准划分而成的控制组,包含了一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。
  • subsystem(子系统):cgroups中的子系统就是一个资源调度控制器。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。
  • hiererchy(层级):层级由一系列cgroup以一个树状结构排列而成,每个层级通过绑定对应的子系统进行资源控制。层级中的cgroup几点可以包括零或多个这几点,子节点继承父节点挂载的子系统。整个操作系统可以有多个层级。

4.组织结构与基本规则

上一篇文章已经介绍过,传统的Unix任务管理,实际上是先启动init任务作为根基点,再由init节点创建子任务作为子节点,而每个子节点又可以创建新的子节点,如此往复,形成一个树状结构。而系统中的多个cgroup也构成类似的树状结构,子节点从父节点继承属性。
它们最大的不同在于,系统中的多个cgroup构成的层级并非单根结构,可以允许存在多个。如果任务模型由init作为根节点构成一棵树,那么系统中的多个cgroup则是由多个cgroup则是由多个层级构成的森林。这样做的目的很好理解,如果只有一个层级,那么所有的任务都将被迫绑定其上的所有子系统,这会给某些任务造成不必要的限制。在Docker中,每个子系统独自构成一个层级,这样做非常易于管理。

5.子系统简介

子系统实际上就是cgroups的资源控制系统,每个子系统独立地控制一种资源,目前Docker使用了如下9种子系统,其中net_cls子系统在内核中已经广泛实现,但在Docker中尚未使用,Docker在网络方案的控制方式在以后的文章中会继续介绍。

  • blkio: 可以为块设备设定输入/输出限制,比如物理驱动设备(包括磁盘、固态硬盘、USB等)。
  • cpu:使用调度程序控制任务对CPU的使用。
  • cpuacct:自动生成cgroup中任务对CPU资源使用的情况的报告。
  • cpuset:可以为cgroup中的任务分配独立的CPU(针对多处理器系统)和内存。
  • devices:可以启动或关闭cgroup中任务对设备的访问。
  • freezer:可以挂起或恢复cgroup中的任务。
  • memory:可以设定cgroup中任务对内存的使用量的限定,并且自动生成这些任务对内存资源使用情况的报告。
  • perf_event:使用后使cgroup中的任务可以进行统一的性能测试。(Pref:linux CPU性能探测器)
  • net_cls:Docker没有直接使用它,它通过使用等级识别符(classid)标记网络数据包,从而允许Linux流量控制程序(Traffic Controller,TC)识别从具体cgroup中生成的数据包。

上述子系统如何使用虽然很重要,但是Docker并没有对cgroup本身做增强,容器用户一般也不需要直接操作cgroup。这里我只是大致说明下操作流程,让读者有一个感性的认识。
Linux中cgroup的实现形式表现为一个文件系统,因此需要mount这个文件系统才能使用,挂载成功后就能看到各类子系统。

6.cgroups实现方式及工作原理

在对cgroups规则和自通有一定了解以后,下面简单介绍操作系统内核级别上cgroups的工作原理,希望能有助于读者理解cgroups如何对Docker容器中的进程产生作用。
cgroups的实现本质上是给任务挂上钩子,当任务运行的过程中涉及某种资源时,就会触发钩子上所附带的子系统进行检测,根据资源列别不同,使用对应的技术进行资源限制和优先级分配。

  • cgroups如何判断资源超限及超出限额后的措施

对于不同的系统资源,cgroups提供了统一的接口对资源进行控制和统计,但限制的具体方式则不尽相同。比如memorary子系统,会描述内存状态的“mm_struct”结构体中记录它所属的cgroup,当进程需要申请更多内存时,就会触发cgroup用量检测,用量超过cgroup规定的限额,则拒绝用户的内存申请,否则就给予相应内存并在cgroup的统计信息中记录。实际实现要比上述描述复杂的多,不仅需要考虑内存的分配和回收,还需要考虑不同类型的内存如cache和swap等。
进程所需的内存超过它所属的cgroup最大限额以后,如果设置了OOM Control(内存超限控制),那么进程就会收到OOM信号并结束;否则进程就会被挂起,进入睡眠状态,进入睡眠状态,直到cgroup中其他进程释放了足够的内存资源为止。Docker中默认是开启OOM Control的。其他子系统的实现与此类似,cgroups提供了多种资源限制的策略供用户选择。

  • cgroup与任务之间的关联关系

实现上,cgroup与任务之间是多对多关系,所以它们并不直接关联,而是通过一个中间结构把双向的关联信息记录起来。每个任务结构体tsak_struct中都包含了一个指针,可以查到对应的cgroup的情况,同时也可以查询到各个子系统的状态,这些子系统状态中也包含了找到任务的指针,不同类型的子系统按需定义本身的控制信息结构体,最终在地定义的结构体中吧子系统状态指针包含进去,然后内核通过container_of(这个宏可以通过一个结构体的成员找到结构体自身)等宏定义来获取对应的结构体,关联到任务,从此达到资源限制的目的。同时,为了让cgroups便于用户理解和使用,也为了用精简的内核代买为cgroup提供熟悉的权限和命名空间管理,内核开发者们按照Linux虚拟文件转化器(Virtual Filesystem Switch,VFS)接口实现了一套名为cgroup的文件系统,非常巧的用来表示cgroups的层级概念,把各个子系统的实现都疯撞到文件系统的各项操作中。

  • Docker在使用cgroup时的注意事项

在实际使用过程中,Docker需要通过挂载cgroup文件系统新建一个层级结构,挂载时指定要绑定的子系统。把cgroup文件系统挂载上以后,就可以像操作文件一样对cgroup的层级进行浏览和操作管理(包括权限管理、子文件管理等)。除了cgroup文件系统以外,内核没有为cgroups的访问和操作添加任何系统调用。

  • /sys/fs/cgroup/cpu/docker/下文件的作用

前面已经说过,以资源开头的文件都是用来限制这个cgroup下任务的可用配置文件。
一个cgroup创建完成,不管绑定了何种子系统,其目录下都会生成以下几个文件,用来描述cgroup的相应信息。同样,把相应信息写入这些配置文件中就可以生效。
本文由浅入深的讲解了cgroups,从cgroups是什么,到cgroups要怎么用,最后对大量的cgroup子系统进行了讲解。可以看到内核对cgroups的支持已经较多,但是依旧有许多工作要完善。如网络方面目前通过TC(Traffic Controller)来控制,未来需要统一整合;由县级调度方面依旧有很大的改进空间。

你可能感兴趣的:(Linux内核,docker)