FAIRYFAR-INTERNAL
 
  FAIRYFAR-INTERNAL  |  SITEMAP  |  ABOUT-ME  |  HOME  
Redhat Linux 8使用cgroup限制用户资源

0. 引言

在同一套物理主机环境中,如果部署多套数据库软件,常常面临资源隔离/限制需求,按照Linux用户来隔离资源是一种常见的解决方案。然而,我们发现网上搜索到的资料绝大部分过于陈旧,仅适用于Redhat/CentOS Linux 7等比较旧的操作系统,当我们在Redhat/CentOS Linux 8系统上部署时,方法根本不可用。本文试图探索其中的原因,给出多种解决方法。虽然本文以SeaboxSQL数据库为例探讨多套应用程序的资源隔离问题,但是,其方法是普适性的。方法在Redhat 8.5上验证通过,同样适用于CentOS 8

1. 需求

Redhat Linux 8系统上,使用cgroup限制指定用户的资源使用量。

例如,假设有这样一个需求,在一台物理服务器上,安装多套SeaboxSQL数据库,因为业务优先级不同,期望不同数据库允许使用的资源数量不同,譬如,高优先级的SeaboxSQL数据库允许使用更多的CPU资源,而低优先级的SeaboxSQL数据库仅允许使用很少量的CPU资源。

通常,我们可能会想到使用Docker或其它虚拟化技术进行资源隔离,但是虚拟化或多或少都有一些性能损失[1]。因此,更明智的方法是使用多个用户安装SeaboxSQL数据库,按用户来隔离资源。通过搜索,有很多关于使用cgroup限制Linux用户资源的文章,笔者为此整理成参考文献[2]。

本文假定您已经了解:

  • cgroup基础
  • libcgrouplibcgroup-tools工具使用方法
  • Redhat/CentOS Linux 7使用cgroup限制用户资源的方法

本文所需的物料可从以下项目获取[3]:

https://github.com/fairyfar/cgroup-cn

2. 问题

不幸的是,当你在Redhat Linux 8或者其它更新的Linux发行版(例如统信UOS V20)上使用该方法时,却遇到了致命问题:关键服务cgred(对应cgrulesengd应用)已经被踢出libcgroup-tools包了。例如,在Redhat Linux 8.5上,即使已经安装了libcgrouplibcgroup-tools包的情况下,cgred服务仍然不存在:

snippet.bash
[root@bogon ~]# yum list installed | grep libcgroup
libcgroup.x86_64                                   0.41-19.el8                                   @base
libcgroup-tools.x86_64                             0.41-19.el8                                   @base
 
[root@bogon ~]# systemctl status cgred
Unit cgred.service could not be found.

查阅libcgroup的变更日志,有以下记录:

snippet.bash
[root@bogon ~]# rpm -q --changelog libcgroup
* Tue Jan 14 2014 Peter Schiffer <pschiffe@redhat.com> 0.41-1
- resolves: #966008
  updated to 0.41
- removed deprecated cgred service
  please use Control Group Interface in Systemd instead

关于cgred被移除的讨论,详见参考文献[4]。

3. 解决方法

本文提供三种方法,但是每种方法都有其局限性,请权衡使用。

3.1 使用systemd控制用户资源

Redhat Linux 8libcgroup包移除cgred服务,变更日志提到:

please use Control Group Interface in Systemd instead

看来官方是希望用systemd来替代cgred,因此,首先想到使用systemd提供的接口来控制用户资源。以下通过一个具体例子来演示如何用systemd控制用户资源。

3.1.1 实例

假设,欲限制Linux用户seabox的CPU使用上限为30%(即,单个CPU核的30%)。步骤如下:

(1). 获取用户UID

snippet.bash
[root@bogon ~]# id seabox
uid=1002(seabox) gid=1002(fairyfar) groups=1002(seabox)

(2). 设置配额

snippet.bash
[root@bogon ~]# systemctl set-property user-1002.slice CPUQuota=30%

如果命令报以下错误:

snippet.bash
[root@bogon ~]# systemctl set-property user-1002.slice CPUQuota=30%
Failed to set unit properties on user-1002.slice: Unit user-1000.slice is not loaded.

则,需要在set-property之前先执行start命令:

snippet.bash
[root@bogon ~]# systemctl start user-1002.slice

(3). 查看配置情况

snippet.bash
[root@bogon ~]# systemctl cat user-1002.slice
 
# /etc/systemd/system/user-1002.slice.d/50-CPUQuota.conf
[Slice]
CPUQuota=30%

(4). 使配置立即生效

snippet.bash
[root@bogon ~]# systemctl daemon-reload

3.1.2 控制参数

上述实例中,systemctl set-property命令中使用了参数CPUQuota,表示CPU使用率上限,30%表示单个CPU核的30%使用率。systemctl支持很多种参数,用于控制不同类型资源。详见参考文献[5]。

需要特别注意的是,systemctl支持的参数种类与systemd版本有关,查看systemd版本方法:

snippet.bash
[root@bogon ~]# systemctl --version
systemd 239 (239-51.el8)

例如,Redhat Linux 8.5默认的systemd版本为239,从参考文献[5]可以查询到:

参数 作用 支持参数的systemd最低版本
———– ——————- ————————-
CPUQuota 配置CPU使用率上限 213
AllowedCPUs 绑定CPU核(cpuset) 244

所以,很遗憾,Redhat Linux 8.5不支持通过systemctl绑定CPU核功能。

3.1.3 删除配置

在“实例”小节中,演示了配置资源限制,那么如何删除限制呢?有两种方法:

如果systemd版本大于等于229,则可以使用systemctl revert命令:

snippet.bash
[root@bogon ~]# systemctl revert user-1002.slice

否则,低版本需要手动删除:

snippet.bash
[root@bogon ~]# rm -rf /etc/systemd/system/user-1002.slice.d/

删除的路径在systemctl cat结果中可以找到,当然也可以有选择性地删除部分配置项。

3.1.4 方法的局限性

主要有两方面局限。

(1). 支持的参数类型受限于systemd版本。

Redhat Linux 8默认的systemd版本支持的参数类型很有限,一些常用的资源控制尚未支持。理所当然,笔者认为:

Redhat Linux 8libcgroup中移除cgred,似乎操之过急。

(2). 用户登录方式影响systemd控制资源的有效性。

本方法可以控制用户通过GDMThe GNOME Display Manager)或ssh登录时的资源使用,但是不适用于su方式切换登录的用户。例如,以下方式登录的用户,不受上述systemd配置的资源限制。

snippet.bash
[root@bogon ~]# whoami
root
[root@bogon ~]# su - seabox
# su方式登录的用户,资源不受限制。
[seabox@bogon ~]$ do someting

3.1.5 cgroup v2支持情况

本方法支持cgroup v2

3.2 直接安装rhel7的libcgroup包

3.2.1 降版本

Redhat 7获取rpm包,然后安装到Redhat 8

(1). 从Redhat 7获取rpm

Redhat 7上,执行以下yum命令,只下载包但不安装:

snippet.bash
[root@bogon ~]# yum install --downloadonly --downloaddir=/tmp/ libcgroup
[root@bogon ~]# yum install --downloadonly --downloaddir=/tmp/ libcgroup-tools

为便于下载,笔者已经将两个rpm包放至以下位置:

(2). 卸载Redhat 8默认的libcgroup

snippet.bash
[root@bogon ~]# yum remove libcgroup-tools
[root@bogon ~]# yum remove libcgroup

(3). 在Redhat 8上安装rpm

snippet.bash
[root@bogon ~]# rpm -i ./libcgroup-0.41-21.el7.x86_64.rpm
[root@bogon ~]# rpm -i ./libcgroup-tools-0.41-21.el7.x86_64.rpm

3.2.2 方法的局限性

本方法在下列系统上验证通过:

  • Redhat/CentOS Linux 8.5
  • UnionTech OS Server release 20 (kongzi)
  • openEuler release 20.03 (LTS-SP3)

其它版本Linux是否可行,有待验证。

3.2.3 cgroup v2支持情况

本方法不支持cgroup v2,因为Redhat Linux 7libcgroup版本为v0.41,尚未支持cgroup v2。如果启用了cgroup v2,则启动cgred服务报错:

snippet.bash
[root@bogon ~]# systemctl start cgred
Job for cgred.service failed because the control process exited with error code.
See "systemctl status cgred.service" and "journalctl -xe" for details.
 
[root@bogon ~]# systemctl status cgred
● cgred.service - CGroups Rules Engine Daemon
   Loaded: loaded (/usr/lib/systemd/system/cgred.service; disabled; vendor preset: disabled)
   Active: failed (Result: exit-code) since Fri 2025-01-08 04:55:33 EST; 2min 52s ago
  Process: 5534 ExecStart=/usr/sbin/cgrulesengd $OPTIONS (code=exited, status=81)
 
Jan 8 04:55:33 bogon systemd[1]: Starting CGroups Rules Engine Daemon...
Jan 8 04:55:33 bogon cgrulesengd[5534]: Error: libcgroup initialization failed, Cgroup is not mounted
Jan 8 04:55:33 bogon systemd[1]: cgred.service: Control process exited, code=exited status=81
Jan 8 04:55:33 bogon systemd[1]: cgred.service: Failed with result 'exit-code'.
Jan 8 04:55:33 bogon systemd[1]: Failed to start CGroups Rules Engine Daemon.

3.3 使用libcgroup控制用户资源

3.3.1 libcgroup项目概况

libcgroup是一个开源项目,提供一系列cgroup应用工具、系统服务,以及开发库。该项目源码最早托管在SourceForge

https://sourceforge.net/projects/libcg/

现在已迁移至GitHub

https://github.com/libcgroup/libcgroup

libcgroup v2.0版本开始,全面支持cgroup v2

请注意:

  • libcgroup项目一直在维护cgrulesengdcgred服务),移除cgredLinux发行版的分叉行为。
  • 大部分Linux发行版提供libcgrouplibcgroup-tools两个安装包,但是,这两个包中的应用程序和库均源自libcgroup项目,只是按功能拆成了两个安装包(实际上还有个libcgroup-pam包),libcgroup安装包主要提供.so库文件,而libcgroup-tools安装包提供应用程序和系统服务。

3.3.2 编译libcgroup

libcgroup项目下载源代码,自行编译libcgroup,以Redhat Linux 8.5环境为例。

(1). 下载源码,然后解压缩。

snippet.bash
[root@bogon ~]# wget https://github.com/libcgroup/libcgroup/releases/download/v0.41/libcgroup-0.41.tar.bz2

(2). 准备编译环境

安装依赖库,并创建安装路径:

snippet.bash
[root@bogon ~]# yum install pam-devel
[root@bogon ~]# mkdir /root/libcgroup

(3). 编译

snippet.bash
[root@bogon ~]# cd libcgroup-0.41
[root@bogon libcgroup-0.41]# ./configure --prefix=/root/libcgroup
[root@bogon libcgroup-0.41]# make
[root@bogon libcgroup-0.41]# make install

编译和安装成功后,可以在/root/libcgroup目录下查看编译结果。

3.3.3 制作rpm安装包

上一小节,我们自行编译,得到了需要的cgrulesengd应用程序,其实现在我们可以仿照Redhat 7中的libcgroup-tools手动创建cgred服务。但是,这样不便于维护。如果把编译好的文件制作成rpm安装包,那么就可以一键安装好全部工具和服务。

下面以libcgroup-tools为例演示rpm制作过程。制作libcgrouprpm方法与此类似,当然,如果你乐意,也可以将两个rpm二合一。

(1). 准备环境

安装rpm-build(有的发行版Linux中名称为rpmbuild)和rpmdevtools

snippet.bash
[root@bogon ~]# yum install rpm-build
[root@bogon ~]# yum install rpmdevtools

(2). 创建rpmbuild目录结构

使用rpmdev-setuptree工具创建目录。当然,rpmbuild目录及其子目录也可以手动mkdir创建。默认情况下,目录位置和名称都是确定的,没有必要刻意去修改。

snippet.bash
[root@bogon ~]# rpmdev-setuptree
[root@bogon ~]# ll rpmbuild/
total 0
drwxr-xr-x 2 root root 6 Jan  8 04:21 BUILD
drwxr-xr-x 2 root root 6 Jan  8 04:21 RPMS
drwxr-xr-x 2 root root 6 Jan  8 04:21 SOURCES
drwxr-xr-x 2 root root 6 Jan  8 04:21 SPECS
drwxr-xr-x 2 root root 6 Jan  8 04:21 SRPMS

rpmbuild/SPECS目录中创建并编写一个libcgroup-tools-0.41-19.el8.x86_64.spec文件(名字可自选),.spec是打包控制文件,内容如下:

Name:           libcgroup-tools
Version:        0.41
Release:        19.el8
Summary:        libcgroup tools package.

Group:          Applications/System
License:        GPL
URL:            www.seaboxdata.com

%description
cgroup tools rpm package.

%prep
%build
%install
mkdir -p $RPM_BUILD_ROOT/usr/
mkdir -p $RPM_BUILD_ROOT/usr/bin/
mkdir -p $RPM_BUILD_ROOT/usr/lib/systemd/system/
mkdir -p $RPM_BUILD_ROOT/usr/sbin

cp -r ../BUILD/etc/ $RPM_BUILD_ROOT/etc/
mkdir -p $RPM_BUILD_ROOT/etc/cgrules.d
cp -r ../BUILD/usr/bin/cgclassify $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgcreate $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgdelete $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgexec $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgget $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgset $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/cgsnapshot $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/lscgroup $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/bin/lssubsys $RPM_BUILD_ROOT/usr/bin/
cp -r ../BUILD/usr/lib/systemd/system/cgconfig.service $RPM_BUILD_ROOT/usr/lib/systemd/system/
cp -r ../BUILD/usr/lib/systemd/system/cgred.service $RPM_BUILD_ROOT/usr/lib/systemd/system/
cp -r ../BUILD/usr/sbin/cgclear $RPM_BUILD_ROOT/usr/sbin/
cp -r ../BUILD/usr/sbin/cgconfigparser $RPM_BUILD_ROOT/usr/sbin/
cp -r ../BUILD/usr/sbin/cgrulesengd $RPM_BUILD_ROOT/usr/sbin/
cp -r ../BUILD/usr/share $RPM_BUILD_ROOT/usr/share/

%files
/etc/cgconfig.conf
/etc/cgconfig.d/
/etc/cgrules.d/
/etc/cgrules.conf
/etc/cgsnapshot_blacklist.conf
/etc/sysconfig/cgred
/usr/bin/cgclassify
/usr/bin/cgcreate
/usr/bin/cgdelete
/usr/bin/cgexec
/usr/bin/cgget
/usr/bin/cgset
/usr/bin/cgsnapshot
/usr/bin/lscgroup
/usr/bin/lssubsys
/usr/lib/systemd/system/cgconfig.service
/usr/lib/systemd/system/cgred.service
/usr/sbin/cgclear
/usr/sbin/cgconfigparser
/usr/sbin/cgrulesengd
/usr/share/doc/libcgroup-tools-0.41
/usr/share/man/*

(3). 将需要打包的文件拷贝至rpmbuild/BUILD目录下对应子目录

按照.spec文件中的%files的文件列表,将需要打包的文件手动拷贝到rpmbuild/BUILD目录下的相应子目录。文件来源有两部分:

一部分是来自上一小节编译好的文件。例如,列表项的/usr/bin/cgclassify文件,需要手动将编译好的cgclassify文件拷贝到rpmbuild/BUILD/usr/bin/目录:

snippet.bash
[root@bogon rpmbuild]# pwd
/root/rpmbuild
[root@bogon rpmbuild]# mkdir -p BUILD/usr/bin/
[root@bogon rpmbuild]# cp /root/libcgroup/bin/cgclassify BUILD/usr/bin/

另一部分文件需要手动编写(当然,可以从发行版Linux现成安装包里提取),例如,列表项中的/usr/lib/systemd/system/cgconfig.service可以从Redhat 7libcgroup-tools-0.41-21.el7.x86_64.rpm中提取,然后拷贝到rpmbuild/BUILD/usr/lib/systemd/system/cgconfig.service。为了简化这一步操作,笔者已经将这部分文件上传至以下位置:

(4). 执行打包命令

snippet.bash
[root@bogon rpmbuild]# rpmbuild -bb ./SPECS/libcgroup-tools-0.41-19.el8.x86_64.spec

打包成功后,rpm包将生成在rpmbuild/RPMS目录下。

snippet.bash
[root@bogon rpmbuild]# ll RPMS/x86_64/
total 92
-rw-r--r-- 1 root root 91168 Jan  9 07:20 libcgroup-tools-0.41-19.el8.x86_64.rpm

按照上述步骤继续制作libcgroup rpm安装包。

3.3.4 制作rpm过程种遇到的问题

(1). contains an invalid rpath

错误内容如下:

ERROR 0002: file 'xxx' contains an invalid rpath 'yyy' in [yyy]

解决方法:修改~/.rpmmacros文件,注释掉以下行:

%__arch_install_post \
    ... /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot

(2). /etc/ld.so.conf: No such file or directory

警告内容如下:

/sbin/ldconfig: Warning: ignoring configuration file that cannot be opened: /etc/ld.so.conf: No such file or directory

警告可以忽略。

3.3.5 方法的局限性

过程相对繁琐,容易出错。但是,可以在任意发行版Linux上编译,与系统契合度更高,可按需自由定制。

3.3.6 cgroup v2支持情况

本方法是否支持cgroup v2取决于我们编译的libcgroup版本,以上示例我们是从libcgroup v0.41版本源码编译的,所以不支持cgroup v2,如果想支持cgroup v2,需要编译libcgroup v2.0以上版本。

经笔者验证,在Redhat Linux 8.5系统上,libcgroup v2.0可用。

4. 背景

本章节罗列一些背景资料,可选择性阅读。

4.1 libcgroup主要文件的说明

摘自参考文献[6]。

  • cgclassify:指令用于将正在运行的任务移至一个或多个cgroup中。
  • cgclear:指令用于删除层级中的全部cgroup
  • cgconfigparser:指令用于解析 cgconfig.conf 文件并且挂载层级。
  • cgcreate:指令用于在层级中创建新的cgroup
  • cgdelete:指令用于移除指定的cgroup
  • cgexec:指令用于在指定的cgroup中运行任务。
  • cgget:指令用于显示cgroup参数。
  • cgsnapshot:指令用于从现存的子系统中生成配置文件。
  • cgrulesengd:服务用于将任务分配到cgroup
  • cgset:指令用于为cgroup设定参数。
  • lscgroup:指令用于将层级中的cgroups列表。
  • lssubsys: 指令将包含特定子系统的层级列表。
  • cgconfig.confcgroupcgconfig.conf 文件中被定义。
  • cgred.conf:是 cgred 服务的配置文件。
  • cgrules.conf:包含可以确定任务何时归属于某一cgroup的规则。

4.2 cgred的工作原理

libcgroup通过注册cgred系统服务实现Linux用户资源管理,cgred服务实际上由应用程序cgrulesengd实现用户进程关联cgroup资源组功能。cgrulesengd应用程序的主要源代码对应libcgroupcgrulesengd.c文件。

cgrulesengd通过Netlink机制获得应用程序创建与退出事件,然后根据应用程序所述用户将进程PID写入对应cgroup组的cgroup.procstasks接口文件。

Netlink机制作为一种内核与用户空间通信的机制,是一种特殊的socket通信方式,对于Linux内核与用户空间进行双向数据传输是非常好的方式,详见参考文献[7]。

4.3 海盒数据库SeaboxSQL的资源管理

开篇讨论的海盒SeaboxSQL数据库[8],是北京东方金信科技股份有限公司积累多年数据库开发经验,打造的—款拥有完全自主知识产权、面向事务型业务处理的企业级关系型数据库。

本文讨论的利用cgroup控制多套数据库资源使用的解决方法属于普适方案,实际上Seabox数据库(SeaboxSQLSeaboxMPP)支持资源组管理功能,可以按照数据库用户组限制多种资源配额,可以进行更细致的资源管理。

5. 参考文献



打赏作者以资鼓励: