我们知道使用不同的 Namespace,可以实现容器中的进程看不到别的容器的资源,但是有一个问题你是否注意到?

容器内的进程仍然可以任意地使用主机的 CPU 、内存等资源,如果某一个容器使用的主机资源过多,可能导致主机的资源竞争,进而影响业务。那如果我们想限制一个容器资源的使用(如 CPU、内存等)应该如何做呢?

一、cgroups 由来

cgroups(全称:control groups)是 Linux 内核的一个功能,它可以实现限制进程或者进程组的资源(如 CPU、内存、磁盘 IO 等)。

在 2006 年,Google 的工程师( Rohit Seth 和 Paul Menage 为主要发起人) 发起了这个项目,起初项目名称并不是cgroups,而被称为进程容器(process containers)。在 2007 年cgroups代码计划合入Linux 内核,但是当时在 Linux 内核中,容器(container)这个词被广泛使用,并且拥有不同的含义。为了避免命名混乱和歧义,进程容器被重名为cgroups,并在 2008 年成功合入 Linux 2.6.24 版本中。cgroups目前已经成为 systemd、Docker、Linux Containers(LXC) 等技术的基础。

二、cgroups 功能及核心概念

2.1 功能

容器cce和ref 容器cgroup机制_容器cce和ref

2.2 核心概念

容器cce和ref 容器cgroup机制_docker_02

  1. 子系统(subsystem):是一个内核的组件,一个子系统代表一类资源调度控制器。例如内存子系统可以限制内存的使用量,CPU 子系统可以限制 CPU 的使用时间。
  2. 控制组(cgroup):表示一组进程和一组带有参数的子系统的关联关系。例如,一个进程使用了 CPU 子系统来限制 CPU 的使用时间,则这个进程和 CPU 子系统的关联关系称为控制组。
  3. 层级树(hierarchy):是由一系列的控制组按照树状结构排列组成的。这种排列方式可以使得控制组拥有父子关系,子控制组默认拥有父控制组的属性,也就是子控制组会继承于父控制组。比如,系统中定义了一个控制组 c1,限制了 CPU 可以使用 1 核,然后另外一个控制组 c2 想实现既限制 CPU 使用 1 核,同时限制内存使用 2G,那么 c2 就可以直接继承 c1,无须重复定义 CPU 限制。

cgroups 的三个核心概念中,子系统是最核心的概念,因为子系统是真正实现某类资源的限制的基础。

三、cgroups 子系统实例

mount 命令查看一下当前系统已经挂载的cgroups信息:

]# mount -t cgroup

操作系统版本和内核版本如下

不同内核版本cgroups子系统和使用方式可能略有差异

容器cce和ref 容器cgroup机制_docker_03

容器cce和ref 容器cgroup机制_容器cce和ref_04

以cpu子系统为例子

 第一步:在 cpu 子系统下创建 cgroup

在相应的子系统下创建目录

# mkdir /sys/fs/cgroup/cpu/mydocker

容器cce和ref 容器cgroup机制_关联关系_05

新建的目录下被自动创建了很多文件,其中 cpu.cfs_quota_us 文件代表在某一个阶段限制的 CPU 时间总量,单位为微秒

例如,我们想限制某个进程最多使用 1 核 CPU,就在这个文件里写入 100000(100000 代表限制 1 个核) ,tasks 文件中写入进程的 ID 即可(如果要限制多个进程 ID,在 tasks 文件中用换行符分隔即可)。

此时,我们所需要的 cgroup 就创建好了

第二步:创建进程,加入 cgroup

把当前运行的 shell 进程加入 cgroup,然后在当前 shell 运行 cpu 耗时任务(这里利用到了继承,子进程会继承父进程的 cgroup)。

将 shell 进程加入 cgroup 中

cd /sys/fs/cgroup/cpu/mydocker
 echo $$ > tasks

 

容器cce和ref 容器cgroup机制_容器cce和ref_06

 

查看一下 tasks 文件内容: 

其中第一个进程 ID 为当前 shell 的主进程,也就是说,当前 shell 主进程为 348

容器cce和ref 容器cgroup机制_容器cce和ref_07

第三步:执行 CPU 耗时任务,验证 cgroup 是否可以限制 cpu 使用时间

使用以下命令制造一个死循环,来提升 cpu 使用率: