Docker Cgroup资源配置
一.简介
- CGroup 是 Control Groups 的缩写,是 Linux 内核提供的一种可以限制、记录、隔离进程组 (process groups) 所使用的物力资源 (如 cpu memory i/o 等等) 的机制。2007 年进入 Linux 2.6.24 内核,CGroups 不是全新创造的,它将进程管理从 cpuset 中剥离出来,作者是 Google 的 Paul Menage。CGroups 也是 LXC 为实现虚拟化所使用的资源管理手段。
- Cgroups最初的目标是为资源管理提供的一个统一的框架,既整合现有的cpuset等子系统,也为未来开发新的子系统提供接口。现在的cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化(OS Level Virtualization)。
二.Cgroup 功能及组成
-
CGroup 是将任意进程进行分组化管理的 Linux 内核功能。CGroup 本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为 CGroup 子系统或控制器。CGroup 子系统有控制内存的 Memory 控制器、控制进程调度的 CPU 控制器等。运行中的内核可以使用的 Cgroup 子系统由/proc/cgroup 来确认。
-
CGroup 提供了一个 CGroup 虚拟文件系统,作为进行分组管理和各子系统设置的用户接口。要使用 CGroup,必须挂载 CGroup 文件系统。这时通过挂载选项指定使用哪个子系统。
提供的功能 | 作用 |
---|---|
限制进程组可以使用的资源数量(Resource limiting ) | 如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。 |
进程组的优先级控制(Prioritization ) | 如:可以使用cpu子系统为某个进程组分配特定cpu share。 |
记录进程组使用的资源数量(Accounting ) | 如:可以使用cpuacct子系统记录某个进程组使用的cpu时间 |
进程组隔离(Isolation) | 如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。 |
进程组控制(Control) | 如:使用freezer子系统可以将进程组挂起和恢复。 |
三.Cgroup子系统介绍
子系统 | 介绍 |
---|---|
blkio | 是块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等等) |
cpu | 使用调度程序提供对 CPU 的 cgroup |
cpuacct | 自动生成 cgroup 中任务所使用的 CPU 报告。 |
cpuset | 为 cgroup 中的任务分配独立 CPU(在多核系统)和内存节点。 |
devices | 可允许或者拒绝 cgroup 中的任务访问设备。 |
freezer | 挂起或者恢复 cgroup 中的任务。 |
memory | 设定 cgroup 中任务使用的内存限制,并自动生成由那些任务使用的内存资源报告。 |
net_cls | 使用等级识别符(classid)标记网络数据包,可允许 Linux 流量控制程序(tc)识别从具体 cgroup 中生成的数据包。 |
ns | 名称空间子系统。 |
四.Cgroup相关概念及其关系
1.相关概念
- 1.任务(task)。在cgroups中,任务就是系统的一个进程。
- 2.控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用cgroups以控制族群为单位分配的资源,同时受到cgroups以控制族群为单位设定的限制。
- 3.层级(hierarchy)。控制族群可以组织成hierarchical的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
- 4.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
2.相互关系
- 1.每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。
- 2.一个子系统最多只能附加到一个层级。
- 3.一个层级可以附加多个子系统
- 4.一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。
- 5.系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup。
五.实验搭建
1.stress工具测试CPU和内存
使用dockerfile 创建一个基于Centos的stress工具镜像
cd /opt #进入opt目录
mkdir stress #创建stress目录
cd stress #进入stress目录
vim Dockerfile #创建Dockerfile文件
FROM centos:7 #基础镜像
MAINTAINER lmx #描述信息,自定义
RUN yum install -y wget #下载wget工具(wget作用是支持网页端的下载)
RUN wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo #通过wget从阿里云下载yum仓库的配置文件,一些扩展性的软件包可以使用了
RUN yum install -y stress #下载stress软件包,作用是可以指定多个线程去不停的做循环语句
docker build -t centos:stress #生成镜像
docker run -itd --cpu-shares 100 centos:stress #创建容器,命令中的--cpu-shares参数值不能保证可以获得1个vcpu或者多个GHz的CPU资源,它仅是一个弹性的加权值。
说明:默认情况下,每个Docker容器的CPU份额都是1024。单独一个容器的份额是没有意义的。只有在同时运行多个容器时,容器的CPU加权的效果才能体现出来。
-
例如,两个容器A、B的CPU份额分别为1000和500,在CPU进行时间片分配的时候,容器A比容器B多一倍的机会获得CPU的时间片。但分配的结果取决于当时主机和其他容器的运行状态,实际上也无法保证容器A一定能获得CPU时间片。比如容器A的进程一直是空闲的,那么容器B是可以获取比容器A更多的CPU时间片的。极端情况下,例如主机上只运行了一个容器,即使它的CPU份额只有50, 它也可以独占整个主机的CPU资源。
-
Cgroups只在容器分配的资源紧缺时,即在需要对容器使用的资源进行限制时,才会生效。因此,无法单纯根据某个容器的CPU份额来确定有多少CPU资源分配给它,资源分配结果取决于同时运行的其他容器的CPU分配和容器中进程运行情况。
-
可以通过cpu share来设置容器使用CPU的优先级,比如启动了两个容器及运行查看CPU使用百分比。
docker run -tid --name cpu512 --cpu-shares 512 centos:stress stress -c 10 #容器产生10个子函数进程
docker exec -it 64bca244b4fb bash #进入容器使用top查看cpu使用情况
#再开启一个容器做比较
docker run -tid --name cpu1024 --cpu-shares 1024 centos:stress stress -c 10
docker exec -it 6c75bfc905e0 bash #进容器使用top对比两个容器的%CPU,比例是1:2
2.CPU周期限制
- Docker 提供了–cpu-period、–cpu-quota两个参数控制容器可以分配到的CPU时钟周期
- –cpu-quota是用来指定在周期内,最大可以有多少时间用来跑这个容器。与–cpu-shares不同的是,这种配置是指定一个绝对值,容器对CPU资源的使用绝对不会超过配置的值。
- cpu-period和cpu-quota的单位为微妙。cpu-period的最小值为1000微秒,最大值为1秒,默认值为0.1秒。
- cpu-quota的值默认为-1,表示不做控制。cpu-period和cpu-quota参数一般联合使用。
- 例如: 容器进程需要每1秒使用单个CPU的0.2秒时间,可以将cpu-period设置为1000000 (即1秒),cpu-quota设置为200000 (0.2秒)。
- 当然,在多核情况下,如果允许容器进程完全占用两个CPU,则可以将cpu-period设置为100000 (即0.1秒),cpu-quota设置为200000 (0.2秒)。
docker run -tid --cpu-period 100000 --cpu-quota 200000 centos:stress #创建容器,period是0.1秒,quota为0.2秒
docker exec -it a1e74331431e bash #进入容器
cat /sys/fs/cgroup/cpu/cpu.cfs_period_us #查看period文件,为0.1秒
100000
cat /sys/fs/cgroup/cpu/cpu.cfs_quota_us #查看quota文件,为0.2秒
200000
3.CPU Core控制
- 对多核CPU的服务器,Docker还可以控制容器运行使用哪些CPU内核,即使用–cpuset-cpus参数。
- 这对具有多CPU的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置。
docker run -tid --name cpu1 --cpuset-cpus 0-1 centos:stress #执行此命令需要宿主机为双核,表示创建的容器只能用0、1两个内核。
最终生成的cgroup的CPU内核配置如下:
docker exec -it d708eb4f6f03 bash #进入容器
cat /sys/fs/cgroup/cpuset/cpuset.cpus
0-1
docker exec d708eb4f6f03 taskset -c -p 1 #容器内部第一个进程号pid为1被绑定到指定CPU上运行
4.CPU配额控制参数的混合使用
-
通过cpuset-cpus参数指定容器A使用CPU内核0,容器B只使用CPU内核1。
-
在主机上只有这两个容器使用对应CPU内核的情况,它们各自占用全部的内核资源,cpu-shares没有明显效果。
-
cpuset-cpus、cpuset-mems 参数只在多核、多内存节点上的服务器上有效,并且必须与实际的物理配置匹配,否则也无法达到资源控制的目的。
-
在系统具有多个CPU内核的情况下,需要通过cpuset-cpus参数为设置容器CPU内核才能方便地进行测试。
##提前将宿主系统修改为4核心CPU##
docker run -tid --name cpu3 --cpuset-cpus 1 --cpu-shares 512 centos:stress stress -c 1 #创建容器
docker exec -it 8799a625c89c bash #进入容器
docker run -tid --name cpu4 --cpuset-cpus 3 --cpu-shares 1024 centos:stress stress -c 1 #创建容器
top #按1查看每个核心的占用
docker exec -it 75985e7e13d5 bash
总结: 上面的 centos:stress 镜像安装了stress工具,用来测试CPU和内存的负载。通过在两个容器上分别执行stress -c 1命令,将会给系统一个随机负载,产生1个进程。这个进程都反复不停的计算由 rand() 产生随机数的平方根,直到资源耗尽。
观察到宿主机上的CPU使用率,第三个内核的使用率接近100%, 并且一批进程的CPU使用率明显存在2:1的使用比例的对比。
5.内存限额
- 与操作系统类似,容器可使用的内存包括两部分::物理内存和Swap。
- Docker通过下面两组参数来控制容器内存的使用量。
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M #允许该容器最多使用200M的内存和300M的swap。
--vm 1: 启动1个内存工作线程。
--vm-bytes 280M: 每个线程分配280M内存。
-m或--memory: 设置内存的使用限额(软限额)
--memory-swap: 设置内存+swap的使用限额(硬限额)
默认情况下,容器可以使用主机上的所有空闲内存。与CPU的cgroups配置类似,Docker会自动为容器在目录/sys/fs/cgroup/memory/docker/<容器的完整长ID>中创建相应cgroup配置文件
如果让工作线程分配的内存超过300M,分配的内存超过限额,stress线程报错,容器退出。
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M
6.Block IO 的限制
- 默认情况下,所有容器能平等地读写磁盘,可以通过设置–blkio-weight参数来改变容器block IO的优先级。
- –blkio-weight与–cpu-shares类似,设置的是相对权重值,默认为500。
docker run -it --name container_A --blkio-weight 600 centos:stress
cat /sys/fs/cgroup/blkio/blkio.weight
docker run -it --name container_B --blkio-weight 300 centos:stress
cat /sys/fs/cgroup/blkio/blkio.weight
7.bps和iops的限制
- bps 是byte per second, 每秒读写的数据量。
- iops 是io per second, 每秒 IO 的次数。
参数 | 作用 |
---|---|
–device-read-bps | 限制读某个设备的bps |
–device-write-bps | 限制写某个设备的bps |
–device-read-iops | 限制读某个设备的iops |
–device-write-iops | 限制写某个设备的iops |
docker run -it --device-write-bps /dev/sda:8MB centos:stress
dd if=/dev/zero of=test bs=1M count=1024 oflag=direct #查看,可以按ctrI+c中断查看
通过dd命令测试在容器中写磁盘的速度。因为容器的文件系统是在host /dev/sda上的,在容器中写文件相当于对host/dev/sda进行写操作。另外,oflag=direct 指定用 direct IO 方式写文件,这样–device-write-bps才能生效
docker run -it centos:stress
dd if=/dev/zero of=test bs=1M count=1024 oflag=direct