什么是Cgroups?
Cgroups提供了对一组进程及将来子进程的资源限制、控制和统 计的能力,这些资源包括 CPU、内存、存储、网络等 。 通过 Cgroups
,可以方便地限制某个进 程的资源占用,并且可以实时地监控进程的监控和统计信息 。
Cgroups
的接口是通过操作一个虚拟文件系统实现,一般挂载在/sys/fs/cgroup
下。
Cgroups的组成
Cgroups
包括三个组件:
-
cgroup
是对进程分组的一种管理机制,一个cgroup
包含一组进程。 -
subsystem
是一组资源控制的模块,Cgroups
依靠它来限制进程的资源管理,包括:
blkio 设置对块设备(比如硬盘)输入输出的访问控制 。
cpu 设置 cgroup 中进程的 CPU 被调度的策略。
cpuacct 可以统计cgroup中进程的 CPU 占用 。
cpuset 在多核机器上设置 cgroup 中进程可以使用的 CPU 和内存(此处内存仅使用于
NUMA 架构) 。
devices 控制 cgroup 中进程对设备的访问 。
freezer 用于挂起( suspend)和恢复( resume) cgroup 中的进程 。
memory 用于控制 cgroup 中进程的内存占用 。
net_els 用于将 cgroup 中进程产生的网络包分类,以便 Linux 的 tc (traffic con位oller)可
以根据分类区分出来自某个 cgroup 的包并做限流或监控 。
net_prio 设置 cgroup 中进程产生的网络流量的优先级 。
ns 这个 subsystem 比较特殊,它的作用是使 cgroup 中的进程在新的 Namespace 中 fork
新进程 CNEWNS)时,创建出一个新的 cgroup,这个 cgroup包含新的 Namespace 中
的进程 。 -
hierarchy
是一组树状关系的cgroup
,其中的cgroup
有继承关系。
Cgroup的使用
挂载hierarchy
mkdir test-cgroup #创建一个hierarchy的挂载点
mount -t cgroup -o none,name=test-group none test-cgroup #挂载一个hierarchy到这个目录
ls ./test-cgroup #查看这个目录下的文件
cg1 cgroup.clone_children cgroup.sane_behavior release_agent
cg2 cgroup.procs notify_on_release tasks
这些文件是刚刚创建的hierarchy
的根cgroup
,每创建一个新的hierarchy
都会默认创建一个根cgroup
,系统中的所有进程都会加入到这个根cgroup
中。
可以从文件cgroup.procs中看到系统的所用进程组,在tasks中可以看到所有进程的PID以验证上面的说法。
创建cgroup
cd test-cgroup #到挂载了hierarchy的目录中
mkdir cg1 #创建两个新目录
mkdir cg2 #这两个目录相当于在这个hierarchy中创建了两个子cgroup
tree #查看目录树
.
|-- cg1
| |-- cgroup.clone_children
| |-- cgroup.procs
| |-- notify_on_release
| `-- tasks
|-- cg2
| |-- cgroup.clone_children
| |-- cgroup.procs
| |-- notify_on_release
| `-- tasks
|-- cgroup.clone_children
|-- cgroup.procs
|-- cgroup.sane_behavior
|-- notify_on_release
|-- release_agent
`-- tasks
tree的输出可以看到新创建的cg1、cg2中自动创建了cgroup的文件,也就意味这在上一步的根root
之下创建了两个同级的cgroup。
cg1、cg2的cgroup.procs和tasks中没有任何内容,说明手动创建的非根cgroup
不会默认添加任何进程和进程组。
使用subsystem
mount|grep cgroup #查看挂载,在什么是Cgroups中提到,Cgroups的本质是操作一个挂载类型为cgroup的虚拟文件系统
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
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/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
none on /root/test-cgroup type cgroup (rw,relatime,name=test-group)
从输出看到/sys/fs/cgroup的文件系统是tmpfs,而其中的多个目录的文件系统是cgroup,test-cgroup也一样。这说明了/sys/fs/cgroup/中的每一个目录对应一个hierarchy
,这些hierarchy
是系统默认创建的,其中每一个hierarchy
默认对应了一个subsystem
,从目录名可以看出本别是cpuacct、memory等等。
ls /sys/fs/cgroup/memory #查看系统创建的名为memory的hierarchy
cgroup.clone_children memory.max_usage_in_bytes
cgroup.event_control memory.move_charge_at_immigrate
cgroup.procs memory.numa_stat
cgroup.sane_behavior memory.oom_control
init.scope memory.pressure_level
memory.failcnt memory.soft_limit_in_bytes
memory.force_empty memory.stat
memory.kmem.failcnt memory.swappiness
memory.kmem.limit_in_bytes memory.usage_in_bytes
memory.kmem.max_usage_in_bytes memory.use_hierarchy
memory.kmem.slabinfo notify_on_release
memory.kmem.tcp.failcnt ram_cg1
memory.kmem.tcp.limit_in_bytes release_agent
memory.kmem.tcp.max_usage_in_bytes system.slice
memory.kmem.tcp.usage_in_bytes tasks
memory.kmem.usage_in_bytes user.slice
memory.limit_in_bytes
系统的名为memory的hierarchy
中包含了许多memory类型的subsystem
生成的文件,意味着这个hierarchy
附加了一个memory类型的subsystem
。而之前创建的test-cgroup没有附加任何的subsystem
,所以也就没有这些subsystem
相关的文件。
cd /sys/fs/cgroup/memory
mkdir test-memory #在这个hierarchy中创建一个子cgroup
cd test-memory && ls ##查看新的cgroup的内容
cgroup.clone_children memory.limit_in_bytes
cgroup.event_control memory.max_usage_in_bytes
cgroup.procs memory.move_charge_at_immigrate
memory.failcnt memory.numa_stat
memory.force_empty memory.oom_control
memory.kmem.failcnt memory.pressure_level
memory.kmem.limit_in_bytes memory.soft_limit_in_bytes
memory.kmem.max_usage_in_bytes memory.stat
memory.kmem.slabinfo memory.swappiness
memory.kmem.tcp.failcnt memory.usage_in_bytes
memory.kmem.tcp.limit_in_bytes memory.use_hierarchy
memory.kmem.tcp.max_usage_in_bytes notify_on_release
memory.kmem.tcp.usage_in_bytes tasks
memory.kmem.usage_in_bytes
echo $$ >> tasks #将当前shell的进程纳入了这个cgroup中
echo 100m >> memory.limit_in_bytes #限制这个cgroup使用100m内存
上面的操作在系统创建的的hierarchy
中创建一个附加了memory的subsystem
的cgroup
,并限制了100m的内存使用。
stress --vm-bytes 200m --vm-keep -m 1 #使用stress工具测试内存占用200m内存 stess启动失败
stress --vm-bytes 90m --vm-keep -m 1 #使用stress工具测试内存占用90m内存 stess能够启动成功
通过stress可以验证这个cgroup
的subsystem
成功把加入的进程内存占用在了100m。
docker
docker run -itd m 128m ubuntu #启动一个容器 限制128m内存
fc3e9a13ee2c5bc528ae59748cee00443469b2c87bcb4c752e7cfe0eb39df121
cd /sys/fs/cgroup/memory/docker && ls #查看docker在memory中创建的cgroup
cgroup.clone_children memory.kmem.limit_in_bytes memory.kmem.usage_in_bytes memory.soft_limit_in_bytes
cgroup.event_control memory.kmem.max_usage_in_bytes memory.limit_in_bytes memory.stat
cgroup.procs memory.kmem.slabinfo memory.max_usage_in_bytes memory.swappiness
fc3e9a13ee2c5bc528ae59748cee00443469b2c87bcb4c752e7cfe0eb39df121 memory.kmem.tcp.failcnt memory.move_charge_at_immigrate memory.usage_in_bytes
memory.failcnt memory.kmem.tcp.limit_in_bytes memory.numa_stat memory.use_hierarchy
memory.force_empty memory.kmem.tcp.max_usage_in_bytes memory.oom_control notify_on_release
memory.kmem.failcnt
cd c3e9a13ee2c5bc528ae59748cee00443469b2c87bcb4c752e7cfe0eb39df121 && ls
cgroup.clone_children memory.kmem.failcnt memory.kmem.tcp.limit_in_bytes memory.max_usage_in_bytes memory.soft_limit_in_bytes notify_on_release
cgroup.event_control memory.kmem.limit_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.move_charge_at_immigrate memory.stat tasks
cgroup.procs memory.kmem.max_usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.numa_stat memory.swappiness
memory.failcnt memory.kmem.slabinfo memory.kmem.usage_in_bytes memory.oom_control memory.usage_in_bytes
memory.force_empty memory.kmem.tcp.failcnt memory.limit_in_bytes memory.pressure_level memory.use_hierarchy
cat memory.limit_in_bytes
134217728
可以分析出docker容器限制内存的基本原理如下:
- docker在memory
hierarchy
中创建名为docker的子cgroup
。 - 创建一个附加了-m参数的容器时,在memory/docker下创建一个新的
cgroup
,将-m限制的参数写入memory.limit_in_bytes。 - 将容器的PID写入tasks,实现内存限制。