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
基础libcgroup
和libcgroup-tools
工具使用方法Redhat/CentOS Linux 7
使用cgroup
限制用户资源的方法
本文所需的物料可从以下项目获取[3]:
2. 问题
不幸的是,当你在Redhat Linux 8
或者其它更新的Linux
发行版(例如统信UOS V20
)上使用该方法时,却遇到了致命问题:关键服务cgred
(对应cgrulesengd
应用)已经被踢出libcgroup-tools
包了。例如,在Redhat Linux 8.5
上,即使已经安装了libcgroup
和libcgroup-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 8
从libcgroup
包移除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 8
从libcgroup
中移除cgred
,似乎操之过急。
(2). 用户登录方式影响systemd
控制资源的有效性。
本方法可以控制用户通过GDM
(The 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 7
的libcgroup
版本为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
项目一直在维护cgrulesengd
(cgred
服务),移除cgred
是Linux
发行版的分叉行为。- 大部分
Linux
发行版提供libcgroup
和libcgroup-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
制作过程。制作libcgroup
的rpm
方法与此类似,当然,如果你乐意,也可以将两个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 7
的libcgroup-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.conf
:cgroup
在cgconfig.conf
文件中被定义。cgred.conf
:是cgred
服务的配置文件。cgrules.conf
:包含可以确定任务何时归属于某一cgroup
的规则。
4.2 cgred的工作原理
libcgroup
通过注册cgred
系统服务实现Linux
用户资源管理,cgred
服务实际上由应用程序cgrulesengd
实现用户进程关联cgroup
资源组功能。cgrulesengd
应用程序的主要源代码对应libcgroup
的cgrulesengd.c
文件。
cgrulesengd
通过Netlink
机制获得应用程序创建与退出事件,然后根据应用程序所述用户将进程PID写入对应cgroup
组的cgroup.procs
或tasks
接口文件。
Netlink
机制作为一种内核与用户空间通信的机制,是一种特殊的socket
通信方式,对于Linux
内核与用户空间进行双向数据传输是非常好的方式,详见参考文献[7]。
4.3 海盒数据库SeaboxSQL的资源管理
开篇讨论的海盒SeaboxSQL
数据库[8],是北京东方金信科技股份有限公司积累多年数据库开发经验,打造的—款拥有完全自主知识产权、面向事务型业务处理的企业级关系型数据库。
本文讨论的利用cgroup
控制多套数据库资源使用的解决方法属于普适方案,实际上Seabox
数据库(SeaboxSQL
和SeaboxMPP
)支持资源组管理功能,可以按照数据库用户组限制多种资源配额,可以进行更细致的资源管理。
5. 参考文献
- An updated performance comparison of virtual machines and Linux containers. IBM Research. DOI:10.1109/ISPASS.2015.7095802
![]() | ![]() |