在同一套物理主机环境中,如果部署多套数据库软件,常常面临资源隔离/限制需求,按照Linux
用户来隔离资源是一种常见的解决方案。然而,我们发现网上搜索到的资料绝大部分过于陈旧,仅适用于Redhat/CentOS Linux 7
等比较旧的操作系统,当我们在Redhat/CentOS Linux 8
系统上部署时,方法根本不可用。本文试图探索其中的原因,给出多种解决方法。虽然本文以SeaboxSQL
数据库为例探讨多套应用程序的资源隔离问题,但是,其方法是普适性的。方法在Redhat 8.5
上验证通过,同样适用于CentOS 8
。
在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]:
不幸的是,当你在Redhat Linux 8
或者其它更新的Linux
发行版(例如统信UOS V20
)上使用该方法时,却遇到了致命问题:关键服务cgred
(对应cgrulesengd
应用)已经被踢出libcgroup-tools
包了。例如,在Redhat Linux 8.5
上,即使已经安装了libcgroup
和libcgroup-tools
包的情况下,cgred
服务仍然不存在:
[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
的变更日志,有以下记录:
[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]。
本文提供三种方法,但是每种方法都有其局限性,请权衡使用。
Redhat Linux 8
从libcgroup
包移除cgred
服务,变更日志提到:
please use Control Group Interface in Systemd instead
看来官方是希望用systemd
来替代cgred
,因此,首先想到使用systemd
提供的接口来控制用户资源。以下通过一个具体例子来演示如何用systemd
控制用户资源。
假设,欲限制Linux
用户seabox
的CPU使用上限为30%
(即,单个CPU核的30%
)。步骤如下:
(1). 获取用户UID
[root@bogon ~]# id seabox uid=1002(seabox) gid=1002(fairyfar) groups=1002(seabox)
(2). 设置配额
[root@bogon ~]# systemctl set-property user-1002.slice CPUQuota=30%
如果命令报以下错误:
[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
命令:
[root@bogon ~]# systemctl start user-1002.slice
(3). 查看配置情况
[root@bogon ~]# systemctl cat user-1002.slice # /etc/systemd/system/user-1002.slice.d/50-CPUQuota.conf [Slice] CPUQuota=30%
(4). 使配置立即生效
[root@bogon ~]# systemctl daemon-reload
上述实例中,systemctl set-property
命令中使用了参数CPUQuota
,表示CPU使用率上限,30%
表示单个CPU核的30%
使用率。systemctl
支持很多种参数,用于控制不同类型资源。详见参考文献[5]。
需要特别注意的是,systemctl
支持的参数种类与systemd
版本有关,查看systemd
版本方法:
[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核功能。
在“实例”小节中,演示了配置资源限制,那么如何删除限制呢?有两种方法:
如果systemd
版本大于等于229,则可以使用systemctl revert
命令:
[root@bogon ~]# systemctl revert user-1002.slice
否则,低版本需要手动删除:
[root@bogon ~]# rm -rf /etc/systemd/system/user-1002.slice.d/
删除的路径在systemctl cat
结果中可以找到,当然也可以有选择性地删除部分配置项。
主要有两方面局限。
(1). 支持的参数类型受限于systemd
版本。
Redhat Linux 8
默认的systemd
版本支持的参数类型很有限,一些常用的资源控制尚未支持。理所当然,笔者认为:
Redhat Linux 8
从libcgroup
中移除cgred
,似乎操之过急。
(2). 用户登录方式影响systemd
控制资源的有效性。
本方法可以控制用户通过GDM
(The GNOME Display Manager
)或ssh
登录时的资源使用,但是不适用于su
方式切换登录的用户。例如,以下方式登录的用户,不受上述systemd
配置的资源限制。
[root@bogon ~]# whoami root [root@bogon ~]# su - seabox # su方式登录的用户,资源不受限制。 [seabox@bogon ~]$ do someting
本方法支持cgroup v2
。
从Redhat 7
获取rpm
包,然后安装到Redhat 8
。
(1). 从Redhat 7
获取rpm
包
在Redhat 7
上,执行以下yum
命令,只下载包但不安装:
[root@bogon ~]# yum install --downloadonly --downloaddir=/tmp/ libcgroup [root@bogon ~]# yum install --downloadonly --downloaddir=/tmp/ libcgroup-tools
为便于下载,笔者已经将两个rpm
包放至以下位置:
(2). 卸载Redhat 8
默认的libcgroup
[root@bogon ~]# yum remove libcgroup-tools [root@bogon ~]# yum remove libcgroup
(3). 在Redhat 8
上安装rpm
包
[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
本方法在下列系统上验证通过:
Redhat/CentOS Linux 8.5
UnionTech OS Server release 20 (kongzi)
openEuler release 20.03 (LTS-SP3)
其它版本Linux
是否可行,有待验证。
本方法不支持cgroup v2
,因为Redhat Linux 7
的libcgroup
版本为v0.41
,尚未支持cgroup v2
。如果启用了cgroup v2
,则启动cgred
服务报错:
[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.
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
安装包提供应用程序和系统服务。
从libcgroup
项目下载源代码,自行编译libcgroup
,以Redhat Linux 8.5
环境为例。
(1). 下载源码,然后解压缩。
[root@bogon ~]# wget https://github.com/libcgroup/libcgroup/releases/download/v0.41/libcgroup-0.41.tar.bz2
(2). 准备编译环境
安装依赖库,并创建安装路径:
[root@bogon ~]# yum install pam-devel [root@bogon ~]# mkdir /root/libcgroup
(3). 编译
[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
目录下查看编译结果。
上一小节,我们自行编译,得到了需要的cgrulesengd
应用程序,其实现在我们可以仿照Redhat 7
中的libcgroup-tools
手动创建cgred
服务。但是,这样不便于维护。如果把编译好的文件制作成rpm安装包,那么就可以一键安装好全部工具和服务。
下面以libcgroup-tools
为例演示rpm
制作过程。制作libcgroup
的rpm
方法与此类似,当然,如果你乐意,也可以将两个rpm
二合一。
(1). 准备环境
安装rpm-build
(有的发行版Linux
中名称为rpmbuild
)和rpmdevtools
:
[root@bogon ~]# yum install rpm-build [root@bogon ~]# yum install rpmdevtools
(2). 创建rpmbuild
目录结构
使用rpmdev-setuptree
工具创建目录。当然,rpmbuild
目录及其子目录也可以手动mkdir
创建。默认情况下,目录位置和名称都是确定的,没有必要刻意去修改。
[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/
目录:
[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). 执行打包命令
[root@bogon rpmbuild]# rpmbuild -bb ./SPECS/libcgroup-tools-0.41-19.el8.x86_64.spec
打包成功后,rpm
包将生成在rpmbuild/RPMS
目录下。
[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
安装包。
(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
警告可以忽略。
过程相对繁琐,容易出错。但是,可以在任意发行版Linux
上编译,与系统契合度更高,可按需自由定制。
本方法是否支持cgroup v2
取决于我们编译的libcgroup
版本,以上示例我们是从libcgroup v0.41
版本源码编译的,所以不支持cgroup v2
,如果想支持cgroup v2
,需要编译libcgroup v2.0
以上版本。
经笔者验证,在Redhat Linux 8.5
系统上,libcgroup v2.0
可用。
本章节罗列一些背景资料,可选择性阅读。
摘自参考文献[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
的规则。
libcgroup
通过注册cgred
系统服务实现Linux
用户资源管理,cgred
服务实际上由应用程序cgrulesengd
实现用户进程关联cgroup
资源组功能。cgrulesengd
应用程序的主要源代码对应libcgroup
的cgrulesengd.c
文件。
cgrulesengd
通过Netlink
机制获得应用程序创建与退出事件,然后根据应用程序所述用户将进程PID写入对应cgroup
组的cgroup.procs
或tasks
接口文件。
Netlink
机制作为一种内核与用户空间通信的机制,是一种特殊的socket
通信方式,对于Linux
内核与用户空间进行双向数据传输是非常好的方式,详见参考文献[7]。
开篇讨论的海盒SeaboxSQL
数据库[8],是北京东方金信科技股份有限公司积累多年数据库开发经验,打造的—款拥有完全自主知识产权、面向事务型业务处理的企业级关系型数据库。
本文讨论的利用cgroup
控制多套数据库资源使用的解决方法属于普适方案,实际上Seabox
数据库(SeaboxSQL
和SeaboxMPP
)支持资源组管理功能,可以按照数据库用户组限制多种资源配额,可以进行更细致的资源管理。