资源争夺,第6部分:cpuset或共享并不总是正确的

在谈论cgroup时,红帽用户经常问同样的问题:“我有一个应用程序在延迟方面非常敏感。 通过将cgroup绑定到某些处理器内核,是否可以使用cgroup将该应用程序与其他应用程序隔离开?”



当然可以 否则,我们将不会选择此问题作为今天文章的主题。

在童年时代,我们经常被告知分享是正确的。 总的来说,就是这样。 但是也有例外。

就像我们在本系列第一篇文章中所写的那样,默认情况下,Red Hat Enterprise Linux 7的行为类似于球形的祖母。 从某种意义上说,她正在尝试在所有询问系统资源的人之间公平分配系统资源。 但是,在现实生活中,祖母养的宠物会变得更多。 转换为sysadmin,这意味着在某些情况下某些应用程序或服务比其他应用程序或服务更重要,因此应给予所有可能的关注,以使它们尽可能快速地响应。

红帽企业版Linux 7分两个步骤执行此操作:

  1. 我们将部分处理器内核隔离开来,以将其转移给此类应用程序专用。
  2. 我们创建了cgroups组和单元文件,将此应用程序绑定到隔离的内核。

关于这些帖子中的示例有一点点题外话


Hat Enterprise Linux 7.4改变了短片的工作方式,例如用户会话。 结果,他们不能再使用systemctl set-property命令即时更改cgroup设置,对配置进行永久更改或创建插入文件。 是的,这很遗憾,但是Linux开发社区已经决定这样做。 好消息是这些更改并未影响服务。 也就是说,如果应用程序通过单元文件启动和停止(作为守护程序工作),那么我们所有的示例都可以正常工作。 此外,仍然可以使用古老的工具(例如cgcreate和cgset)创建自己的cgroup,然后将用户会话和进程放在这些组中以使用CPU ball和其他控件。 生活中,一切都会改变,因此我们只能适应和发明新技术。 现在我们转向今天的话题。

用isolcpus建立分离主义


Linux内核中最重要的组件之一是进程调度程序。 如果更深入一点,则进程是作为应用程序或服务的一部分的可执行代码。 实际上,该过程由计算机执行的一系列指令组成,无论是查看印章还是更严重的内容,它都会执行或执行该指令。

这些指令由中央处理器(也称为CPU)处理。 在现代计算机上,CPU通常由几个称为内核的处理器组成。

默认情况下,调度程序将每个处理器内核视为执行模块之一,并在调度模块中将新进程分配给它们。 在这种情况下,调度程序会考虑到负载,尝试在内核之间或多或少地平均分配新兴进程。 不幸的是,无法告知调度程序该特定进程最终将导致整个进程组,并且该组将需要与其他进程隔离执行,因为它们不应具有公共处理器核。

因此,我们需要以某种方式告诉计划者,以使他不会接触处理器核心的一部分,即不会给处理器带来任何影响。 然后,我们自己(或在某些其他进程的帮助下)将强制执行那些我们认为有必要与内核调度程序隔离的进程。 可以使用grub配置文件中内核启动行中的isolcpus参数来完成此操作。 在下面的示例中,我们有一台具有四个内核的机器,该机器上有两个grub文件:一个位于/ etc / default中,称为grub.noiso(这是默认配置备份),第二个位于此处,简称为grub,因此它拿起grub2-mkconfig。 编辑了第二个文件,以将内核1-3与进程调度程序隔离。


警告:在Red Hat Enterprise Linux 7上,您无需手动修改/ boot文件夹中的grub.conf文件。 而是,对/ etc / default / grub进行必要的更改,然后使用适当的实用程序重建grub.conf文件,例如,如下所示:


使用isolcpus参数时,有必要列出以逗号分隔的已发布处理器核心,编号从0开始。系统重新启动后,进程调度程序将不会将这些核心用于任何事物,除非每个核心上必须具有某些系统级进程。 为了检查我们的方法是否有效,我们将启动几个加载过程,然后通过top命令查看每个内核的加载。


如您所见,所有加载进程都位于CPU 0上,而不是均匀分布在所有四个内核上。 因此,我们正确注册了boot参数。

使用cpuset将进程绑定到内核


现在,我们继续进行最好不要做的事情,如果您不明白为什么要这样做,那么最好在进行全面测试后才能将其部署到生产中

这些警告是为了什么? 除了我们将要做的工作外,通常,我们还会在libcgroup工具箱的帮助下完成一些简单的事情,这是我们在上一篇文章中所写的。 如果您还记得的话,这只是一组用于创建,修改和销毁cgroup的命令。 实际上,它们是Red Hat Enterprise Linux 6的一部分,但是它们也可以安装在Red Hat Enterprise Linux 7上,尽管这种可能性将来可能会消失。 简要回顾一下使用libcgroup的主要建议:

  1. 使用systemd来控制那些受systemd本身控制的cgroup控制器(这些是CPU,内存和块I / O)。
  2. 使用libcgroup工具来管理所有其他cgroup控制器。
  3. 请特别注意您的操作带来的计划外后果。

使用cpuset的概念,一切都很简单-这是处理器内核的列表(编号,调用,从0开始),接受仅在这些内核上执行的任务。 这些是最常见的处理器核心,它们可以由流程调度程序控制(这是默认情况下系统的配置方式),或者相反,它们可以与调度程序隔离(如我们在上面的示例中所做的那样)。

让我们从示例中检查系统上的目录/ sys / fs / cgroup文件系统。 如您所见,由于该控制器是内核的一部分,因此cpuset目录已经存在(尽管它不受systemd的控制)。 但是,它还没有cgroup,因此我们在此目录中仅看到默认设置。


检查我们的机器上是否安装了libcgroup工具包:


如果未安装,则可以使用yum install libcgroup命令轻松解决,甚至不需要重新启动。

现在创建cpuset。 为此,我们将使用以下命令为cpuset创建一个新的cgroup并注册其属性:


Cgcreate命令创建一个名为testset的cgroup并将其放置在cpuset控制器中。 然后,我们将VM的第三个核心分配给这个新的cpuset,并为其分配NUMA区域0。即使您的系统不使用NUMA(并且我们的系统不使用它),您仍然需要注册该区域,否则您将无法将任务分配给cgroup组。 现在,检查是否已在文件系统上创建testset目录,并查看其中的内容。


如您所见,我们所做的更改已经到位,但是到目前为止,尚未对此cpuset执行任何进程。 如何在这里种植一些工艺?

有几种方法可以做到这一点:

  • 您可以将现有进程的PID驱动到任务文件中。 它有效,但不是很漂亮。
  • 您可以使用cgexec并在进程启动时指定组。 如果应用程序不是守护程序,则此方法有效。 此外,所有这些都可以漂亮地写在应用程序启动脚本中。
  • 对于作为运行systemd的守护程序运行的应用程序,您可以创建服务文件。

让我们看看cgexec选项。


反过来,我们启动了foo.exe,它启动了子进程,该子进程只会主动加载处理器。 cgexec命令中的--sticky选项表示“任何子进程都必须与父进程保留在同一cgroup中”。 因此,这是一个重要的选择,必须牢记。 现在,我们看到cgroup中有两个进程在旋转,我们知道它们的PID。 看一下顶部:


如您所见,CPU 3现在已装入眼球,其余的正在冷却。

这是一个单位文件与系统服务运行相同应用程序的外观:


单位文件中有三个ExecStartPre命令,这些命令执行我们已经手动完成的设置。 然后是ExecStart命令,它将启动应用程序。 当应用程序停止运行时,ExecStopPost命令将自行清除,删除cgroup。


如您所见,在上一个示例中,我们创建了一个名为set1的新cgroup。 我们这样做是为了表明您可以拥有共享同一CPU的几个活动cgroup。 对谁来说似乎有用,但相反却使某人感到困惑。

好吧,行得通吗? 好像是这样!


现在,我们将完成服务工作,并验证cgroup是否被破坏:


注意:重新启动后,不会保存使用cgcreate创建的cgroup组。 因此,必须在启动脚本和单位文件中规定创建此类组的步骤。

因此,现在您的武库中有更多与cgroup一起使用的工具。 我们希望他们派上用场!

我们的“资源之战”系列中的其他cgroup帖子可在以下位置找到:

Source: https://habr.com/ru/post/zh-CN430748/


All Articles