Docker 基础技术之 Linux cgroups 详解

时间:2024-01-15 15:27:14

本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫。

推荐大家到公众号阅读,那里阅读体验更好,也沉淀了很多篇干货。

前面两篇文章我们总结了 Docker 背后使用的资源隔离技术 Linux namespace。

Docker 基础技术之 Linux namespace 详解

Docker 基础技术之 Linux namespace 源码分析

本篇将讨论另外一个技术——资源限额,这是由 Linux cgroups 来实现的。

cgroups 是 Linux 内核提供的一种机制,这种机制可以根据需求把一系列任务及子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。(来自 《Docker 容器与容器云》)

通俗来说,cgroups 可以限制和记录任务组(进程组或线程组)使用的物理资源(包括 CPU、内存、IO 等)。

为了方便用户(程序员)操作,cgroups 以一个伪文件系统的方式实现,并对外提供 API,用户对文件系统的操作就是对 cgroups 的操作。

从实现上来,cgroups 实际上是给每个执行任务挂了一个钩子,当任务执行过程中涉及到对资源的分配使用时,就会触发钩子上的函数对相应的资源进行检测,从而对资源进行限制和优先级分配。

cgroups 的作用

总结下来,cgroups 提供以下四个功能:

资源限制:cgroups 可以对任务使用的资源总额进行限制,如设定应用运行时使用内存的上限,一旦超过这个配额就发出 OOM(Out of Memory)提示。

优先级分配:通过分配的 CPU 时间片数量和磁盘 IO 带宽大小,实际上就相当于控制了任务运行的优先级。

资源统计:cgroups 可以统计系统的资源使用,如 CPU 使用时长、内存用量等,这个功能非常适用于计费。

任务控制:cgroups 可以对任务执行挂起、恢复等操作。

cgroups 的子系统

cgroups 在设计时根据不同的资源类别分为不同的子系统,一个子系统本质上是一个资源控制器,比如 CPU 资源对应 CPU 子系统,负责控制 CPU 时间片的分配,内存对应内存子系统,负责限制内存的使用量。进一步,一个子系统或多个子系统可以组成一个 cgroup,cgroups 中的资源控制都是以 cgroup 为单位来实现,一个任务(或进程或线程)可以加入某个 cgroup,也可以从一个 cgroup 移动到另一个 cgroup,但这里有一些限制,在此就不再赘述了,详细查阅相关资料了解。

对于我们来说,最关键的是知道怎么用,下面就针对 CPU、内存和 IO 资源来看 Docker 是如何使用的?

对于 CPU,Docker 使用参数 -c 或 --cpu-shares 来设置一个容器使用的 CPU 权重,权重的大小也影响了 CPU 使用的优先级。

如下,启动两个容器,并分配不同的 CPU 权重,最终 CPU 使用率情况:

docker run --name "container_A" -c 1024 ubuntu
docker run --name "container_B" -c 512 ubuntu

Docker 基础技术之 Linux cgroups 详解

当只有一个容器时,即使指定较少的 CPU 权重,它也会占满整个 CPU,说明这个权重只是相对权重,如下将上面的 “container_A” 停止,“container_B” 就分配到全部可用的 CPU。

Docker 基础技术之 Linux cgroups 详解

对于内存,Docker 使用 -m(设置内存的限额)和 --memory-swap(设置内存和 swap 的限额)来控制容器内存的使用量,如下,给容器限制 200M 的内存和 100M 的 swap,然后给容器内的一个工作线程分配 280M 的内存,因为 280M 在容许的 300M 范围内,没有问题。其内存分配过程是不断分配又释放,如下:

Docker 基础技术之 Linux cgroups 详解

如果让工作线程使用内存超过 300M,则出现内存超限的错误,容器退出,如下:

Docker 基础技术之 Linux cgroups 详解

对于 IO 资源,其使用方式与 CPU 一样,使用 --blkio-weight 来设置其使用权重,IO 衡量的两个指标是 bps(byte per second,每秒读写的数据量) 和 iops(io per second, 每秒 IO 的次数),实际使用,一般使用这两个指标来衡量 IO 读写的带宽,几种使用参数如下:

  • --device-read-bps,限制读某个设备的 bps。

  • --device-write-bps,限制写某个设备的 bps。

  • --device-read-iops,限制读某个设备的 iops。

  • --device-write-iops,限制写某个设备的 iops。

假如限制容器对其文件系统 /dev/sda 的 bps 写速率为 30MB/s,则在容器中用 dd 测试其写磁盘的速率如下,可见小于 30MB/s。

Docker 基础技术之 Linux cgroups 详解

如果是正常情况下,我的机器可以达到 56.7MB/s,一般都是超 1G 的。

Docker 基础技术之 Linux cgroups 详解

上面几个资源使用限制的例子,本质上都是调用了 Linux kernel 的 cgroups 机制来实现的,每个容器创建后,Linux 会为每个容器创建一个 cgroup 目录,以容器的 ID 命名,目录在 /sys/fs/cgroup/ 中,针对上面的 CPU 资源限制的例子,我们可以在 /sys/fs/cgroup/cpu/docker 中看到相关信息,如下:

Docker 基础技术之 Linux cgroups 详解

其中,cpu.shares 中保存的就是限制的数值,其他还有很多项,感兴趣可以动手实验看看。

总结

cgroups 的作用,cgroups 的实现,cgroups 的子系统机制,CPU、内存和 IO 的使用方式,以及对应 Linux 的 cgroups 文件目录。


我的公众号 「Linux云计算网络」(id: cloud_dev) ,号内有 10T 书籍和视频资源,后台回复 「1024」 即可领取,分享的内容包括但不限于 Linux、网络、云计算虚拟化、容器Docker、OpenStack、Kubernetes、工具、SDN、OVS、DPDK、Go、Python、C/C++编程技术等内容,欢迎大家关注。

Docker 基础技术之 Linux cgroups 详解