容器基础之 cgroups 学习
容器基础之 cgroups 学习
cgroups 简单介绍
老规矩,按照国际惯例,先百度、谷歌下这到底是个啥?到底有啥用?
Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(cpu、memory、IO 等)。2006 的时候,Google 的一些工程师(主要是 Paul Menage 和 Rohit Seth)启动了这个项目,最初的名字叫 process containers。因为 container 在内核中名字有歧义,2007 的时候改名为 control groups,并合并到 2008 年发布的 2.6.24 内核版本。最初 cgroups 的版本被称为 v1,这个版本的 cgroups 设计并不友好,理解起来非常困难。后续的开发工作由 Tejun Heo 接管,他重新设计并重写了 cgroups,新版本被称为 v2,并首次出现在 kernel 4.5 版本。
目前 cgroups 已经成为了很多技术的基础,比如:LXC、docker、systemd
有了 cgroups 这个技术,就可以很方便的限制 CPU、内存等资源,所以了解、掌握 cgroups 技术对我们学习容器是非常有必要的。
概念及原理
cgroups 主要作用
实现 cgroups 的主要目的是为不同用户层面的资源管理提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,cgroups 提供了四大功能:
- 资源限制:cgroups 可以对任务资源总额进行限制。比如设定任务运行时使用的内存上限,一旦超出就发 OOM。
- 优先级分配:通过分配的 CPU 时间片数量和磁盘 IO 带宽,实际上就等同于控制了任务运行的优先级。
- 资源统计:cgoups 可以统计系统的资源使用量,比如 CPU 使用时长、内存用量等。这个功能非常适合当前云端产品按使用量计费的方式。
- 任务控制:cgroups 可以对任务执行挂起、恢复等操作。
它最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。
cgroups 子系统
cgroups 的全称是 control groups,cgroups 为每种可以控制的资源定义了一个子系统。典型的子系统介绍如下:
- cpu 子系统,主要限制进程的 cpu 使用率。
- cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
- cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
- memory 子系统,可以限制进程的 memory 使用量。
- blkio 子系统,可以限制进程的块设备 io。
- devices 子系统,可以控制进程能够访问某些设备。
- net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
- freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
- ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。
cgroups 层级结构(Hierarchy)
内核使用 cgroup 结构体来表示一个 control group 对某一个或者某几个 cgroups 子系统的资源限制。cgroup 结构体可以组织成一颗树的形式,每一棵 cgroup 结构体组成的树称之为一个 cgroups 层级结构。cgroups 层级结构可以 attach 一个或者几个 cgroups 子系统,当前层级结构可以对其 attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 attach 到一个 cpu 层级结构中。

比如上图表示两个 cgroups 层级结构,每一个层级结构中是一颗树形结构,树的每一个节点是一个 cgroup 结构体(比如 cpu_cgrp, memory_cgrp)。第一个 cgroups 层级结构 attach 了 cpu 子系统和 cpuacct 子系统, 当前 cgroups 层级结构中的 cgroup 结构体就可以对 cpu 的资源进行限制,并且对进程的 cpu 使用情况进行统计。 第二个 cgroups 层级结构 attach 了 memory 子系统,当前 cgroups 层级结构中的 cgroup 结构体就可以对 memory 的资源进行限制。
在每一个 cgroups 层级结构中,每一个节点(cgroup 结构体)可以设置对资源不同的限制权重。比如上图中 cgrp1 组中的进程可以使用 60%的 cpu 时间片,而 cgrp2 组中的进程可以使用 20%的 cpu 时间片。
cgroups 文件系统接口
在 Linux 中,Cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下,下面以腾讯云主机 ubuntu16.04 为例
$ 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/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
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/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
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/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
它的输出结果,是一系列文件系统目录,我们单独看下关于 cpu 的子系统是如何进行限制的
$ ls /sys/fs/cgroup/cpu
cgroup.clone_children cpuacct.usage cpuacct.usage_sys cpu.stat user.slice
cgroup.procs cpuacct.usage_all cpuacct.usage_user notify_on_release
cgroup.sane_behavior cpuacct.usage_percpu cpu.cfs_period_us release_agent
cpuacct.usage_percpu_sys cpu.cfs_quota_us system.slice cpuacct.stat
cpuacct.usage_percpu_user cpu.shares tasks
需要特别注意下面这两个关键文件:``cfs_period和cfs_quota`
这两个参数需要组合使用,可以用来限制进程在长度为 cfs_period 的一段时间内,只能被分配到总量为 cfs_quota 的 CPU 时间。
现在,我们需要在这个 cpu 子系统里新建一个目录,例如:container
$ cd /sys/fs/cgroup/cpu
$ sudo mkdir container
这个目录就称为一个“控制组”。你会发现,操作系统会在你新创建的 container 目录下,自动生成该子系统对应的资源限制文件
$ ls container/
cgroup.clone_children cpuacct.usage_all cpuacct.usage_sys cpu.shares
cgroup.procs cpuacct.usage_percpu cpuacct.usage_user cpu.stat
cpuacct.stat cpuacct.usage_percpu_sys cpu.cfs_period_us notify_on_release
cpuacct.usage cpuacct.usage_percpu_user cpu.cfs_quota_us tasks
执行一个脚本,用来测试 cpu 限制情况
$ while : ; do : ; done &
[1] 12155
重新打开一个新的终端,使用top命令你会看到有一个bash进程,已经把服务器的 cpu 占到了 100%,进程号正是 12155,即刚我们执行的一个死循环的脚本
此时,我们可以通过查看 container 目录下的文件,看到 container 控制组里的 CPU quota 还没有任何限制(即:-1),CPU period 则是默认的 100 ms(100000 us)
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
-1
$ cat /sys/fs/cgroup/cpu/container/cpu.cfs_period_us
100000
接下来,我们可以通过修改这些文件的内容来设置限制。比如,向 container 组里的 cfs_quota 文件写入 20 ms(20000 us)
$ echo 20000 > /sys/fs/cgroup/cpu/container/cpu.cfs_quota_us
这条命令的含义是在每 100 ms 的时间里,被该控制组限制的进程只能使用 20 ms 的 CPU 时间,也就是说这个进程只能使用到 20% 的 CPU 带宽。
接下来,我们把被限制的进程的 PID 写入 container 组里的 tasks 文件,上面的设置就会对该进程生效了:
$ echo 12155 > /sys/fs/cgroup/cpu/container/tasks
再次使用 top 命令,你会非常明显的看到,cpu 很快的被限制到了 20%
Linux Cgroups 的设计还是比较易用的,它就是一个子系统目录加上一组资源限制文件的组合。而对于 Docker 等 Linux 容器项目来说,它们只需要在每个子系统下面,为每个容器创建一个控制组(即创建一个新目录),然后在启动容器进程之后,把这个进程的 PID 填写到对应控制组的 tasks 文件中就可以了
好的,下面看看在 docker 里是怎么进行限制的。
运行一个容器
$ sudo docker run -it -d --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash
查看该容器的 container id
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
547e871365af ubuntu "/bin/bash" 2 minutes ago Up 2 minutes sad_noyce
进到这个新创建的控制组中
$ cd /sys/fs/cgroup/cpu/docker/547e871365af6c4f4e835b8c3cfe0355c865eb2a692faab0f8ec82dd468e987b
可以很明显的看到系统自动生成了一堆文件
$ ls -l
total 0
-rw-r--r-- 1 root root 0 Jul 27 23:01 cgroup.clone_children
-rw-r--r-- 1 root root 0 Jul 27 22:59 cgroup.procs
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.stat
-rw-r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_all
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Jul 27 23:01 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Jul 27 22:59 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jul 27 22:59 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jul 27 23:01 cpu.shares
-r--r--r-- 1 root root 0 Jul 27 23:01 cpu.stat
-rw-r--r-- 1 root root 0 Jul 27 23:01 notify_on_release
-rw-r--r-- 1 root root 0 Jul 27 23:01 tasks
分别查看 cpu-period 和 cpu-quota 的值
$ cat cpu.cfs_period_us
100000
$ cat cpu.cfs_quota_us
20000
到这里,这个容器只能使用 20%的 cpu
可以关注下我的微信公众号哦,一起学习进步


浙公网安备 33010602011771号