《Docker进阶与实战》——2.3节Cgroup介绍

2.3 Cgroup介绍
2.3.1 Cgroup是什么
Cgroup是control group的简写,属于Linux内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也就是做资源QoS,这些资源主要包括CPU、内存、block I/O和网络带宽。Cgroup从2.6.24开始进入内核主线,目前各大发行版都默认打开了Cgroup特性。
从实现的角度来看,Cgroup实现了一个通用的进程分组的框架,而不同资源的具体管理则是由各个Cgroup子系统实现的。截止到内核4.1版本,Cgroup中实现的子系统及其作用如下:
devices:设备权限控制。
cpuset:分配指定的CPU和内存节点。
cpu:控制CPU占用率。
cpuacct:统计CPU使用情况。
memory:限制内存的使用上限。
freezer:冻结(暂停)Cgroup中的进程。
net_cls:配合tc(traffic controller)限制网络带宽。
net_prio:设置进程的网络流量优先级。
huge_tlb:限制HugeTLB的使用。
perf_event:允许Perf工具基于Cgroup分组做性能监测。
在Cgroup出现之前,只能对一个进程做一些资源控制,例如通过sched_setaffinity系统调用限定一个进程的CPU亲和性,或者用ulimit限制一个进程的打开文件上限、栈大小等。另外,使用ulimit可以对少数资源基于用户做资源控制,例如限制一个用户能创建的进程数。而Cgroup可以对进程进行任意的分组,如何分组是用户自定义的,例如安卓的应用分为前台应用和后台应用,前台应用是直接跟用户交互的,需要响应速度快,因此前台应用对资源的申请需要得到更多的保证。为此安卓将前台应用和后台应用划分到不同的Cgroup中,并且对放置前台应用的Cgroup配置了较高的系统资源限额。
从1.6版本开始,Docker也支持ulimit,读者可以查阅相关Docker文档及Linux用户手册。

2.3.2 Cgroup的接口和使用
Cgroup的原生接口通过cgroupfs提供,类似于procfs和sysfs,是一种虚拟文件系统。以下用一个实例演示如何使用Cgroup。
(1)挂载cgroupfs
命令如下:

# mount –t cgroup –o cpuset cpuset /sys/fs/cgroup/cpuset

首先必须将cgroupfs挂载起来,这个动作一般已经在启动时由Linux发行版做好了。可以把cgroupfs挂载在任意一个目录上,不过标准的挂载点是/sys/fs/cgroup。
实际上sysfs里面只有/sys/fs/cgroup目录,并且sysfs是不允-许用户创建目录的,这里可以将tmpfs挂载上去,然后在tmpfs上创建目录。

(2)查看cgroupfs
# ls /sys/fs/cgroup/cpuset
cgroup.clone_children  cpuset.memory_pressure
cgroup.procs           cpuset.memory_pressure_enabled
cgroup.sane_behavior   cpuset.memory_spread_page
cpuset.cpu_exclusive   cpuset.memory_spread_slab
cpuset.cpus            cpuset.mems
cpuset.effective_cpus  cpuset.sched_load_balance
cpuset.effective_mems  cpuset.sched_relax_domain_level
cpuset.mem_exclusive   notify_on_release
cpuset.mem_hardwall    release_agent
cpuset.memory_migrate  tasks

可以看到这里有很多控制文件,其中以cpuset开头的控制文件都是cpuset子系统产生的,其他文件则由Cgroup产生。这里面的tasks文件记录了这个Cgroup的所有进程(包括线程),在系统启动后,默认没有对Cgroup做任何配置的情况下,cgroupfs只有一个根目录,并且系统所有的进程都在这个根目录中,即进程pid都在根目录的tasks文件中。

意 实际上现在大多数Linux发行版都是采用的systemd,而systemd使用了Cgroup,所以在这些发行版上,当系统启动后看到的cgroupfs是有一些子目录的。

(3)创建Cgroup

# mkdir /sys/fs/cgroup/cpuset/child
通过mkdir创建一个新的目录,也就创建了一个新的Cgroup。
(4)配置Cgroup
# echo 0 > /sys/fs/cgroup/cpuset/child/cpuset.cpus
# echo 0 > /sys/fs/cgroup/cpsuet/child/cpuset.mems
接下来配置这个Cgroup的资源配额,通过上面的命令,就可以限制这个Cgroup的进程只能在0号CPU上运行,并且只会从0号内存节点分配内存。
(5)使能Cgroup
# echo $$ > /sys/fs/cgroup/cpuset/child/tasks

最后,通过将进程id写入tasks文件,就可以把进程移动到这个Cgroup中。并且,这个进程产生的所有子进程也都会自动放在这个Cgroup里。这时,Cgroup才真正起作用了。
$$表示当前进程。另外,也可以把pid写入cgroup.procs中,两者的区别是写入tasks只会把指定的进程加到child中,写入cgroup.procs则会把这个进程所属的整个线程组都加到child中。

2.3.3 Cgroup子系统介绍
对实际资源的分配和管理是由各个Cgroup子系统完成的,下面介绍几个主要的子系统。
1. cpuset子系统
cpuset可以为一组进程分配指定的CPU和内存节点。cpuset一开始是用在高性能计算(HPC)上的,在NUMA架构的服务器上,通过将进程绑定到固定的CPU和内存节点上,来避免进程在运行时因跨节点内存访问而导致的性能下降。当然,现在cpuset也广泛用在了kvm和容器等场景上。
cpuset的主要接口如下。
cpuset.cpus:允许进程使用的CPU列表(例如0~4,9)。
cpuset.mems:允许进程使用的内存节点列表(例如0~1)。
2. cpu子系统
cpu子系统用于限制进程的CPU占用率。实际上它有三个功能,分别通过不同的接口来提供。
CPU比重分配。这个特性使用的接口是cpu.shares。假设在cgroupfs的根目录下创建了两个Cgroup(C1和C2),并且将cpu.shares分别配置为512和1024,那么当C1和C2争用CPU时,C2将会比C1得到多一倍的CPU占用率。要注意的是,只有当它们争用CPU时cpu share才会起作用,如果C2是空闲的,那么C1可以得到全部的CPU资源。
CPU带宽限制。这个特性使用的接口是cpu.cfs_period_us和cpu.cfs_quota_us,这两个接口的单位是微秒。可以将period设置为1秒,将quota设置为0.5秒,那么Cgroup中的进程在1秒内最多只能运行0.5秒,然后就会被强制睡眠,直到进入下一个1秒才能继续运行。
实时进程的CPU带宽限制。以上两个特性都只能限制普通进程,若要限制实时进程,就要使用cpu.rt_period_us和cpu.rt_runtime_us这两个接口了。使用方法和上面类似。
3. cpuacct子系统
cpuacct子系统用来统计各个Cgroup的CPU使用情况,有如下接口。
cpuacct.stat:报告这个Cgroup分别在用户态和内核态消耗的CPU时间,单位是USER_HZ。USER_HZ在x86上一般是100,即1 USER_HZ等于0.01秒。
cpuacct.usage:报告这个Cgroup消耗的总CPU时间,单位是纳秒。
cpuacct.usage_percpu:报告这个Cgroup在各个CPU上消耗的CPU时间,总和也就是cpuacct.usage的值。
4. memory子系统
memory子系统用来限制Cgroup所能使用的内存上限,有如下接口。
memory.limit_in_bytes:设定内存上限,单位是字节,也可以使用k/K、m/M或者g/G表示要设置数值的单位,例如

# echo 1G > memory.limit_in_bytes

默认情况下,如果Cgroup使用的内存超过上限,Linux内核会尝试回收内存,如果仍然无法将内存使用量控制在上限之内,系统将会触发OOM,选择并“杀死”该Cgroup中的某个进程。

memory.memsw.limit_in_bytes:设定内存加上交换分区的使用总量。通过设置这个值,可以防止进程把交换分区用光。
memory.oom_control:如果设置为0,那么在内存使用量超过上限时,系统不会“杀死”进程,而是阻塞进程直到有内存被释放可供使用时;另一方面,系统会向用户态发送事件通知,用户态的监控程序可以根据该事件来做相应的处理,例如提高内存上限等。
memory.stat:汇报内存使用信息。

  1. blkio子系统
    blkio子系统用来限制Cgroup的block I/O带宽,有如下接口。
    blkio.weight:设置权重值,范围在100到1000之间。这跟cpu.shares类似,是比重分配,而不是绝对带宽的限制,因此只有当不同的Cgroup在争用同一个块设备的带宽时才会起作用。

    blkio.weight_device:对具体的设备设置权重值,这个值会覆盖上述的blkio.weight。例如将Cgroup对/dev/sda的权重设为最小值:

    # echo 8:0 100 > blkio.weight_device

    blkio.throttle.read_bps_device:对具体的设备,设置每秒读磁盘的带宽上限。例如对/dev/sda的读带宽限制在1MB/秒:

    # echo "8:0 1048576" > blkio.throttle.read_bps_device


    blkio.throttle.write_bps_device:设置每秒写磁盘的带宽上限。同样需要指定设备。
    blkio.throttle.read_iops_device:设置每秒读磁盘的IOPS上限。同样需要指定设备。
    blkio.throttle.write_iops_device:设置每秒写磁盘的IOPS上限。同样需要指定设备。
    ```
    blkio子系统有两个重大的缺陷。一是对于比重分配,只支持CFQ调度器。另一个缺陷是,不管是比重分配还是上限限制,都只支持Direct-IO,不支持Buffered-IO,这使得blkio cgroup的使用场景很有限,好消息是Linux内核社区正在解决这个问题。
  1. devices子系统
    devices子系统用来控制Cgroup的进程对哪些设备有访问权限,有如下接口。
    devices.list。只读文件,显示目前允许被访问的设备列表。每个条目都有3个域,分别为:
    类型:可以是a、c或b,分别表示所有设备、字符设备和块设备。
    设备号:格式为major:minor的设备号。
    权限:r、w和m的组合,分别表示可读、可写、可创建设备结点(mknod)。
    例如“a : rmw”,表示所有设备都可以被访问。而“c 1:3 r”,表示对1:3这个字符设备(即/dev/null)只有读权限。
    devices.allow。只写文件,以上面描述的格式写入该文件,就可以允许相应的设备访问权限。
    devices.deny。只写文件,以上面描述的格式写入该文件,就可以禁止相应的设备访问权限。
时间: 2024-08-30 15:20:10

《Docker进阶与实战》——2.3节Cgroup介绍的相关文章

《Docker进阶与实战》——导读

目 录 序 前 言 第1章 Docker简介1.1 引言1.2 功能和组件1.3 安装和使用1.4 概念澄清1.5 本章小结第2章 关于容器技术2.1 容器技术的前世今生2.2 一分钟理解容器2.3 Cgroup介绍2.4 Namespace介绍2.5 容器造就Docker2.6 本章小结第3章 理解Docker镜像3.1 Docker image概念介绍3.2 使用Docker image3.3 Docker image的组织结构3.4 Docker image扩展知识3.5 本章小结第4章

《Docker进阶与实战》——2.6节本章小结

2.6 本章小结本章对容器技术做了详细的剖析,相信读者已经对容器的概念和原理有了清晰的认识,在这样的基础之上,可以更加轻松和深刻地理解Docker的一系列技术了,接下来的章节会针对Docker中的各个子系统做详细的介绍.

《Docker进阶与实战》——1.3节安装和使用

1.3 安装和使用1.3.1 Docker的安装 Docker的安装和使用有一些前提条件,主要体现在体系架构和内核的支持上.对于体系架构,除了Docker一开始就支持的x86-64,其他体系架构的支持则一直在不断地完善和推进中,用户在安装前需要到Docker官方网站查看最新的支持情况.对于内核,目前官方的建议是3.10以上的版本,除了内核版本以外,Docker对于内核支持的功能,即内核的配置选项也有一定的要求(比如必须开启Cgroup和Namespace相关选项,以及其他的网络和存储驱动等).如

《Docker进阶与实战》——1.4节概念澄清

1.4 概念澄清 本书的附录A是关于Docker常见问题的解答,但对于Docker基本概念方面的问题,希望读者可以在阅读完本章后就有清晰的认识,所以本节会针对与Docker概念息息相关的几个常见问题进行说明.1.4.1 Docker在LXC基础上做了什么工作 首先我们需要明确LXC的概念,但这常常有不同的认识.LXC目前代表两种含义: LXC用户态工具(https://github.com/lxc/lxc). Linux Container,即内核容器技术的简称. 这里通常指第二种,即Docke

《Docker进阶与实战》——2.2节一分钟理解容器

2.2 一分钟理解容器2.2.1 容器的组成 上文已多次提及,容器的核心技术是Cgroup + Namespace,但光有这两个抽象的技术概念是无法组成一个完整的容器的.在2.1.2节也提到过最早的容器概念就包括了对文件目录视图的抽象隔离,而所有的这一切,都需要有工具来驱动,需要有一个工具来提供用户可操作的接口,来创建一个容器.所以笔者认为,对于Linux容器的最小组成,可以由以下公式来表示: 容器 = cgroup + namespace + rootfs + 容器引擎(用户态工具) 其中各项

《Docker进阶与实战》——2.1节容器技术的前世今生

2.1 容器技术的前世今生2.1.1 关于容器技术 容器技术,又称为容器虚拟化,从字面上看它首先是一种虚拟化技术.在如今的技术浪潮下,虚拟化技术层出不穷,包括硬件虚拟化.半虚拟化.操作系统虚拟化等.本书不会对虚拟化技术展开介绍,只需要知道容器虚拟化是一种操作系统虚拟化,是属于轻量级的虚拟化技术即可.又因为在实现原理上,每一种虚拟化技术之间都有较大的差别,所以即使没有虚拟化的技术背景,也是可以单独来学习容器虚拟化的. 容器技术之所以受欢迎,一个重要的原因是它已经集成到了Linux内核中,已经被当作

《Docker进阶与实战》——2.4节SparkContext概述

2.4 Namespace介绍2.4.1 Namespace是什么 Namespace是将内核的全局资源做封装,使得每个Namespace都有一份独立的资源,因此不同的进程在各自的Namespace内对同一种资源的使用不会互相干扰. 这样的解释可能不清楚,举个例子,执行sethostname这个系统调用时,可以改变系统的主机名,这个主机名就是一个内核的全局资源.内核通过实现UTS Namespace,可以将不同的进程分隔在不同的UTS Namespace中,在某个Namespace修改主机名时,

《Docker进阶与实战》——2.5节容器造就Docker

2.5 容器造就Docker 关于容器是否是Docker的核心技术,业界一直存在着争议.有人认为Docker的核心技术是对分层镜像的创新使用,有人认为其核心是统一了应用的打包分发和部署方式,为服务器级别的"应用商店"提供了可能,而这将会是颠覆传统行业的举措.事实上,这一系列创新并不是依赖于容器技术的,基于传统的hypervisor也可以做到,业界也由此诞生了一些开源项目,比如Hyper.Clear Linux等.另外,Docker官方对Docker核心功能的描述"Build,

《Docker进阶与实战》——3.2节使用Docker image

3.2 使用Docker image Docker内嵌了一系列命令制作.管理.上传和下载镜像.可以调用REST API给Docker daemon发送相关命令,也可以使用client端提供的CLI命令完成操作.本书的第7章会详细阐述Docker REST API的细节,本节则主要根据功能对涉及image的命令进行说明.下面就从Docker image的生命周期角度说明Docker image的相关使用方法. 3.2.1 列出本机的镜像 下面的命令可以列出本地存储中镜像,也可以查看这些镜像的基本信