在 Linux Virtualization – Chroot Jail 文章中,我们讨论了内核命名空间和进程监狱。要理解这篇文章,您可能不需要阅读前面的文章,但我强烈建议您在深入研究资源限制之前先阅读一遍。它应该对理解正在发生的事情有很大帮助。
什么是 cgroup?
cgroups(控制组的缩写)是 Linux 内核功能,用于限制、说明和隔离进程集合的资源使用(CPU、内存、磁盘 I/O、网络等)。
此功能最初由 Google 的 2 位工程师以“进程容器”的名称开发,但后来以“cgroups”的名称合并到 Linux 内核主线中。
为什么需要它?
cgroups 的设计目标之一是为许多不同的用例提供统一的接口,从控制单个进程(例如使用 nice)到整个操作系统级虚拟化。简单来说,cgroups 提供:
- 资源限制:组可以设置为不超过配置的内存限制,其中还包括文件系统缓存。
- 优先级——某些组可能会获得更大份额的 CPU 利用率或磁盘 I/O 吞吐量。
- 会计 –衡量一个组的资源使用情况,例如可以用于计费目的。
- 控制——冻结进程组、检查点和重新启动。
它们是如何直接或间接使用的?
控制组可以以多种方式使用:
- 通过手动访问 cgroup 虚拟文件系统。
- 通过使用 cgcreate、cgexec 和 cgclassify(来自 libcgroup)等工具即时创建和管理组。
- 通过“规则引擎守护进程”可以自动将某些用户、组或命令的进程移动到其配置中指定的 cgroups。
- 间接通过其他使用 cgroup 的软件,例如 Docker、Linux Containers (LXC) 虚拟化、libvirt、systemd、Open Grid Scheduler/Grid Engine 和 Google 的 lmctfy。
您可能会感到惊讶,但这个静默守护进程构成了您在线体验的重要组成部分,因为很多网站使用容器/虚拟化来托管多个服务器或网站,包括 NetFlix、heruko 和 reddit。
安装 cgroups:一些 Linux 版本预装了 cgroups。要检查它们是否已经安装/挂载,请检查以下输出:
$ mount | grep "^cgroup"
如果你看到文件挂载在 /sys/fs/cgroup/ 上,那么你可以直接跳到下一个主题跳过安装部分。
第二个命令安装 cgroup-tools,这使得控制和监视控制组更容易。我们将在本教程中使用相同的命令。我们将使用 iotop 实用程序来监控磁盘 I/O 速率。
$ sudo apt-get install cgroup-bin cgroup-lite libcgroup1 cgroup-lite
$ sudo apt-get install cgroup-tools
$ sudo apt-get install iotop
如果您安装了 cgroup,但在 /sys/fs/cgroup 上看不到它们安装,请使用这些命令,
$ mount -t tmpfs cgroup_root /sys/fs/cgroup
$ mkdir /sys/fs/cgroup/blkio
$ mount -t cgroup -o blkio none /sys/fs/cgroup/blkio
示例 1:我们将创建一个磁盘控制组,以便我们可以运行任何具有有限磁盘读/写可用量的进程。即我们想要限制由一个进程或一组进程完成的读取和写入。
步骤 1:要创建 cgroup,只需在 /sys/fs/cgroup 中创建一个目录,或者如果您有 cgroup-tools 设置,那么我们可以在子系统的适当目录中使用它们。内核会自动用设置文件节点填充 cgroup 的目录。不过,建议使用 cgroup-tools API,
# Switch to root for the rest of the commands
$ sudo su
$ cgcreate -g blkio:myapp OR mkdir /sys/fs/cgroup/blkio/myapp
该命令将在“blkio”系统下创建一个子组“myapp”。块 I/O (blkio) 子系统通过 cgroup 中的任务控制和监视对块设备上的 I/O 的访问。将值写入这些文件可提供对各种资源的受控访问。您可以通过运行命令 lscgroup 来检查您的组是否已创建,该命令会列出所有控制组。
$ lscgroup | grep blkio:/myapp
blkio:/myapp
重要提示:这些文件不是磁盘上的普通文件。这些是伪文件,内核直接使用它们来读取和修改配置。不要在文本编辑器中打开它们并尝试保存它们。始终使用“echo”命令写入它们。
在深入研究简单的东西之前,让我们看看新创建的组的目录结构。以下是本教程需要的一些重要文件,以了解 cgroup 的工作原理。 (最重要的在图像中突出显示)
第 2 步:我们创建 2 个终端并将它们一个放在另一个下面。成为两个终端的 root 用户。在顶部终端中,我们运行 iotop 实用程序来监视磁盘 I/O,
$ sudo su
$ iotop -o
在下面的终端上,我们使用“dd”命令创建了一个 512 MB 的临时文件
$ sudo su
$ dd if=/dev/zero of=~/test_if bs=1M count=512
在dd命令中,“if”代表输入文件,“of”是输出文件,“bs”是块大小,“count”是写入块的次数。命令完成后,创建 ~/temp_if大小为 512 MB。您可以在顶部终端窗口上看到实时 I/O 速率。
第 3 步:现在进行下一个实验,我们需要确保已将所有文件系统缓冲区刷新到磁盘并删除所有缓存,以免它们干扰我们的结果。
$ free -m
$ sync
$ echo 3 > /proc/sys/vm/drop_caches
$ free -m
现在,您应该会看到可用 RAM 的增加和缓存大小的减少。
第 3 步:现在要设置节流限制,我们使用以下命令。比如说,我们想为一个进程设置 5 MB 的读/写限制。从内核文档中,您会发现, blkio.throttle.read_bps_device 和 blkio.throttle.write_bps_device 接受格式的条目,
其中, major和minor是特定设备的值,我们希望对其进行速率限制。 rate_per_second是该组的进程可以达到的最大速率。
获取主要和次要数字很容易。我正在使用的机器只有一个磁盘 /dev/sda 因此运行命令 ls -l /dev/sda* 我可以获得主要和次要编号。
突出显示的值是我的 /dev/sda 磁盘的主要和次要编号。
现在,我们写入以下值以将读取速率限制为 5 Mb/sec
$ echo "8:0 5242880" > /sys/fs/cgroup/blkio/myapp/blkio.throttle.read_bps_device
$ cat /sys/fs/cgroup/blkip/myapp/blkio.throttle.read_bps_device
在运行受控进程之前,我们必须了解没有任何限制的读取速度。通过在底部终端中运行此命令来读取我们之前创建的文件。
$ dd if=~/test_if of=/dev/null
第五步:您可以在顶部终端中看到实时读取率。文件创建完成后,您还可以看到平均速率,这是由 dd 命令显示的。将数据刷新到磁盘并删除所有缓存,如前所示,以避免结果中出现任何歧义。
要在节流下运行此命令,我们使用 cgexec
$ cgexec -g blkio:/myapp dd if=~/test_if of=/dev/null
我们为 -g 参数提供 : 名称,在本例中它是“blkio:myapp”。顶级终端中的费率应该类似于此。
有趣的是,我们可以采用任何没有内置速率限制的应用程序,我们可以根据需要对其进行节流。
上图是在读取 2 个文件时绘制的,这些文件的进程属于同一个 cgroup,读取限制为 50 Mb/秒。正如您最初看到的那样,读取速率跃升至最大值,但是一旦第二次读取开始,它就达到了平衡状态,正如预期的那样,总共为 50MB/s。一旦对“file-abc”的读取结束,速率会再次跳跃以达到最大值。
您可以通过在 blkio.throttle文件中回显新值来实时更改速率。内核会自动更新配置。
示例 2:我们按照类似的步骤创建内存限制应用程序。由于大部分内容相同,我将跳过说明,直接跳转到命令。
第 1 步:我创建了一个简单的 c 程序,它在每次迭代中分配 1MB 并运行总共 50 次迭代,总共分配 50 MB。
// a simple c-program that allocates 1MB in each
// iteration and run for a total of 50 iterations,
// allocating a total of 50 MB
#include
#include
#include
int main(void)
{
int i;
char *p;
for (i = 0; i < 50; ++i)
{
// Allocate 1 MB each time.
if ((p = malloc(1<<20)) == NULL)
{
printf("Malloc failed at %d MB\n", i);
return 0;
}
memset(p, 0, (1<<20));
printf("Allocated %d to %d MB\n", i, i+1);
}
printf("Done!\n");
return 0;
}
$ sudo su # Switch to root for the rest of the commands
$ cgcreate -g memory:myapp_mem OR mkdir /sys/fs/cgroup/memory/myapp_mem
$ cd /sys/fs/cgroup/memory/myapp_mem
$ lscgroup # To check if the group was created successfully.
现在,可以从内核文档中获取内存节流配置的格式。(参考文献中的链接)
$ echo "5242880" > memory.limit_in_bytes
在运行代码之前,我们需要禁用交换。如果程序无法从 RAM 中获取内存(因为我们有限制),它将尝试在交换时分配内存,这在我们的情况下是不可取的。
$ sudo swapoff -a # Disable swap
交换状态必须类似于上面显示的状态。
$ gcc mem_limit.c -o mem_limit
首先,在没有任何内存限制的情况下运行代码,
$ ./mem_limit
现在,比较从受控 cgroup 中运行时的输出,
$ cgexec -g memory:myapp_mem /root/mem_limit
您可以从以下位置检查各种资源记帐信息,例如当前内存使用情况、使用的最大内存、内存限制等,
$ cat memory.usage_in_bytes
$ cat memory.max_usage_in_bytes
$ cat memory.limit_in_bytes
您可以探索更多参数,例如 memory.failcnt、memory.kmem.* 和 memory.kmem.tcp.*
您阅读的文档越多,您的理解就越好。
我们可以扩展此方法并创建受限制的应用程序。这种方法是很久以前创建的,但最近它已被广泛应用于众多应用中。虚拟机、容器等使用它来强制执行资源限制。
理解 cgroups 的目的是了解容器中的资源节流实际上是如何完成的。下一个要探索的主题是容器。我们将在下一篇文章中详细讨论。
参考:
- https://www.kernel.org/doc/Documentation/cgroup-v1/
- https://www.kernel.org/doc/Documentation/cgroup-v1/blkio-controller.txt
- https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/ch-Subsystems_and_Tunable_Parameters.html
- https://lwn.net/Articles/604609/
- https://help.ubuntu.com/lts/serverguide/cgroups.html
- https://www.youtube.com/watch?v=sK5i-N34im8 docker 团队非常好的演示。这个视频将作为这篇文章和下一篇文章之间的桥梁。我会在下一篇文章的开头再次提到它。它在理解命名空间和 cgroup 方面非常方便。
关于作者:
Pinkesh Badjatiya来自 IIIT 海得拉巴。他是个极客,拥有大量值得寻找的项目。他的项目工作可以在这里看到。如果您还想在这里展示您的博客,请参阅GBlog,了解 GeeksforGeeks 上的客座博客写作。