容器核心技术之cgroup

cgroup概述

  • Cgroups (Control Groups)是 Linux 下用于对一个或一组进程进行资源控制和监控的机制;
  • 可以对诸如 CPU 使用时间、内存、磁盘 I/O 等进程所需的资源进行限制;
  • 不同资源的具体管理工作由相应的 Cgroup 子系统(Subsystem)来实现 ;
  • 针对不同类型的资源限制,只要将限制策略在不同的的子系统上进行关联即可 ;
  • Cgroups 在不同的系统资源管理子系统中以层级树(Hierarchy)的方式来组织管理:每个Cgroup 都可以包含其他的子 Cgroup,因此子 Cgroup 能使用的资源除了受本 Cgroup 配置的资源参数限制,还受到父Cgroup 设置的资源限制 。

cgroups 功能及核心概念

cgroups 主要提供了如下功能

  • 资源限制: 限制资源的使用量,例如我们可以通过限制某个业务的内存上限,从而保护主机其他业务的安全运行。
  • 优先级控制:不同的组可以有不同的资源( CPU 、磁盘 IO 等)使用优先级。
  • 审计:计算控制组的资源使用情况。
  • 控制:控制进程的挂起或恢复。

cgroups功能的实现依赖于三个核心概念:子系统、控制组、层级树。

  • 子系统(subsystem):是一个内核的组件,一个子系统代表一类资源调度控制器。例如内存子系统可以限制内存的使用量,CPU 子系统可以限制 CPU 的使用时间。

  • 控制组(cgroup):表示一组进程和一组带有参数的子系统的关联关系。例如,一个进程使用了 CPU 子系统来限制 CPU 的使用时间,则这个进程和 CPU 子系统的关联关系称为控制组。

  • 层级树(hierarchy):是由一系列的控制组按照树状结构排列组成的。这种排列方式可以使得控制组拥有父子关系,子控制组默认拥有父控制组的属性,也就是子控制组会继承于父控制组。比如,系统中定义了一个控制组 c1,限制了 CPU 可以使用 1 核,然后另外一个控制组 c2 想实现既限制 CPU 使用 1 核,同时限制内存使用 2G,那么 c2 就可以直接继承 c1,无须重复定义 CPU 限制。

cgroups 的三个核心概念中,子系统是最核心的概念,因为子系统是真正实现某类资源的限制的基础。

Linux内核代码中Cgroups的实现

进程数据结构

struct task_struct
{
#ifdef CONFIG_CGROUPS
     // 设置这个进程属于哪个css_set
    struct css_set __rcu *cgroups;
     // cg_list是用于将所有同属于一个css_set的task连成一起
    struct list_head cg_list;
#endif
}

css_set 是 cgroup_subsys_state 对象的集合数据结构

struct css_set {
/*
* Set of subsystem states, one for each subsystem. This array is
* immutable after creation apart from the init_css_set during
* subsystem registration (at boot time).
*/
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
};

cgroup子系统

  1. cpu 子系统,主要限制进程的 cpu 使用率。
  2. cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  3. cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  4. memory 子系统,可以限制进程的memory 使用量。
  5. blkio 子系统,可以限制进程的块设备 io。
  6. devices 子系统,可以控制进程能够访问某些设备。
  7. net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  8. net_prio — 这个子系统用来设计网络流量的优先级
  9. freezer 子系统,可以挂起或者恢复cgroups 中的进程。
  10. ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace
  11. hugetlb —这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。

当前系统已挂载的cgroup信息

# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,net_prio,net_cls)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,pids)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,memory)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuset)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,devices)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuacct,cpu)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,blkio)

cpu子系统

  • cpu.shares: 可出让的能获得 CPU 使用时间的相对值。
  • cpu.cfs_period_us:cfs_period_us 用来配置时间周期长度,单位为 us(微秒)。
  • cpu.cfs_quota_us:cfs_quota_us 用来配置当前 Cgroup 在 cfs_period_us 时间内最多能使用的 CPU 时间数,单位为 us(微秒)。
  • cpu.stat : Cgroup 内的进程使用的 CPU 时间统计。
  • nr_periods : 经过 cpu.cfs_period_us 的时间周期数量。
  • nr_throttled : 在经过的周期内,有多少次因为进程在指定的时间周期内用光了配额时间而受到限制。
  • throttled_time : Cgroup 中的进程被限制使用 CPU 的总用时,单位是 ns(纳秒)。

cpu子系统练习

1、在cgroup cpu子系统下创建目录

cd /sys/fs/cgroup/cpu
mkdir cpudemo

执行完上述命令后,我们查看一下我们新创建的目录下发生了什么?

# ls -l /sys/fs/cgroup/cpu/cpudemo
total 0
-rw-r--r--. 1 root root 0 Oct  7 16:09 cgroup.clone_children
--w--w--w-. 1 root root 0 Oct  7 16:09 cgroup.event_control
-rw-r--r--. 1 root root 0 Oct  7 16:09 cgroup.procs
-r--r--r--. 1 root root 0 Oct  7 16:09 cpuacct.stat
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpuacct.usage
-r--r--r--. 1 root root 0 Oct  7 16:09 cpuacct.usage_percpu
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpu.cfs_period_us
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpu.cfs_quota_us
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpu.rt_period_us
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpu.rt_runtime_us
-rw-r--r--. 1 root root 0 Oct  7 16:09 cpu.shares
-r--r--r--. 1 root root 0 Oct  7 16:09 cpu.stat
-rw-r--r--. 1 root root 0 Oct  7 16:09 notify_on_release
-rw-r--r--. 1 root root 0 Oct  7 16:09 tasks

由上可以看到我们新建的目录下被自动创建了很多文件,其中 cpu.cfs_quota_us 文件代表在某一个阶段限制的 CPU 时间总量,单位为微秒。例如,我们想限制某个进程最多使用 1 核 CPU,就在这个文件里写入 100000(100000 代表限制 1 个核) ,tasks 文件中写入进程的 ID 即可(如果要限制多个进程 ID,在 tasks 文件中用换行符分隔即可)。

2、创建进程,加入cgroup
使用以下命令将 shell 进程加入 cgroup 中:

# cd /sys/fs/cgroup/cpu/mydocker
# echo $$ > tasks  

查看一下 tasks 文件内容:

# cat tasks 
4924
5008

3、执行 CPU 耗时任务,验证 cgroup 是否可以限制 cpu 使用时间
执行完上述命令后,我们新打开一个 shell 窗口,使用 top -p 命令查看当前 cpu 使用率,-p 参数后面跟进程 ID,我这里是 4924

# top -p 4924
top - 16:19:45 up 10:27,  2 users,  load average: 1.67, 0.50, 0.21
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 34.2 us, 12.8 sy,  0.0 ni, 52.5 id,  0.0 wa,  0.0 hi,  0.5 si,  0.0 st
KiB Mem :  3880256 total,  2901232 free,   478524 used,   500500 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  3158040 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                 
 4924 root      20   0   15920   2864   1796 R  98.3  0.1   1:04.15 bash

此时,该进程消耗完一个cpu
4、限制进程使用cpu为0.5核

# top -p 4924
top - 16:19:45 up 10:27,  2 users,  load average: 1.67, 0.50, 0.21
Tasks:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
%Cpu(s): 34.2 us, 12.8 sy,  0.0 ni, 52.5 id,  0.0 wa,  0.0 hi,  0.5 si,  0.0 st
KiB Mem :  3880256 total,  2901232 free,   478524 used,   500500 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  3158040 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                 
 4924 root      20   0   15920   2864   1796 R  50  0.1   1:04.15 bash

此时,该进程使用的cpu使用率为50%

内存子系统

  • memory.usage_in_bytes : cgroup 下进程使用的内存,包含 cgroup 及其子 cgroup 下的进程使用的内存
  • memory.max_usage_in_bytes : cgroup 下进程使用内存的最大值,包含子 cgroup 的内存使用量。
  • memory.limit_in_bytes : 设置 Cgroup 下进程最多能使用的内存。如果设置为 -1,表示对该 cgroup 的内存使用不做限制。
  • memory.oom_control : 设置是否在 Cgroup 中使用 OOM(Out of Memory)Killer,默认为使用。当属于该 cgroup 的进程使用的内存超过最大的限定值时,会立刻被 OOM Killer 处理。

memory子系统练习

1、memory子系统目录中创建目录结构

cd /sys/fs/cgroup/memory
mkdir memorydemo    # 创建该目录后,会生成一些控制文件

限制该cgroup使用内存为1G

echo 1073741824 > memory.limit_in_bytes

2、创建进程,加入 cgroup
把当前 shell 进程 ID 写入 tasks 文件内:

复制代码
# cd /sys/fs/cgroup/memory/memorydemo
# echo $$ > tasks 

3、使用内存压测工具,模拟内存溢出
memtester压测,限制1500M内存导致进程被Kill

# memtester 1500 1
memtester version 4.5.0 (64-bit)
Copyright (C) 2001-2020 Charles Cazabon.
Licensed under the GNU General Public License version 2 (only).

pagesize is 4096
pagesizemask is 0xfffffffffffff000
want 1500MB (1572864000 bytes)
got  1500MB (1572864000 bytes), trying mlock ...Killed

删除cgroup

直接删除创建的文件夹即可

rmdir /sys/fs/cgroup/memory/memorydemo/

docker如何使用cgroup

1、使用如下命令创建一个容器

docker run -itd -m=1g openjdk:8u252-jdk

上述命令创建并启动了一个容器,并且限制内存为 1G。然后我们进入cgroups内存子系统的目录,使用 ls 命令查看一下该目录下的内容:

#  ls -l /sys/fs/cgroup/memory
total 0
-rw-r--r--.  1 root root 0 Oct 18 15:56 cgroup.clone_children
--w--w--w-.  1 root root 0 Oct 18 15:56 cgroup.event_control
-rw-r--r--.  1 root root 0 Oct 18 15:56 cgroup.procs
-r--r--r--.  1 root root 0 Oct 18 15:56 cgroup.sane_behavior
drwxr-xr-x.  3 root root 0 Oct 19 10:10 docker
......

通过上面输出可以看到,该目录下有一个 docker 目录,该目录正是 Docker 在内存子系统下创建的。我们进入到 docker 目录下查看一下相关内容:

# cd /sys/fs/cgroup/memory/docker/
# ls
895f62af62c33b36aee78e8454a557b3d2f125084f8127333b2f37c1e4c49a9e 
......

可以看到 docker 的目录下有一个一串随机 ID 的目录,该目录即为我们上面创建的容器的 ID。然后我们进入该目录,查看一下该容器的 memory.limit_in_bytes 文件的内容。

# cd 895f62af62c33b36aee78e8454a557b3d2f125084f8127333b2f37c1e4c49a9e/
# cat memory.limit_in_bytes 
1073741824

内存限制正好为1Gib

写在最后: cgroups 不仅可以实现资源的限制,还可以为我们统计资源的使用情况,容器监控系统的数据来源也是 cgroups 提供的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值