容器核心技术–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 的目的不是去精通他们的底层原理和使用方法,而是要真正认识到:容器的本质就是一个特殊的进程。