0%

cgroups 总结

cgroups 简介

cgroups,其名称源自控制组群(control groups)的简写,是 Linux 内核的一个功能,用来限制、控制与分离一个进程组的资源(如 CPU、内存、磁盘输入输出等)。

cgroups 主要功能

  1. 限制进程组可以使用的资源数量(Resource limiting)。比如:memory 子系统可以为进程组设定一个 memory 使用上限,一旦进程组使用的内存达到限额再申请内存,就会触发 OOM(out of memory)。
  2. 进程组的优先级控制(Prioritization)。比如:可以使用 cpu 子系统为某个进程组分配特定cpu share。
  3. 记录进程组使用的资源数量(Accounting)。比如:可以使用 cpuacct 子系统记录某个进程组使用的 cpu 时间
  4. 进程组隔离(Isolation)。比如:使用 ns 子系统可以使不同的进程组使用不同的 namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
  5. 进程组控制(Control)。比如:使用 freezer 子系统可以将进程组挂起和恢复。

cgroup 子系统

  1. cpu 子系统,主要限制进程的 cpu 使用率。
  2. cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  3. cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  4. memory 子系统,可以限制进程的 memory 使用量。
  5. blkio 子系统,可以限制进程的块设备 io。
  6. devices 子系统,可以控制进程能够访问某些设备。
  7. net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  8. freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
  9. 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

参考