写点什么

玩转 Cgroup 系列之三:挑战手动管理 Cgroup

作者:小猿姐
  • 2023-11-14
    浙江
  • 本文字数:2272 字

    阅读完需:约 7 分钟

玩转 Cgroup 系列之三:挑战手动管理 Cgroup

在前面的文章中,作者讨论了 Cgroup 和 CPUShare 如何用于系统管理和性能调优。


在这一篇文章中,作者将重点介绍 Cgroup 的手动管理任务。虽然手动管理 Cgroup 不是一件容易的事,但了解其中的过程可以帮助我们更好地认识 Cgroup 和资源管理(点击参考原文)。

挑战手动管理 Cgroup

来看看如何在没有相关工具支持的情况下创建 Cgroup。


本质上, Cgroup 只是一个挂载有 cgroups 的目录结构,可以位于文件系统的任何位置。在默认情况下,我们可以在 /sys/fs/cgroup 中找到系统创建的 Cgroup。而如果想手动创建 Cgroup 该怎么做呢?首先,需要创建一个顶级目录:


# mkdir -p /my_cgroups
复制代码


创建后,需要决定使用哪些控制器。注意,Cgroup v1 版本的结构大致如下:


/my_cgroups├── <controller type>│   ├── <group 1>│   ├── <group 2>│   ├── <group 3>
复制代码


要创建的所有组都单独嵌套在每个控制器下。因此,控制器 memory 下的 group1 与控制器 blkio 下的 group1 是完全独立的。在这个基础上,我们创建一个基本的 CPUShares 示例。简单起见,我先在系统上生成一些负载:


# cat /dev/urandom
复制代码


这一步会在系统中产生一些人为负载,以便进行简单测算。它不是一个真实的负载示例,但是突出了 CPUShares 的主要特点。我还设置了一台运行 CentOS 8 的虚拟机,只有一个 vCPU,以便进行非常简单的数学计算。做好这些准备之后,第一步就是为我们的 Cgroup 控制器创建一些目录:


# mkdir -p /my_cgroups/{memory,cpusets,cpu}
复制代码


接下来,将 Cgroup 挂载到这些文件夹中:


# mount -t cgroup -o memory none /my_cgroups/memory# mount -t cgroup -o cpu,cpuacct none /my_cgroups/cpu# mount -t cgroup -o cpuset none /my_cgroups/cpusets
复制代码


如果想创建自己的 Cgroup,只需在您想要使用的控制器下创建一个新的目录就可以了。在这个例子中,我处理的是位于 cpu 目录中的文件 cpu.shares。下面我们在 cpu 控制器下创建一些 Cgroup:


# mkdir -p /my_cgroups/cpu/{user1,user2,user3}
复制代码


请注意,这些目录是由控制器自动填充的:


ls -l /my_cgroup/cpu/user1/-rw-r--r--. 1 root root 0 Sep  5 10:26 cgroup.clone_children-rw-r--r--. 1 root root 0 Sep  5 10:26 cgroup.procs-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.stat-rw-r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_all-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_percpu-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_percpu_sys-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_percpu_user-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_sys-r--r--r--. 1 root root 0 Sep  5 10:26 cpuacct.usage_user-rw-r--r--. 1 root root 0 Sep  5 10:26 cpu.cfs_period_us-rw-r--r--. 1 root root 0 Sep  5 10:26 cpu.cfs_quota_us-rw-r--r--. 1 root root 0 Sep  5 10:26 cpu.rt_period_us-rw-r--r--. 1 root root 0 Sep  5 10:26 cpu.rt_runtime_us-rw-r--r--. 1 root root 0 Sep  5 10:20 cpu.shares-r--r--r--. 1 root root 0 Sep  5 10:26 cpu.stat-rw-r--r--. 1 root root 0 Sep  5 10:26 notify_on_release-rw-r--r--. 1 root root 0 Sep  5 10:23 tasks
复制代码


现在我已经设置了一些 Cgroup,然后就可以生成负载了。打开三个 SSH 会话,并在前台运行以下命令:


# cat /dev/urandom
复制代码


可以在 top 中看到结果:



注意: CPUShares 是基于顶级 Cgroup 的。默认情况下,顶级 Cgroup 是无约束的,这意味着一旦上层进程需要 CPUShares,系统将优先考虑该进程。是不是感觉有点绕?所以我们最好在系统上对 Cgroup 的布局进行可视化,以避免混淆,这一点非常重要。


从上面的截图中可以看到,所有的 cat 进程几乎都接收到了相同数量的 CPU 时间。这是因为默认情况下 Cgroup 在 cpu.shares 文件中的赋值是 1024。如前所述,这个份额是由父级与其他 Cgroup 的关系决定的。在本例中,我没有调整任何父级的权重。因此,如果所有父级 Cgroup 同时需要资源的话,它们将以默认的 1024 CPUShares 的权重进行分配。


回过头来,我在这里创建了带有默认值的 Cgroup,每个组的默认权重为 1024。如果要更改这个值,只需更改 cpu.shares 文件即可:


# echo 2048 > user1/cpu.shares# echo 768 > user2/cpu.shares# echo 512 > user3/cpu.shares
复制代码


现在,权重计算会变得更复杂。但因为我还没有将任何进程添加到 Cgroup 中,它还处于未激活状态。如果要将进程添加到 Cgroup 中,将所需的 PID 添加到 tasks 文件中就可以:


# echo 2023 > user1/tasks
复制代码


同样,可以在 top 中看到结果:



如图所示,新 Cgroup 中的进程大致接收到了一半的 CPU 时间。这是因为按照之前的等式,可以计算出:



我们继续将其他两个进程添加到各自的 Cgroup 中并观察结果:


# echo 2024 > user2/tasks# echo 2025 > user3/tasks
复制代码



可以看到权重已经生效,user1 占用了大约 61% 的 CPU 时间。剩余的时间分配给了 user2user3



不过,我们的示例也存在几个问题:


  1. 这些设置都是手动创建的,如果放入 Cgroup 中的进程的 PID 发生改变,会怎么样?

  2. 自定义的文件和文件夹在重启后不会保存,怎么办?

  3. 大部分工作都需要手动完成,有哪些工具可用?


我知道你很急,但是你先别急,systemd 为我们提供了很好的解决方案。我们在下一篇文章中会涉及。

总结

现在我们已经知道如何手动管理 Cgroup 了,因此后面大家能够更好地体会 Cgroup 和 systemd 协同使用的价值。


我将在本系列的第四篇,也就是完结篇中,深入探讨这个想法。

用户头像

小猿姐

关注

还未添加个人签名 2022-08-11 加入

每个开发者都想知道的云原生和数据库技术

评论

发布
暂无评论
玩转 Cgroup 系列之三:挑战手动管理 Cgroup_cpu_小猿姐_InfoQ写作社区