简单介绍
在基于 Linux-3.x 内核版本的很多发行版都提供了 Systemd 来管理系统和服务. 同时也将 cgroup 功能加到了 slice, scope 和 service 三个单元中, 详见 sec-Default_Cgroup_Hierarchies. 基于这些特性我们可以很方便的通过 systemd 来限制服务或者进程对系统资源的使用, 这在单主机多服务的场景下会很有用. 下面则以 MySQL 服务为例介绍如何使用 systemd 限制资源的使用, 其它服务的限制和此等同.
示例使用
以 Centos7 系统为例, 从 redhat-resource-control 的文档来看, 官方建议通过 service 来实现资源的限制, 所以这里我们增加可以带端口参数(如果单台主机有多个 MySQL 实例的话)启动的 mysql 服务:
# cat /usr/lib/systemd/system/[email protected]
[Unit]
Description=MySQL Server node%i
[Service]
Type=forking
Environment="PORT_ARGS=%I"
PermissionsStartOnly=true
ExecStart=/usr/local/mysqlnode/bin/node ${PORT_ARGS} start
ExecStop=/usr/local/mysqlnode/bin/node ${PORT_ARGS} stop
[Install]
WantedBy=multi-user.target
上述服务以 fork 方式启动服务, @ 符号之后的端口号即为相应的端口参数, 启动后查看对应服务状态:
# systemctl start mysql@3327
# systemctl status mysql@3327
● [email protected] - MySQL Server node3327
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; vendor preset: disabled)
Active: active (running) since Fri 2018-12-07 21:31:32 CST; 4s ago
Process: 58084 ExecStart=/usr/local/mysqlnode/bin/node ${PORT_ARGS} start (code=exited, status=0/SUCCESS)
Main PID: 58159 (mysqld_safe)
CGroup: /system.slice/system-mysql.slice/[email protected]
├─58159 /bin/sh /opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101//bin/mysqld_safe --defaults-file=/export/mysql/node3327/my.node.cnf
├─59489 /opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101/bin/mysqld --defaults-file=/export/mysql/node3327/my.node.cnf --basedir=/opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101 --datadir=/export/mysql/node3327/...
└─59490 logger -t mysqld-3327 -p daemon.error
通过 systemd 增加 cgroup 限制,可以一次设置单项属性值, 也可以一次设置多项:
# systemctl set-property [email protected] MemoryLimit=5G # 5G 内存
# systemctl set-property [email protected] CPUQuota=150% # 150% cpu 使用率
# systemctl set-property [email protected] BlockIOWeight=1000 # IO 权重
# systemctl status mysql@3327
● [email protected] - MySQL Server node3327
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; vendor preset: disabled)
Drop-In: /etc/systemd/system/[email protected]
└─50-MemoryLimit.conf
Active: active (running) since Fri 2018-12-07 21:31:32 CST; 1min 18s ago
Process: 58084 ExecStart=/usr/local/mysqlnode/bin/node ${PORT_ARGS} start (code=exited, status=0/SUCCESS)
Main PID: 58159 (mysqld_safe)
Memory: 1.3M (limit: 5G) # 内存限制 5G
CGroup: /system.slice/system-mysql.slice/[email protected]
├─58159 /bin/sh /opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101//bin/mysqld_safe --defaults-file=/export/mysql/node3327/my.node.cnf
├─59489 /opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101/bin/mysqld --defaults-file=/export/mysql/node3327/my.node.cnf --basedir=/opt/Percona-Server-5.6.38-rel83.0-Linux.x86_64.ssl101 --datadir=/export/mysql/node3327/...
└─59490 logger -t mysqld-3327 -p daemon.error
更多属性参见 man systemd.resource-control
, 不过一些参数没有对应的属性, 需要手动单独设置, 比如设置单独某个服务的 memory + swap
限制, 可以使用以下命令将限制的字节数写到对应的参数文件中:
echo xxxxxx > memory.memsw.limit_in_bytes
查看 MySQL 进程 cgroup 信息, 可以看到 memory, blkid, cpuacct 三个条目对应的信息:
# cat /proc/59489/cgroup
11:memory:/system.slice/system-mysql.slice/[email protected]
10:devices:/system.slice/system-mysql.slice
9:cpuset:/
8:blkio:/system.slice/system-mysql.slice/[email protected]
7:perf_event:/
6:hugetlb:/
5:freezer:/
4:cpuacct,cpu:/system.slice/system-mysql.slice
3:pids:/system.slice/system-mysql.slice
2:net_prio,net_cls:/
1:name=systemd:/system.slice/system-mysql.slice/[email protected]
查看进程相关的限制值:
# cat /sys/fs/cgroup/memory/system.slice/system-mysql.slice/[email protected]/memory.limit_in_bytes
5368709120
# cat /sys/fs/cgroup/blkio/system.slice/system-mysql.slice/[email protected]/blkio.weight
1000
# cat /sys/fs/cgroup/cpu/system.slice/system-mysql.slice/[email protected]/cpu.cfs_quota_us
150000
简单验证
这里仅以不同的 CPUQuota
为例说明, 如下所示可以看到, MySQL 进程在不同 CPUQuota 限制下的不同表现, benchyou
的压测结果也相差较大.
限制进程 CPUQuota 为 150% 的情况下:
# top -p 59489
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
59489 mysql 20 0 3412052 1.468g 8784 S 149.5 2.3 7:08.22 mysqld
# benchyou --oltp-tables-count=256 --read-threads=30 --update-threads 8 --write-threads 6 --delete-threads=5 --mysql-table-engine=innodb ..
time thds tps wtps rtps rio rio/op wio wio/op rMB rKB/op wMB wKB/op cpu/op freeMB cacheMB w-rsp(ms) r-rsp(ms) total-number
[13s] [r:30,w:6,u:8,d:5] 3323 304 3019 0 0.00 0 0.00 0.00 0.00 0.00 0.00 0.00 0 0 62.56 9.92 40765
time thds tps wtps rtps rio rio/op wio wio/op rMB rKB/op wMB wKB/op cpu/op freeMB cacheMB w-rsp(ms) r-rsp(ms) total-number
[14s] [r:30,w:6,u:8,d:5] 3319 325 2994 0 0.00 0 0.00 0.00 0.00 0.00 0.00 0.00 0 0 57.21 9.99 44084
限制进程 CPUQuota 为 400% 情况下:
# top -p 59489
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
59489 mysql 20 0 3416472 1.474g 8796 S 400.0 2.4 7:47.42 mysqld
# benchyou --oltp-tables-count=256 --read-threads=30 --update-threads 8 --write-threads 6 --delete-threads=5 --mysql-table-engine=innodb ..
time thds tps wtps rtps rio rio/op wio wio/op rMB rKB/op wMB wKB/op cpu/op freeMB cacheMB w-rsp(ms) r-rsp(ms) total-number
[10s] [r:30,w:6,u:8,d:5] 10547 967 9580 0 0.00 0 0.00 0.00 0.00 0.00 0.00 0.00 0 0 19.49 3.12 96270
time thds tps wtps rtps rio rio/op wio wio/op rMB rKB/op wMB wKB/op cpu/op freeMB cacheMB w-rsp(ms) r-rsp(ms) total-number
[11s] [r:30,w:6,u:8,d:5] 10829 1036 9793 0 0.00 0 0.00 0.00 0.00 0.00 0.00 0.00 0 0 18.23 3.04 107099
会话级别限制
如果没有以 service 启动, 可以按照 pid 查看会话级别的信息:
# systemctl status 303 # 303 为 mysql 进程 id
● session-31208.scope - Session 31208 of user root
session-31208.scope
即为会话级别的信息, 一个会话可能包含多个 MySQL 进程, 使用上述的 systemctl set-property session-31208.scope XXXX
即可对整个会话的进程进行相关的限制. 当然使用这种方式即是对多个进程总体使用情况的限制.
其它限制
如果要单独限制 1 个进程 id, 则需要借助 libcgroup
提供的工具, 具体的使用同 Centos6 的设置, 不过 Centos7 已经弃用了 libgcgroup-tools
, 不再建议使用, 如果要对单独进程进行限制, 最好使用 service
进行启动. 另外从 systemd
提供的 ControlGroupInterface 来看, 其提供的控制选项还不够精细, 比如没有 memsw, cpuset 等设置的属性, 这种情况下如果有限制的需求, 还需要靠和以前一样的 libcgroup-tool 方式来设置, 比如以下设置, 限制 pid 为 59489 的进程仅使用 cpu 0 ~ 5, mem 可以是 cpu node0 或 node1, cgclassify 没有指定 --sticky
选项的话则 tasks 会包含指定进程的子进程信息:
# cgcreate -g cpuset:[email protected]
# cgset -r cpuset.cpus=0-5 [email protected]
# cgset -r cpuset.mems=0-1 [email protected]
# cgclassify -g cpuset:[email protected] 59489
当然也可以使用红帽知识库的方式在启动进程的时候就限制相应的 cpuset 等选项, 详见 redhat-1445073