cgroups 简介
cgroups,其名称源自控制组群(control groups)的简写,是 Linux 内核的一个功能,用来限制、控制与分离一个进程组的资源(如 CPU、内存、磁盘输入输出等)。
cgroups 主要功能
- 限制进程组可以使用的资源数量(Resource limiting)。比如:memory 子系统可以为进程组设定一个 memory 使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发 OOM(out of memory)。
- 进程组的优先级控制(Prioritization)。比如:可以使用 cpu 子系统为某个进程组分配特定cpu share。
- 记录进程组使用的资源数量(Accounting)。比如:可以使用 cpuacct 子系统记录某个进程组使用的 cpu 时间
- 进程组隔离(Isolation)。比如:使用 ns 子系统可以使不同的进程组使用不同的 namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
- 进程组控制(Control)。比如:使用 freezer 子系统可以将进程组挂起和恢复。
cgroup 子系统
- cpu 子系统,主要限制进程的 cpu 使用率。
- cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
- cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
- memory 子系统,可以限制进程的 memory 使用量。
- blkio 子系统,可以限制进程的块设备 io。
- devices 子系统,可以控制进程能够访问某些设备。
- net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
- freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
- ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。
下面重点介绍几个常用的子系统
cpu,cpuacct 子系统
root@linyouquan-test:/sys/fs/cgroup/cpu,cpuacct# ls -l | awk '{print $9}'
cgroup.clone_children
cgroup.procs # 当前 cgroup 中的所有进程 ID,系统不保证 ID 是顺序排列的,且 ID 有可能重复
cgroup.sane_behavior
cpuacct.stat
cpuacct.usage # 记录这个 cgroup 中所有任务(包括其子孙层级中的所有任务)消耗的总 CPU 时间(纳秒)。
cpuacct.usage_all
cpuacct.usage_percpu # 记录这个 cgroup 中所有任务(包括其子孙层级中的所有任务)在每个 CPU 中消耗的 CPU 时间(以纳秒为单位)。
cpuacct.usage_percpu_sys
cpuacct.usage_percpu_user
cpuacct.usage_sys
cpuacct.usage_user
cpu.cfs_period_us # 用来配置时间周期长度,取值范围为1毫秒(ms)到1秒(s)
cpu.cfs_quota_us # 用来配置当前 cgroup 在设置的周期长度内所能使用的 CPU 时间数,和 cpu.cfs_period_us 配合起来设置 CPU 的使用上限
cpu.shares # 用来设置 CPU 的相对值,并且是针对所有的 CPU(内核),默认值是1024
cpu.stat
notify_on_release
release_agent
system.slice
tasks # 当前 cgroup 中的所有线程 ID,系统不保证 ID 是顺序排列的
user.slice
cpuset 子系统
root@linyouquan-test:/sys/fs/cgroup/cpuset# ls -l | awk '{print $9}'
cgroup.clone_children
cgroup.procs
cgroup.sane_behavior
cpuset.cpu_exclusive
cpuset.cpus # 设定该 cgroup 任务可以访问的 CPU
cpuset.effective_cpus # 显示实际进程可以使用的 cpu
cpuset.effective_mems # 显示实际进程可以使用的 mem
cpuset.mem_exclusive
cpuset.mem_hardwall
cpuset.memory_migrate
cpuset.memory_pressure
cpuset.memory_pressure_enabled
cpuset.memory_spread_page
cpuset.memory_spread_slab
cpuset.mems # 设定该 cgroup 中任务可以访问的内存节点
cpuset.sched_load_balance
cpuset.sched_relax_domain_level
notify_on_release
release_agent
tasks
memory 子系统
root@linyouquan-test:/sys/fs/cgroup/memory# ls -l | awk '{print $9}'
cgroup.clone_children
cgroup.event_control
cgroup.procs
cgroup.sane_behavior
memory.failcnt
memory.force_empty
memory.kmem.failcnt
memory.kmem.limit_in_bytes
memory.kmem.max_usage_in_bytes
memory.kmem.slabinfo
memory.kmem.tcp.failcnt
memory.kmem.tcp.limit_in_bytes
memory.kmem.tcp.max_usage_in_bytes
memory.kmem.tcp.usage_in_bytes
memory.kmem.usage_in_bytes
memory.limit_in_bytes # 设定用户内存(包括文件缓存)的最大用量。
memory.max_usage_in_bytes # 报告 cgroup 中进程所用的最大内存量(以字节为单位)
memory.move_charge_at_immigrate
memory.numa_stat
memory.oom_control
memory.pressure_level
memory.soft_limit_in_bytes # hard limit 是一个硬性标准,绝对不能超过这个值,而 soft limit 可以被超越
memory.stat # 报告大范围内存统计
memory.swappiness
memory.usage_in_bytes
memory.use_hierarchy
notify_on_release
release_agent
system.slice
tasks
user.slice
安装 cgroups
本文测试环境基于 Ubuntu 18.04.5,系统自带 cgroups。Linux 把 cgroups 实现成了一个 file system,输入以下命令可以看到 cgroup 已经 mount 好了。
linyouquan@linyouquan-test:/sys/fs/cgroup$ mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
或者使用lssubsys
命令:
linyouquan@linyouquan-test:/sys/fs/cgroup$ lssubsys -m
cpuset /sys/fs/cgroup/cpuset
cpu,cpuacct /sys/fs/cgroup/cpu,cpuacct
blkio /sys/fs/cgroup/blkio
memory /sys/fs/cgroup/memory
devices /sys/fs/cgroup/devices
freezer /sys/fs/cgroup/freezer
net_cls,net_prio /sys/fs/cgroup/net_cls,net_prio
perf_event /sys/fs/cgroup/perf_event
hugetlb /sys/fs/cgroup/hugetlb
pids /sys/fs/cgroup/pids
rdma /sys/fs/cgroup/rdma
cgroups 使用
因为 hadoop yarn 系统主要是针对 cpu 和内存进行调度,所以这里我主要学习 cgroups 关于 cpu,内存的一些用法,至于 blkio 和 net_cls 等,我们就不具体展开。下面是具体实验过程。
cpu
cpu 限制
- 限制前
stress
命令可以用到100%的 cpu(tips:进入 top 页面按1,即可查看 cpu 每个核的用量)
linyouquan@linyouquan-test:~/test/cgroup$ stress -c 1
- 限制后
stress
命令只能用到50%的 cpu
root@linyouquan-test:/sys/fs/cgroup/cpu# mkdir test_cgroup
root@linyouquan-test:/sys/fs/cgroup/cpu# echo 100000 > test_cgroup/cpu.cfs_period_us
root@linyouquan-test:/sys/fs/cgroup/cpu# echo 50000 > test_cgroup/cpu.cfs_quota_us
root@linyouquan-test:/sys/fs/cgroup/cpu# echo 2294 > test_cgroup/cgroup.procs
cpu 绑核
- 测试的工具我们改成了
sysbench
,因为stress
是基于多进程的工具,压测过程会产生多个 pid,不方便我们测试,而sysbench
是基于多线程,只有一个 pid。 - 绑核前查看 cpu 信息
linyouquan@linyouquan-test:~$ cat /proc/cpuinfo | grep -i processor
processor : 0
processor : 1
processor : 2
processor : 3
- 开始 cpu 压测,发现
sysbench
把4个 cpu 核都用满了
linyouquan@linyouquan-test:~$ sysbench --test=cpu --cpu-max-prime=2000000000 --threads=8 run
- 把
sysbench
进程绑定到2号核上,最终看到sysbench
只用到了2号核
root@linyouquan-test:/sys/fs/cgroup/cpuset# mkdir test
root@linyouquan-test:/sys/fs/cgroup/cpuset# cd test/
root@linyouquan-test:/sys/fs/cgroup/cpuset/test# echo 2 > cpuset.cpus
root@linyouquan-test:/sys/fs/cgroup/cpuset/test# echo 0 > cpuset.mems
root@linyouquan-test:/sys/fs/cgroup/cpuset/test# echo 2377 > cgroup.procs
内存
内存限制
- 限制前
stress
命令正常运行
linyouquan@linyouquan-test:~/test/cgroup$ stress --vm 1 --vm-bytes 256M
stress: info: [2452] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
- 开始限制
root@linyouquan-test:/sys/fs/cgroup/memory# mkdir test
root@linyouquan-test:/sys/fs/cgroup/memory# cd test/
root@linyouquan-test:/sys/fs/cgroup/memory/test#
root@linyouquan-test:/sys/fs/cgroup/memory/test# echo 1024000 > memory.limit_in_bytes
root@linyouquan-test:/sys/fs/cgroup/memory/test# echo 0 > memory.swappiness
root@linyouquan-test:/sys/fs/cgroup/memory/test# echo 2453 > cgroup.procs
- 限制后
stress
命令因为 OOM 退出
linyouquan@linyouquan-test:~/test/cgroup$ stress --vm 1 --vm-bytes 256M
stress: info: [2452] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: FAIL: [2452] (415) <-- worker 2453 got signal 9
stress: WARN: [2452] (417) now reaping child worker processes
stress: FAIL: [2452] (451) failed run completed in 143s
查看进程使用了哪些 cgroup
eg:进程1806在 cpu,cpuacct 子系统绑定了 test 目录
root@linyouquan-test:/sys/fs/cgroup/cpu/test# cat /proc/1806/cgroup
12:freezer:/
11:perf_event:/
10:devices:/user.slice
9:memory:/user.slice
8:hugetlb:/
7:rdma:/
6:cpuset:/
5:blkio:/user.slice
4:net_cls,net_prio:/
3:pids:/user.slice/user-1000.slice/session-6.scope
2:cpu,cpuacct:/test
1:name=systemd:/user.slice/user-1000.slice/session-6.scope
0::/user.slice/user-1000.slice/session-6.scope