容器核心技术–CGroup

接上一个小节,我们来试想这样一个场景:

一台宿主机的容器中运行了一个监控服务,但监控服务占用了宿主机全部的 CPU 和内存等资源,导致宿主机上的其他服务和容器都被卡死,无法正常运行。

监控类服务不应占用大量资源,无论是什么原因引起的问题,都不应该影响宿主机的正常使用,否则容器的隔离就没有意义。Namespace 只能做到系统资源维度的隔离,无法做到硬件资源的控制。我们需要使用一种机制 Cgroup,指定容器应用最大占用多少资源。

Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(CPU、Memory、IO 等)。

1. CGroup 核心概念

前面说过 CGroup 是用来对进程进行资源管理的,因此 CGroup 需要考虑如何抽象这两种概念:进程和资源,同时如何组织自己的结构。CGroup 机制中有以下几个基本概念:

  • task:任务,对应于系统中运行的一个实体,下文统称进程;
  • subsystem:子系统,具体的资源控制器(resource class 或者 resource controller),控制某个特定的资源使用;
  • cgroup:控制组,一组任务和子系统的关联关系,表示对这些任务进行怎样的资源管理策略;
  • hierarchy:层级树,由一系列 CGroup 组成的树形结构。每个节点都是一个 CGroup ,CGroup 可以有多个子节点,子节点默认会继承父节点的属性。系统中可以有多个 hierarchy。

Cgroup 机制非常复杂,上面的名词了解就好,学习 Docker 暂时还不需要深入研究它。

在 Linux 环境中,我们可以执行 ls -al /sys/fs/cgroup/ 查看当前系统的 Cgroup:

图片描述

我们看到目录中有若干个子目录,除了 systemd 目录,其他的一个子目录对应一个子系统,子系统功能如下所示。

子系统 功能
blkio 为块设备,如硬盘等设备,设定输入输出限制
cpu 设置 cgroup 中进程的 CPU 被调度的策略
cpuacct 统计 cgroup 中进程的 CPU 占用
cpuset 设置 cgroup 中进程可以使用的 CPU 和内存
devices 控制 cgroup 中进程对设备的访问
freezer 挂起或者恢复 cgroup 中的进程
hugetlb 用于控制 cgroup 中进程的内存占用,这是一个大页文件系统。
memory 控制 cgroup 中进程的内存占用,并统计内存资源使用情况。
net_cls 将 cgroup 中进程产生的网络包分类,允许 Linux 流量控制系统识别从具体 cgroup 中生成的数据包。
net_prio 控制 cgroup 中进程的网络流量的优先级
perf_event 识别任务的 cgroup 成员,可以用来做性能分析
pids 限制 cgroup 及其所有子孙 cgroup 里面能创建的总的进程数量
rdma 限制 RDMA/IB 资源

3. 演示:使用 Cgroup 限制进程 CPU 资源占用

在上面的表格中, 你会发现大量出现Cgroup 中的进程这个描述,原因在于我们必须先挂载子系统,将进程纳入 Cgroup 组规则中,然后 Cgroup 机制才能控制这个进程。

我们马上上手尝试一下,先安装stress

sudo dnf install https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/s/stress-1.0.4-16
.el7.x86_64.rpm

Tips:我们使用stress 的软件进行压力负载测试,stress 会根据设定,执行一系列消耗系统资源的操作,使得系统在一定的负载下运行。

启动一个压力负载测试的进程:

# 产生 1个进程,每个进程都反复不停地计算随机数的平方根
stress -c 1 > /dev/null &

使用top 命令查看资源占用情况
图片描述

图中两个红框从左到右分别代表压力测试的进程号是 1816,CPU 占用百分比 99%,说明压力测试进程正常生效了。

切换到 root 用户:

sudo su - root

进入 /sys/fs/cgroup/cpu 这个目录:

cd /sys/fs/cgroup/cpu

创建一个 Cgroup 组,即在当前目录下创建一个子目录:

mkdir testcpu

进入 testcpu 目录:

cd testcpu

查看 cpu.cfs_period_us 代表时间周期总长度:

cat cpu.cfs_period_us  # 100000

cpu.cfs_quota_us 设为当前 cgroup 在设置的周期长度内所能使用的 CPU 时间 10000,即总周期100000的 10%:

echo 10000 > cpu.cfs_quota_us  

将压力测试的进程添加到 Cgroup 组的规则中,1816 是stress进程 id:

echo 1816 > tasks

top 查看资源占用,发现 stress 进程的 CPU 占用率被压到了 10%,说明 Cgroups 对于 CPU 的控制起了效果。
图片描述

3. 思考

试想一下,我们将本节限制 CPU 资源的操作对象,从 stress 这个程序,改换成前一节的 container,就获得了一个同时限制了硬件资源和系统资源的容器。

4. 小结

通过本节的介绍,我们对 Cgroup 有了直观的认知。Cgroup 是个非常强大的系统,容器技术需要使用它进行资源的限制,但这不代表着 Cgroup 只能用于容器技术,比如在云服务商的计费系统中,也有它的身影。

或许你会感觉这一节与上一节 Namespace 的内容有些生僻,别担心,我们介绍 Namespace 和 CGroup 的目的不是去精通他们的底层原理和使用方法,而是要真正认识到:容器的本质就是一个特殊的进程