docker如何利用cgroup对容器资源进行限制

标签: 程序开发 cgroup 容器 | 发表时间:2021-04-26 19:16 | 作者:admin
出处:https://blog.haohtml.com

在容器里有两个非常重要的概念,一个是 namespace用来进行对容器里所有进程的隔离;另一个就是 cgroup,用来对容器资源进行限制。那 cgroup又是如何实现对进行资源的限制呢,今天我们来了解一下它的实现原理。

什么是cgroup

cgroupControl Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离 ` 进程组` 所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被 LXCdocker 等很多项目用于实现进程资源控制。cgroup 是将任意进程进行分组化管理的 Linux 内核功能。
cgroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。 一定要切记,这里的限制单元为 进程组,而不是进程。

子系统

上面提到的具体的资源管理功能统称为 cgroup 子系统,所有子系统列表可以通过 cat /proc/cgroups 命令查看,主要有以下几大子系统:

# cat /proc/cgroups
#subsys_name	hierarchy	num_cgroups	enabled
cpuset	        4	        7	        1
cpu	        2	        89	        1
cpuacct	        2	        89	        1
blkio	        3	        86	        1
memory	        7	        150	        1
devices	        6	        84	        1
freezer	        5	        7	        1
net_cls	        10	        7	        1
perf_event	    12	        7	        1
net_prio	    10	        7	        1
hugetlb	        8	        7	        1
pids	        9	        94      	1
rdma	        11	        1	        1
  • cpuset:如果是多核心的CPU, 这个子系统会为 cgroup 任务分配单独的CPU和内存。
  • cpu:使用调度程序为cgroup任务提供CPU的访问。
  • cpuacct:产生cgroup 任务的CPU资源报告
  • blkio:设置限制每一个块设备的输入输出控制。例如:磁盘,光盘以及usb 等等。
  • memory: 设置每一个cgroup 的内存限制以及产生内存资源报告。
  • devices:容许或拒绝cgroup任务对设备的访问。
  • freezer:暂停和恢复cgroup任务。
  • net_cls: 标记每一个网络包以供cgroup 方便使用。
  • ns:命名空间子系统,能够设置一个子系统的上限配额。
  • perf_event: 增加了对每一个group 的监测跟踪的能力,能够监测属于某个特定的group 的全部线程以及运行在特定,监控能力超出限制则进行终止。
  • net_prio 设置cgroup中进程产生的网络流量的优先级
  • hugetlb 限制使用的内存页数量
  • pids 限制任务的数量

目前 docker 只是用了其中一部分子系统,实现对资源配额和使用的控制。如可以使用 ` freezer` 子系统对 `进行组` 进行挂起和恢复。

cgroup组件术语

  • task:在cgroup中,任务就是系统的一个进程
  • subsystem:一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
  • control group:控制族群就是按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制;
  • hierarchy:树形结构的 CGroup 层级,每个子 CGroup 节点会继承父 CGroup 节点的子系统配置,每个 Hierarchy 在初始化时会有默认的 CGroup(Root CGroup);
  • 控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。比如一组task进程通过cgroup1限制了CPU使用率,然后其中一个日志进程还需要限制磁盘IO,为了避免限制磁盘IO影响到其他进程,就可以创建cgroup2,使其继承cgroup1并限制磁盘IO,这样这样cgroup2便继承了cgroup1中对CPU使用率的限制并且添加了磁盘IO的限制而不影响到cgroup1中的其他进程;

组件关系

  • 每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的 cgroup 都是此 cgroup 的后代)的初始成员;
  • 一个 subsystem 最多只能附加到一个层级 hierarchy;
  • 一个层级 hierarchy 可以附加多个子系统 subsystem;
  • 一个任务 task 可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级hierarchy;
  • 系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。
  • 一个进程fork出子进程时,该子进程默认自动成为父进程所在的cgroup的成员,也可以根据情况将其移动到到不同的cgroup中.

如图所示,CPU 和 Memory 两个子系统有自己独立的层级系统,而又通过 Task Group 取得关联关系

cgroup关联图 CGroup 典型应用架构图

CGroup 技术可以被用来在操作系统底层限制物理资源,起到 Container 的作用。上图中每一个 JVM 进程对应一个 Container Cgroup 层级,通过 CGroup 提供的各类子系统,可以对每一个 JVM 进程对应的线程级别进行物理限制,这些限制包括 CPU、内存等等许多种类的资源。

cgroup实战

在 Linux 中,cgroups 给用户暴露出来的操作接口是文件系统,即它以文件和目录的方式组织在操作系统的 /sys/fs/cgroup 路径下。在 Ubuntu 16.04 机器里,可以用 mount 指令把它们展示出来:

$ 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/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/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
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/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)


它的输出是一些文件系统目录,这些目录名就是当前系统所支持的子系统,这些子系统都在 /sys/fs/cgroup/ 目录内,如对于cpu子系统来说,相关的几个配置文件为

$ ls /sys/fs/cgroup/cpu
aegis                  cgroup.procs          cpu.cfs_quota_us  cpuacct.stat       cpuacct.usage_percpu       cpuacct.usage_sys   kubepods.slice     system.slice  user.slice
assist                 cgroup.sane_behavior  cpu.shares        cpuacct.usage      cpuacct.usage_percpu_sys   cpuacct.usage_user  notify_on_release  tasks
cgroup.clone_children  cpu.cfs_period_us     cpu.stat          cpuacct.usage_all  cpuacct.usage_percpu_user  init.scope          release_agent      test



其中 ` cpu.cfs_quota_us` 和 ` cpu.cfs_period_us` 是经常使用的两个配置项,两者必须组合使用,表示一个进程组在 `cpu.cfs_period_us` 段时间内,分配给CPU的时间比例为 ` cpu.cfs_quota_us`。

另外输出结果中包含一些子目录,如 aegisassistkubepods.slicesystem.sliceuser.slicetestinit.scope

现在我们看下这些子系统配置文件如何使用,首先我们在 /sys/fs/cgroup/cpu/ 目录下创建一个目录 mycontainer,这个目录称为cgroup,即”控制组”。

$ cd /sys/fs/cgroup/cpu/
$ mkdir mycontainer
$ sys/fs/cgroup/cpu# ls mycontainer/
cgroup.clone_children  cpu.cfs_period_us  cpu.shares  cpu.uclamp.max  cpuacct.stat   cpuacct.usage_all     cpuacct.usage_percpu_sys   cpuacct.usage_sys   notify_on_release
cgroup.procs           cpu.cfs_quota_us   cpu.stat    cpu.uclamp.min  cpuacct.usage  cpuacct.usage_percpu  cpuacct.usage_percpu_user  cpuacct.usage_user  tasks


会发现mycontainer目录时会自动出现一些cpu配置文件,有些配置文件内容为-1,表示不限制,其中tasks文件里表示要控制的进程pid。
我们现在做个实现执行一下死循环脚本,便其完全占用CPU达到100%,然后再对此PID进行CPU限制,看下效果如果。

$ while : ; do : ; done &
[1] 1626025


执行top查看

PID     USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
1626025 root      20   0   12724   1768      0 R 100.0   0.0   0:28.60 bash

发现这个进程的CPU已经达到了100%,下面我们对其进行一下限制。先将进程PID写到 mycontainer 控制组下的tasks文件里,然后限制cpu使用率

$ echo 1626025 > /sys/fs/cgroup/cpu/mycontainer/tasks
$ cat /sys/fs/cgroup/cpu/mycontainer/tasks
1626025


现在我们已成功将其进程号写入tasks文件。上面我们提到过对cpu的限制主要使用两个文件,分别为cpu.cfs_quota_us 和 cpu.cfs_quota_us, 先看一下他们的默认值。

$ cat /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us 
-1
$ cat /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_period_us 
100000

表示在100ms内分配给cpu的机会为不限制,也就是表示100%的资源。我们要做一下限制,让其在100ms时间内,只分配给 20% 的cpu机会

$ echo 20000 > /sys/fs/cgroup/cpu/mycontainer/cpu.cfs_quota_us


然后再执行一下top命令发现cpu使用率立即降下来了,最多为20%左右,可能会有一点点的超出,这个很正常。

PID     USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
1626025 root      20   0   12724   1768      0 R  20.0   0.0  12:43.38 bash


这里我们只对cpu做了限制,你也可以做内在memory做一下限制,由于这里的脚本只会占用cpu,所以不再演示。对于我们经常使用 docker run 命令启动一个容器的时候,其实都有一个配置参数与配置文件相对应,如

$ docker run -it --cpu-period=100000 --cpu-quota=20000 ubuntu /bin/bash


如果你到容器目录查看配置文件会发现相应 cpu.cfs_period_uscpu.vfs_quota_us 的值都已被修改。

参考资料

相关 [docker 利用 cgroup] 推荐:

docker如何利用cgroup对容器资源进行限制

- - 学习日志
在容器里有两个非常重要的概念,一个是 namespace用来进行对容器里所有进程的隔离;另一个就是 cgroup,用来对容器资源进行限制. 那 cgroup又是如何实现对进行资源的限制呢,今天我们来了解一下它的实现原理. cgroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离 ` 进程组` 所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被 LXC、 docker 等很多项目用于实现进程资源控制.

利用Docker构建开发环境

- - UC技术博客
最近接触PAAS相关的知识,在研发过程中开始使用Docker搭建了自己完整的开发环境,感觉生活在PAAS时代的程序员真是幸福,本文会简要介绍下Docker是什么,如何利用Docker来搭建自己的开发环境(本文主要是面向Mac OS X),以及期间所遇到的一些坑和解决方案. (本文会要求你对PAAS、LXC、CGroup、AUFS有一定的了解基础,请自行Google ).

利用docker快速部署应用

- - snoopyxdy的博客
最近研究了几天docker的快速部署,感觉很有新意,非常轻量级和方便,打算在公司推广一下,解放运维,省得每次部署一台新服务器都去跑安装脚本了,对于我们开发人员也是好事情,无需写太多重复的部署文档,直接将docker的images丢上服务器就可以运行了. 安装很简单,直接进入下载页面,根据自己的操作系统下载相对应的安装包即可,下面说一下windows安装:.

Docker & Flatpak

- - IT瘾-dev
目前最流行的技术莫过于Docker,Docker和Docker衍生的东西用到了很多很酷的技术,目前deepin应用软件发布转变成flatpak,这些看似风牛马不相及的技术方案,实际都使用了一个共同的底层技术——Namespace,假如没有namespace支持,这些技术实现都将成为空中楼阁. 一句话总结,无论是Docker、sysmted-nspawn还是flatpak,都是在namespace基础上,针对不同的场景,生出的不同的解决方案.

docker初体验之docker-tomcat

- - BlogJava-首页技术区
docker已经是现在最热的容器技术,最近也去体验了一下,在daocloud注册了一个账号,并开始本机实战docker. daocloud免费有两个容器可用,体验送T恤,邀请送书,这里我分享一个daocloud的邀请码 https://account.daocloud.io/signup?invite_code=mxeq2jkmcur37vz6ven8,daocloud是非常棒的容器云平台,使用体验好,问题响应也及时,绑定微信还送一个额外容器.

kubernetes移除Docker?

- -
两周前,Kubernetes在其最新的Changelog中宣布1.20之后将要弃用dockershime,也就说Kubernetes将不再使用Docker做为其容器运行时. 这一消息持续发酵,掀起了不小的波澜,毕竟Kubernetes+Docker的经典组合是被市场所认可的,大量企业都在使用. 看上去这个“弃用”的决定有点无厘头,那么为什么Kubernetes会做出这样的决定.

Docker应用场景

- - 灯火阑珊
Flynn:一个使用go语言编写的开源PaaS平台,目标是简化分布式环境中应用的部署和维护,可以通过git push命令,将应用部署到Docker,从而省去复杂的配置和操作. CoreOS:一种新的架构体系重新设计的Linux发型版,可以运行在既有的硬件活着云服务器上. CoreOS不提供类似yum或apt的包管理工具,用户不需要在CoreOS中安装软件,而是让程序都在Docker容器中运行.

docker使用场景

- - 开源软件 - ITeye博客
Docker应用容器相对于 VM 有以下几个优点:. 1、启动速度快,容器通常在一秒内可以启动,而 VM 通常要更久. 2、资源利用率高,一台普通PC 可以跑上千个容器,你跑上千个 VM 试试. 3、性能开销小, VM 通常需要额外的 CPU 和内存来完成 OS 的功能,这一部分占据了额外的资源. 因为VM 的 Hypervisor 需要实现对硬件的虚拟化,并且还要搭载自己的操作系统,自然在启动速度和资源利用率以及性能上有比较大的开销.

Docker 监控实战

- - SegmentFault 最新的文章
如今,越来越多的公司开始使用 Docker 了,现在来给大家看几组数据:. 2 / 3 的公司在尝试了 Docker 后最终使用了它. 也就是说 Docker 的转化率达到了 67%,而转化市场也控制在 60 天内. 越大型的公司越早开始使用 Docker. 研究发现主机数量越多的公司,越早开始使用 Docker.

Docker入门例子

- - 开源软件 - ITeye博客
Docker 提供了一个可以运行应用程序的容器. Docker 容器并不包含一个单独的操作系统,而是基于已有的基础设施中操作系统提供的功能来运行的. 2 Docker安装与启动. #将docker加入开机启动. 3 Docker的14个基础命令. 检查Docker的安装是否正确. 运行"Hello World"例子.