0%

Linux cgroup

Linux cgroup

全文基于 CentOS 7.4

cgroup 是 Linux kernel 的一项功能,用来做资源限制的
systemd 是管理进程的
cgroup 层级系统+ systemd 可以把资源管理设置从进程级别移至应用程序级别

本文首先将介绍如何在systemd 中对应用做资源限制
然后介绍如何手动配置 cgroup

systemd 中 cgroup 的默认层级

默认情况下,systemd 会在cgroup下自动创建 slice、scope 和 service 单位的层级

  • name.service
    一个或一组进程
  • name.scope
    一组外部创建的进程
  • parent-name.slice
    一组按层级排列的单位。slice 并不包含进程
    子层标识父的名称

使用 systemd-cgls 可以查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├─1 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
├─docker
│ └─60684a93a27ee26bcc5dd062bb2d453ee9a409d665e01577f0061a0fe2cfd40a
│ └─2922 mysqld
├─user.slice
│ └─user-0.slice
│ └─session-79.scope
│ ├─4527 sshd: root@pts/0
│ ├─4529 -bash
│ ├─4584 systemd-cgls
│ └─4585 less
└─system.slice
├─docker.service
│ ├─2671 /usr/bin/dockerd
│ ├─2683 docker-containerd -l unix:///var/run/docker/libcontainerd/docker-containerd.sock --metrics-interval=0 --start-timeout 2m --state-dir /var/run/docker/libcontainerd/containerd --sh
│ ├─2898 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 3306 -container-ip 172.20.0.2 -container-port 3306
│ └─2905 docker-containerd-shim 60684a93a27ee26bcc5dd062bb2d453ee9a409d665e01577f0061a0fe2cfd40a /var/run/docker/libcontainerd/60684a93a27ee26bcc5dd062bb2d453ee9a409d665e01577f0061a0fe2cf

slice 是个逻辑分组,service、scope才是进程运行的地方

在systemd 中使用 cgroup

首先systemd 启动进程分为临时、永久两种方式

临时:

1
systemd-run --unit=name --scope --slice=slice_name command

使用systemd 管理进程

创建一个测试例子,来验证cgroup 对cpu的限制

1
2
3
4
5
6
tee /usr/lib/systemd/system/test.service<<EOF
[Service]
ExecStart=/usr/bin/stress --cpu 10
EOF
systemctl daemon-reload
systemctl restart test

这样启动后通过top可观察到cpu占用为100%

设置资源限制

使用命令去修改 cgroup

1
systemctl set-property --runtime test CPUQuota=10%

如果不添加 runtime 参数,这个资源限制会被持久化到文件

1
2
3
[root@localhost ~]# cat /etc/systemd/system/test.service.d/50-CPUQuota.conf 
[Service]
CPUQuota=10%

这时候可直接观察到cpu占用降低到10%

这里列下常用配置

1
2
3
4
5
6
7
8
[Service]
# 默认 1024 代表 1core 相对使用率
CPUShares=1500
# 占用比例 按单核算
CPUQuota=110%

# 最大可用内存 单位(K、M、G、T )
MemoryLimit=1G

更多详细配置参考修改 CGROUP

cgroup = v1

cgroup 的手动配置

内核使用文件系统来配置cgroup

hierarchy(层级)

通过虚拟的文件系统,维护了一个树
描述了cgroup之间的从属关系,这种结构意味着配置是可以继承的
通过写入树下的文件,对cgroup配置进行修改

这里只展示创建 cgroup 的层级关系,把进程添加到里面去(不包含资源限制)

创建

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建目录,并且挂载为 cgroup 类型
mkdir cgroup-test
mount -t cgroup -o none,name=cgroup-test cgroup-test ./cgroup-test

# 挂载后可以看到里面创建的文件
[root@localhost ~]# ls -1 ./cgroup-test/
cgroup.clone_children # 默认0,如果1,那么子cgroup会继承父的配置
cgroup.event_control #
cgroup.procs # 包含系统里所有进程
cgroup.sane_behavior
notify_on_release
release_agent
tasks # 当前 cgroup 下进程

注意到 -o none 意味着这个挂载后目录是虚拟的

创建子cgroup

只需要在cgroup-test中 创建目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@localhost cgroup-test]# mkdir cgroup-1
[root@localhost cgroup-test]# mkdir cgroup-2
[root@localhost cgroup-test]# tree
.
├── cgroup-1
│ ├── cgroup.clone_children
│ ├── cgroup.event_control
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup-2
│ ├── cgroup.clone_children
│ ├── cgroup.event_control
│ ├── cgroup.procs
│ ├── notify_on_release
│ └── tasks
├── cgroup.clone_children
├── cgroup.event_control
├── cgroup.procs
├── cgroup.sane_behavior
├── notify_on_release
├── release_agent
└── tasks

2 directories, 17 files

cgroup中添加进程

系统所有进程都默认挂在根节点上
添加进程通过向 tasks 写入PID

1
echo $$ >> cgroup-1/tasks

这样就可以加入到 cgroup-1

一个进程可以加入到多个hierarchy

之前提到systemd 维护的层,在文件系统里/sys/fs/cgroup 下面
/sys/fs/cgroup/systemd/sys/fs/cgroup/cpu 这些目录都是cgroup的层级

subsystem

资源控制的模块,每个模块下会有一个cgroup的hierarchy
通过在不同的hierarchy中对文件进行配置达到资源限制的效果

查看系统支持的模块

1
2
3
4
5
6
7
8
9
10
11
12
13
yum install -y libcgroup-tools

[root@localhost ~]# lssubsys -a
cpuset
cpu,cpuacct
memory
devices
freezer
net_cls,net_prio
blkio
perf_event
hugetlb
pids

这些模块会对指定的资源做限制,配置路径在/sys/fs/cgroup/<cpu>

资源限制

之前在systemd里面对CPU做了限制,那么再去看下真正生效的地方
那么查看文件路径可以通过systemd-cgls找到

1
2
3
4
5
└─system.slice
├─test.service
│ ├─6094 /usr/bin/stress --cpu 10
│ └─6095 /usr/bin/stress --cpu 10

/sys/fs/cgroup/cpu/system.slice/test.service/

1
2
3
4
5
6
7
8
9
10
# cat tasks 
6094
6095
# 可以看到两个进程确实加入到这个cgroup中

# cat cpu.cfs_period_us
100000
# cat cpu.cfs_quota_us
10000

cpu.cfs_period_us: 指cgroup对资源调度周期 (微秒)
cpu.cfs_quota_us: 在一个周期(cpu.cfs_period_us定义),可以占用cpu的时间
一个周期内能占用多少cpu = cpu.cfs_quota_us / cpu.cfs_period_us
也就是设置的10%

然后设置个内存限制看看
systemctl set-property --runtime test MemoryLimit=128M
就可以在下面路径看到

1
2
cat /sys/fs/cgroup/memory/system.slice/test.service/memory.limit_in_bytes 
134217728

systemd 里面参数和内核中配置对应关系

systemd option cgroup
CPUShares cpu.shares
CPUQuota cpu.cfs_quota_us
MemoryLimit memory.limit_in_bytes

Docker

docker 会给每个容器创建 cgroup,以cpu为例就是在
/sys/fs/cgroup/cpu/docker/

参考

资源管理指南
CPU调度参数