docker-Cgroups

什么是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.procstasks中没有任何内容,说明手动创建的非根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,从目录名可以看出本别是cpuacctmemory等等。

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中创建一个附加了memorysubsystemcgroup,并限制了100m的内存使用。

stress --vm-bytes 200m --vm-keep -m 1 #使用stress工具测试内存占用200m内存 stess启动失败

stress --vm-bytes 90m --vm-keep -m 1 #使用stress工具测试内存占用90m内存 stess能够启动成功

通过stress可以验证这个cgroupsubsystem成功把加入的进程内存占用在了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容器限制内存的基本原理如下:

  1. docker在memoryhierarchy中创建名为docker的子cgroup
  2. 创建一个附加了-m参数的容器时,在memory/docker下创建一个新的cgroup,将-m限制的参数写入memory.limit_in_bytes
  3. 将容器的PID写入tasks,实现内存限制。

你可能感兴趣的:(docker-Cgroups)