cgroups隔离与限制

cgroups介绍

cgroups(Control Groups) 是linux 内核提供的一种为系统资源进程有效隔离和限制的一种手段。它可以有效的限制资源使用的cpu,内存及物理设备,如:磁盘等做精准限制。防止某一进程资源占用过大,导致整个系统物理资源耗尽,影响其他进程的运行。

cgroups子系统

cgroups为每种可以控制的资源定义了一个子系统。典型的子系统介绍如下:

  • cpu 子系统,主要限制进程的 cpu 使用率。
  • cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  • cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  • memory 子系统,可以限制进程的 memory 使用量。
  • blkio 子系统,可以限制进程的块设备 io。
  • devices 子系统,可以控制进程能够访问某些设备。
  • net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  • freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
    ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。

这里面每一个子系统都需要与内核的其他模块配合来完成资源的控制,比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的;对内存资源的限制则是内存模块根据 memory 子系统的配置来完成的,而对网络数据包的控制则需要 Traffic Control 子系统来配合完成。

cgroup 将一组任务与一组参数相关联或更多子系统。

子系统是一个利用任务分组的模块cgroups 提供的设施来处理任务组具体方式。一个子系统通常是一个“资源控制器”,它调度资源或应用每个 cgroup 限制,但它可能是任何想要对一组进程采取行动的东西,例如一种虚拟化子系统。

一个 hierarchy 是一组排列在树中的 cgroups,使得系统中的每一个任务都在其中的一个 cgroup 中层次结构和一组子系统;每个子系统都有系统特定的状态附加到层次结构中的每个 cgroup。每个层次都有与之关联的 cgroup 虚拟文件系统的实例。

cgroups 层级结构(Hierarchy)

内核使用 cgroup 结构体来表示一个 control group 对某一个或者某几个 cgroups 子系统的资源限制。cgroup 结构体可以组织成一颗树的形式,每一棵cgroup 结构体组成的树称之为一个 cgroups 层级结构。cgroups层级结构可以 attach 一个或者几个 cgroups 子系统,当前层级结构可以对其 attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 attach 到一个 cpu 层级结构中。

比如上图表示两个cgroups层级结构,每一个层级结构中是一颗树形结构,树的每一个节点是一个 cgroup 结构体(比如cpu_cgrp, memory_cgrp)。第一个 cgroups 层级结构 attach 了 cpu 子系统和 cpuacct 子系统, 当前 cgroups 层级结构中的 cgroup 结构体就可以对 cpu 的资源进行限制,并且对进程的 cpu 使用情况进行统计。 第二个 cgroups 层级结构 attach 了 memory 子系统,当前 cgroups 层级结构中的 cgroup 结构体就可以对 memory 的资源进行限制。

在每一个 cgroups 层级结构中,每一个节点(cgroup 结构体)可以设置对资源不同的限制权重。比如上图中 cgrp1 组中的进程可以使用60%的 cpu 时间片,而 cgrp2 组中的进程可以使用20%的 cpu 时间片。

未使用cgroup限制后果

以下所有操作都是在虚拟机 centos8里面进行操作实验

我们在终端运行一下死循环,并top查看。

1
2
[root@localhost ~]# while : ; do : ;done
[1] 10179

牢记该进程id,下面进行进程隔离是需要用到。也可通过ps查看。

此时cpu爆满,严重影响其他进程。

我们如何将该进程进行有效的资源限制来保护我们系统上的其他资源呢?请看下面操作

linux 进程隔离

1
2
[root@localhost ~]# cd /sys/fs/cgroups/cpu
[root@localhost cpu]# mkdir my-app

如图:

查看我们新建的my-app 控制组限制:

1
2
3
4
[root@localhost cpu]# cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us 
-1
[root@localhost cpu]# cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
100000

my-app 控制组里的 CPU quota 还没有任何限制(即:-1),CPU period 则是默认的 100 ms(100000 us)。

接下来,我们可以通过修改这些文件的内容来设置限制。比如,向 my-app 组里的 cfs_quota 文件写入 20 ms(20000 us):

1
[root@localhost cpu]# echo 20000 > /sys/fs/cgroup/cpu/my-app/cpu.cfs_quota_us

它意味着在每 100 ms 的时间里,被该控制组限制的进程只能使用 20 ms 的 CPU 时间,也就是说这个进程只能使用到 20% 的 CPU 带宽。

接下来,我们把被限制的进程的 PID 写入 my-app 组里的 tasks 文件,上面的设置就会对该进程生效了:

1
[root@localhost cpu]# echo 10179 > /sys/fs/cgroup/cpu/my-app/tasks 

此时我们在通过top 指令查看资源消耗:

docker 容器中的隔离使用

1
docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu bin/bash
  • –cpu-period cpu运行时间
  • –cpu-quota 分配到到cpu运行时间

上面到示例意味者 运行 100ms,只能分配实际运行时间为 20ms,也就意味只占总cpu的20%。

运行容器之后,在交互洁面输入下面代码测试:

1
[root@localhost ~]# while : ; do : ;done

此时查看top命令,cpu 只占到了 20%,达到了有效的限制。

删除限制组

1
rmdir my-app

参考:

Search by:GoogleBingBaidu