📜  Linux 虚拟化:使用 cgroups 进行资源限制

📅  最后修改于: 2021-10-21 05:41:08             🧑  作者: Mango

在 Linux Virtualization – Chroot Jail 文章中,我们讨论了内核命名空间和进程监狱。要理解这篇文章,您可能不需要阅读前面的文章,但我强烈建议您在深入研究资源限制之前先阅读一遍。它应该对理解正在发生的事情有很大帮助。

什么是 cgroup?

cgroups(控制组的缩写)是 Linux 内核功能,用于限制、说明和隔离进程集合的资源使用(CPU、内存、磁盘 I/O、网络等)。
此功能最初由 Google 的 2 位工程师以“进程容器”的名称开发,但后来以“cgroups”的名称合并到 Linux 内核主线中。

为什么需要它?

cgroups 的设计目标之一是为许多不同的用例提供统一的接口,从控制单个进程(例如使用 nice)到整个操作系统级虚拟化。简单来说,cgroups 提供:

  • 资源限制:组可以设置为不超过配置的内存限制,其中还包括文件系统缓存。
  • 优先级——某些组可能会获得更大份额的 CPU 利用率或磁盘 I/O 吞吐量。
  • 会计 –衡量一个组的资源使用情况,例如可以用于计费目的。
  • 控制——冻结进程组、检查点和重新启动。

它们是如何直接或间接使用的?

控制组可以以多种方式使用:

  1. 通过手动访问 cgroup 虚拟文件系统。
  2. 通过使用 cgcreate、cgexec 和 cgclassify(来自 libcgroup)等工具即时创建和管理组。
  3. 通过“规则引擎守护进程”可以自动将某些用户、组或命令的进程移动到其配置中指定的 cgroups。
  4. 间接通过其他使用 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 的工作原理。 (最重要的在图像中突出显示)

linux虚拟化

第 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 接受格式的条目,

:

其中, majorminor是特定设备的值,我们希望对其进行速率限制。 rate_per_second是该组的进程可以达到的最大速率。

获取主要和次要数字很容易。我正在使用的机器只有一个磁盘 /dev/sda 因此运行命令 ls -l /dev/sda* 我可以获得主要和次要编号。

linux虚拟化2

突出显示的值是我的 /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”。顶级终端中的费率应该类似于此。

linux虚拟化3

有趣的是,我们可以采用任何没有内置速率限制的应用程序,我们可以根据需要对其进行节流。

linux虚拟化4

上图是在读取 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 

linux虚拟化5

交换状态必须类似于上面显示的状态。

$ 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 上的客座博客写作。