关于 Cgroup 的话题,这个系列文章是小猿姐迄今为止看过的最清晰易懂的,那么下面就一起来看看 Linux 系统管理员视角下的 Cgroup 吧。
原文链接:https://www.redhat.com/sysadmin/cgroups-part-one
概述
Cgroup 是一个庞大的主题。我们将关于 Cgroup 的讨论分成了四个部分。本文是第一部分,介绍了 Cgroup 的基本概念。第二部分深入探讨了 CPU 份额。第三部分名为“以困难的方式使用 Cgroup”,介绍 Cgroup 的管理任务。第四部分会讨论由 systemd 管理的 Cgroup。下面,我们就开始介绍 Cgroup 的定义以及它如何帮助资源管理和性能调优。
Cgroup 对于领域之内的人不是陌生的主题,你可能在参加有关容器化的演讲时听到有人提到了它,也许你在研究 Linux 性能调优时看到了它,又也许你只是在某一天遍历你的文件系统时发现了 /sys/fs/cgroups
。但是如果你想要更加了解这个已经在内核中存在了很长时间的功能,就可以耐心地读完这四篇文章,或许就能学到一些你之前不知道的东西。
什么是 Cgroup?
Webster 的词典将 Cgroup 定义为……开个玩笑。我一直讨厌那些以无聊的词典定义开始的演讲。相反,我将尝试将 Cgroup 的技术定义简化为容易理解的东西。
Linux 内核负责系统上所有硬件的可靠交互。这意味着,除了使操作系统(OS)能够理解硬件的代码位(驱动程序)之外,它还限制了特定程序可以从系统中获取多少资源。系统必须将系统内存分配给计算机可能执行的所有应用程序。在最基本的形式中,Linux 系统可以无限制地运行大多数应用程序。在所有应用程序都能很好地相互配合的时候,是没有任何问题的。但是,如果某个程序中存在错误,并且它开始占用所有可用内存,会怎么样呢?内核有一个叫做 OOM(Out Of Memory) Killer 的功能。它能在系统崩溃之前停止应用程序以释放足够的 RAM,以使操作系统能够继续正常运行。因此,OOM 进程在系统崩溃之前充当了最后一道防线,有时很有用。但是由于内核可以控制哪些进程能够幸免于 OOM,它也可以确定哪些应用程序从一开始就不应消耗过多的内存。
而 Cgroup 是内核内置的一种设施,允许管理员在系统上设置任何进程的资源利用限制。一般来说,Cgroup 主要控制:
- 每个进程的 CPU 份额数量。
- 每个进程的内存限制。
- 每个进程的块设备 I/O。
- 哪些网络数据包被识别为同一类型,以便其他应用程序可以强制执行网络流量规则。
Cgroup 还能控制别的,但以上四个是大多数管理员关心的主要类别。
Cgroup 的起源
Cgroup 最初由 Google 工程师在 2006 年提出,是用于细粒度资源控制的 Linux 内核机制。Cgroup 于 2007 年合并到 Linux 内核中。虽然目前有两个版本的 Cgroup,但多数发行版和机制仍在使用版本 1,因为它从 2.6.24 内核开始就已经存在了。与大多数添加到主线内核中的功能一样,它起初并没有太高的采用率。版本 2 继续延续这一趋势,虽然已经存在了将近五年,但仍未广泛部署。
影响 Cgroup 采用率的一个问题是人们对其存在及其在现代 Linux 系统中的作用缺乏了解。低意识度和采用率通常意味着与内核接口的交互方式很笨拙、复杂或者完全是一个手动过程。Cgroup 最初就是如此。当然,创建一个临时的 Cgroup 并不难。例如,如果你想模拟在 Cgroup 工具开发之前的早期情况,你可以创建一堆目录,挂载 cgroup
文件系统,然后手动开始配置所有内容。但在深入讨论这些内容之前,让我们先谈谈 Cgroup 在当今 Linux 生态系统中的重要性。
这是最初设计时候的白皮书:
https://www.kernel.org/doc/ols/2007/ols2007v2-pages-45-58.pdf
Cgroup 的重要性
当你运行容器化工作负载时,现代系统中有四个与 Cgroup 密切相关的主要特性。
- 资源限制
正如之前提到的,Cgroup 允许管理员确保系统上运行的程序在 CPU、RAM、块设备 I/O 和设备组等方面保持在可接受的范围内。
注意: 设备组 Cgroup 可以成为系统全面安全策略的关键组件。设备组包括控制对读取、写入和 mknod
操作的权限。读写操作是相当容易理解的。
而 mknod
最初是用来填充出现在 /dev/
中的所有设备的。它们包括硬盘、用于 Arduino、ESP8266 微控制器等设备的 USB 接口,或者系统上可能存在的其他设备。大多数现代 Linux 系统使用 udev
来自动将内核检测到的设备填充到这个虚拟文件系统中。mknod
还允许多个程序通过创建一个命名管道来相互通信。这个概念超出了本文的范围,但你只要理解它有助于把信息从一个程序传递到另一个程序即可。不管怎样,在受控环境中,管理员应该密切关注限制 mknod
的操作。
- 优先级
优先级与资源限制略有不同,因为你不一定是限制进程。相反,你只是在说无论有多少资源可用,进程 X 在系统上总是比进程 Y 有更多的时间。
- 记账
虽然默认情况下大多数企业版 Linux 都关闭了记账功能,因为它会增加额外的资源利用,但打开特定树的资源利用,可以查看在哪个 Cgroup 内的进程正在使用哪些类型的资源,这个功能非常有帮助。
- 进程控制
Cgroup 中有一个称为冷冻器(freezer)的功能。虽然深入理解这个功能超出了本文的范围,但你可以将冷冻器视为对特定进程进行快照并移动的能力。参阅内核文档。
好的,那这一切意味着什么呢?从系统管理员的角度来看,它意味着:
- 首先,即使不深入研究容器技术,通过仔细管理工作负载类型、应用程序和资源要求,你可以在单个服务器上实现更高的密度。
- 其次,它在很大程度上增强了系统的安全性。虽然典型的 Linux 安装默认使用 Cgroup,但它没有对进程施加任何限制。你按照实际情况默认强制施加限制,还可以限制特定用户、组或进程对特定设备的访问权限,进一步加固系统安全。
- 最后,你可以通过 Cgroup 进行大量的性能调优。结合 tuned,创建一个专门调整为你的个体工作负载的环境。在大规模或对延迟敏感的环境中,这些调整可能是是否能够达到服务级别协议(SLA)的差异所在。
Cgroup 是如何工作的?
本文中,我们讨论的是 Cgroup V1。虽然 Cgroup V2 在 Red Hat Enterprise Linux 8(RHEL 8)中可用,但默认情况下已禁用。大多数容器技术如 Kubernetes、OpenShift、Docker 等仍依赖于 Cgroup V1。
Cgroup 是一种控制内核中某些子系统的机制。这些子系统,如设备、CPU、RAM、网络访问等,被称为 Cgroup 术语中的控制器。每种类型的控制器(cpu
、blkio
、memory
等)都被细分为一个类似树形结构。每个分支或叶子都有自己的权重或限制。一个控制组有多个与之关联的进程,使得资源利用可以细粒度调整,易于微调。
*注意:*每个子进程都继承并受到父 Cgroup 设置的限制。
在上面的图表中,你可以看到一个进程(PID 1)可以属于 memory
、disk i/o
和cpu
控制组。Cgroup 是按资源类型创建的,彼此之间没有关联。所以你可以在所有控制器中关联一个 database
组,但这些组是相互独立的。就像 GID 一样,这些组在创建时被分配到一个数字值,而不是友好名称。在内核中,这些值用于确定资源分配。换句话说,假设每个附加到控制器的 Cgroup 名称在附加后被重命名为控制器的名称加上你选择的名称。那么在 memory
控制器中的 database
组可以被称为 memory-database
。它与控制器 cpu
关联的 database
组没有关系,因为它的名称是 cpu-database
。
注意: 这只是一个极其简化的解释,旨在帮助读者更好地理解。如果你想深入了解 Cgroup 的底层代码,请注意这并不准确。
总结
现在你对 Cgroup 是什么以及它如何帮助你进行性能调优和增强安全性有了一定的了解。我们也讨论了 Cgroup 如何与控制器进行交互。
本文并不旨在对 Cgroup 中存在的所有控制器类型进行详细解析,这些内容可能需要一本完整的书才能解释清楚。在下一篇文章中,我将介绍 CPU 份额。因为它们相对复杂,在系统的整体健康状况中起着重要作用,而且其他控制器的功能与之类似。因此,学完下一篇后,你应该能够把从 CPU 控制器中学到的知识应用于大多数其他 Cgroup 控制器中。