docker资源隔离

1、本文将介绍 cgroup 如何作到内存,cpu 和 io 速率的隔离node

本文用脚本运行示例进程,来验证 Cgroups 关于 cpu、内存、io 这三部分的隔离效果。linux

测试机器环境(docker 1.12版本)程序员

AJ3YF3v.jpg!web

aAnquq.png!web

启动 Cgroupsweb

systemctl enable cgconfig.service 
docker

systemctl start cgconfig.servicecentos

执行 mount 命令查看 cgroup 的挂载点服务器

myeMveu.png!web

从上图能够看到 cgroup 挂载在/sys/fs/cgroup 目录网络

groups 能够限制 blkio、cpu、cpuacct、cpuset、devices、freezer、memory、net_cls、ns 等系运维

统的资源,如下是主要子系统的说明:ide

blkio 这个子系统设置限制每一个块设备的输入输出控制。例如:磁盘,光盘以及 usb 等等。

cpu 这个子系统使用调度程序为 cgroup 任务提供 cpu 的访问。

cpuacct 产生 cgroup 任务的 cpu 资源报告。

cpuset 若是是多核心的 cpu,这个子系统会为 cgroup 任务分配单独的 cpu 和内存。

devices 容许或拒绝 cgroup 任务对设备的访问。

freezer 暂停和恢复 cgroup 任务。

memory 设置每一个 cgroup 的内存限制以及产生内存资源报告。

net_cls 标记每一个网络包以供 cgroup 方便使用,它经过使用等级识别符(classid)标记网络数

据包,从而容许 Linux 流量控制程序(TC:Traffic Controller)识别从具体 cgroup 中生成

的数据包。

ns:命名空间子系统

cgroups 管理进程 cpu 资源

咱们先看一个限制 cpu 资源的例子:

跑一个耗 cpu 的脚本

运行一个容器,在容器内建立脚本并运行脚本,脚本内容:

JRNZzqe.png!web

ayqQfmn.png!web

将容器切换到后台运行

ctrl + p && ctrl+q

在宿主机上 top 能够看到这个脚本基本占了 90%多的 cpu 资源

faMZ7j6.jpg!web

下面用 cgroups 控制这个进程的 cpu 资源

对于 centos7 来讲,经过 systemd-cgls 来查看系统 cgroups tree:

#systemd-cgls

Z3U77jj.jpg!web

注:5028 就是咱们所运行的容器 pid

将 cpu.cfs_quota_us 设为 50000,相对于 cpu.cfs_period_us 的 100000 是 50%

rymAjaN.png!web

ru6Fnen.png!web

进入容器,再次执行脚本,打开宿主机的另外一个终端执行 top 命令

而后 top 的实时统计数据以下,cpu 占用率将近 50%,看来 cgroups 关于 cpu 的控制起了效果

F7v2uiY.jpg!web

CPU 资源控制

CPU 资源的控制也有两种策略,一种是彻底公平调度 (CFS:Completely Fair Scheduler)

策略,提供了限额和按比例分配两种方式进行资源控制;另外一种是实时调度(Real-Time

Scheduler)策略,针对实时进程按周期分配固定的运行时间。配置时间都以微秒(μs)为

单位,文件名中用 us 表示。

CFS  调度策略下的配置

按权重比例设定 CPU 的分配

docker 提供了Ccpu-shares 参数,在建立容器时指定容器所使用的 CPU 份额值。例如:

使用命令 docker run -tid Ccpu-shares 100 镜像,建立容器,则最终生成的 cgroup 的 cpu 份额

配置能够下面的文件中找到:

# cat /sys/fs/cgroup/cpu/docker-<容器的完整长 ID>/cpu.shares

uUVJRr6.png!web

cpu-shares 的值不能保证能够得到 1 个 vcpu 或者多少 GHz 的 CPU 资源,仅仅只是一个加权

值。

该加权值是一个整数(必须大于等于 2)表示相对权重,最后除以权重总和算出相对比例,

按比例分配 CPU 时间。

默认状况下,每一个 docker 容器的 cpu 份额都是 1024。单独一个容器的份额是没有意义的,

只有在同时运行多个容器时,容器的 cpu 加权的效果才能体现出来。例如,两个容器 A、B

的 cpu 份额分别为 1000 和 500,在 cpu 进行时间片分配的时候,容器 A 比容器 B 多一倍的

机会得到 CPU 的时间片。若是容器 A 的进程一直是空闲的,那么容器 B 是能够获取比容器

A 更多的 CPU 时间片的。极端状况下,好比说主机上只运行了一个容器,即便它的 cpu 份额

只有 50,它也能够独占整个主机的 cpu 资源。

cgroups 只在容器分配的资源紧缺时,也就是说在须要对容器使用的资源进行限制时,才会

生效。所以,没法单纯根据某个容器的 cpu 份额来肯定有多少 cpu 资源分配给它,资源分配

结果取决于同时运行的其余容器的 cpu 分配和容器中进程运行状况。

cpu-shares 演示案例:

先删除 docker 主机上运行的容器

QrMNfe2.png!web

Docker 经过--cpu-shares 指定 CPU 份额

运行一个容器指定 cpu 份额为 1024

UriQJrI.png!web

注:

--cpu-shares 指定 CPU 份额,默认就是 1024

--cpuset-cpus 能够绑定 CPU。例如,指定容器在--cpuset-cpus 0,1 或--cpuset-cpus 0-3

--cpu 是 stress 命令的选项表示产生 n 个进程 每一个进程都反复不停的计算随机数的平方根

stress 命令是 linux 下的一个压力测试工具。

在 docker 宿主机上打开一个 terminal 执行 top

qIZB3am.jpg!web

而后再启动一个容器, --cpu-shares 为 512 。

baIfamA.png!web

查看 top 的显示结果

E3EZNrm.jpg!web

能够看到 container1 的 CPU 占比为 1024/(1024+512)=2/3,container2 的 CPU 占比为

512/(1024+512)=1/3

将 container1 的 cpu.shares 改成 512,

#echo “512” > /sys/fs/cgroup/cpu/docker-<容器的完整长 ID>/cpu.shares

zqU7jaB.png!web

能够看到两个容器的 CPU 占比趋于平均

yyqaUn2.png!web

设定 CPU 使用周期使用时间上限

cgroups 里,能够用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的全部进程在单

位时间里可使用的 cpu 时间。cpu.cfs_period_us 就是时间周期,默认为 100000,即百毫

秒。cpu.cfs_quota_us 就是在这期间内可以使用的 cpu 时间,默认 -1,即无限制。

cpu.cfs_period_us :设定时间周期(单位为微秒(μs)),必须与 cfs_quota_us 配合使用。

cpu.cfs_quota_us :设定周期内最多可以使用的时间(单位为微秒(μs))。这里的配置指 task

对单个 cpu 的使用上限。

举个例子,若是容器进程须要每 1 秒使用单个 CPU 的 0.2 秒时间,能够将 cpu-period 设置为

1000000(即 1 秒),cpu-quota 设置为 200000(0.2 秒)。

固然,在多核状况下,若 cfs_quota_us 是 cfs_period_us 的两倍,就表示在两个核上

彻底使用 CPU,例如若是容许容器进程须要彻底占用两个 CPU,则能够将 cpu-period 设置为

100000(即 0.1 秒),cpu-quota 设置为 200000(0.2 秒)。

使用示例:

使用命令 docker run 建立容器

2y2qQfF.jpg!web

在宿主机上执行 top

qUV7B3Z.png!web

从上图能够看到基本占了 100%的 cpu 资源

则最终生成的 cgroup 的 cpu 周期配置能够下面的目录中找到:

/sys/fs/cgroup/cpu/docker-<容器的完整长 ID>/

NNnYNbE.png!web

修改容器的 cpu.cfs_period_us 和 cpu.cfs_quota_us 值

mQvEZza.jpg!web

执行 top 查看 cpu 资源

UzyuuaJ.png!web

从上图能够看到基本占了 50%的 cpu 资源

RT  调度策略下的配置 实时调度策略与公平调度策略中的按周期分配时间的方法相似,也是

在周期内分配一个固定的运行时间。

cpu.rt_period_us :设定周期时间。

cpu.rt_runtime_us :设定周期中的运行时间。

NVr2Irb.jpg!web

cpuset - CPU 绑定

对多核 CPU 的服务器,docker 还能够控制容器运行限定使用哪些 cpu 内核和内存节点,即

使用Ccpuset-cpus 和Ccpuset-mems 参数。对具备 NUMA 拓扑(具备多 CPU、多内存节点)的

服务器尤为有用,能够对须要高性能计算的容器进行性能最优的配置。若是服务器只有一个

内存节点,则Ccpuset-mems 的配置基本上不会有明显效果

注:

如今的机器上都是有多个 CPU 和多个内存块的。之前咱们都是将内存块当作是一大块内存,

全部 CPU 到这个共享内存的访问消息是同样的。可是随着处理器的增长,共享内存可能会

致使内存访问冲突愈来愈厉害,且若是内存访问达到瓶颈的时候,性能就不能随之增长。

NUMA(Non-Uniform Memory Access)就是这样的环境下引入的一个模型。好比一台机

器是有2个处理器,有4个内存块。咱们将1个处理器和两个内存块合起来,称为一个NUMA

node,这样这个机器就会有两个 NUMA node。在物理分布上,NUMA node 的处理器和内

存块的物理距离更小,所以访问也更快。好比这台机器会分左右两个处理器(cpu1, cpu2),

在每一个处理器两边放两个内存块(memory1.1, memory1.2, memory2.1,memory2.2),这样

NUMA node1 的 cpu1 访问 memory1.1 和 memory1.2 就比访问 memory2.1 和 memory2.2

更快。因此使用 NUMA 的模式若是能尽可能保证本 node 内的 CPU 只访问本 node 内的内存

块,那这样的效率就是最高的。

使用示例:

feQrmin.jpg!web

表示建立的容器只能用 0、一、2 这三个内核。最终生成的 cgroup 的 cpu 内核配置以下:

QZfq6fj.png!web

cpuset.cpus:在这个文件中填写 cgroup 可以使用的 CPU 编号,如 0-2,16 表明 0、一、2 和 16

这 4 个 CPU。

cpuset.mems:与 CPU 相似,表示 cgroup 可以使用的 memory node,格式同上

经过 docker exec <容器 ID> taskset -c -p 1(容器内部第一个进程编号通常为 1),能够看到容器

中进程与 CPU 内核的绑定关系,能够认为达到了绑定 CPU 内核的目的。

总结:

CPU 配额控制参数的混合使用

当上面这些参数中时,cpu-shares 控制只发生在容器竞争同一个内核的时间片时,若是经过

cpuset-cpus 指定容器 A 使用内核 0,容器 B 只是用内核 1,在主机上只有这两个容器使用对

应内核的状况,它们各自占用所有的内核资源,cpu-shares 没有明显效果。

cpu-period、cpu-quota 这两个参数通常联合使用,在单核状况或者经过 cpuset-cpus 强制容

器使用一个 cpu 内核的状况下,即便 cpu-quota 超过 cpu-period,也不会使容器使用更多的

CPU 资源。

cpuset-cpus、cpuset-mems 只在多核、多内存节点上的服务器上有效,而且必须与实际的物

理配置匹配,不然也没法达到资源控制的目的。

在系统具备多个 CPU 内核的状况下,须要经过 cpuset-cpus 为容器 CPU 内核才能比较方便地进行测试。

内存配额控制

和 CPU 控制同样,docker 也提供了若干参数来控制容器的内存使用配额,能够控制容器的

swap 大小、可用内存大小等各类内存方面的控制。主要有如下参数:

Docker 提供参数-m, --memory=""限制容器的内存使用量,若是不设置-m,则默认容器内存

是不设限的,容器可使用主机上的全部空闲内存

内存配额控制使用示例

设置容器的内存上限,参考命令以下所示

#docker run -dit --memory 128m 镜像

默认状况下,除了Cmemory 指定的内存大小之外,docker 还为容器分配了一样大小的 swap

分区,也就是说,上面的命令建立出的容器实际上最多可使用 256MB 内存,而不是 128MB内存。若是须要自定义 swap 分区大小,则能够经过联合使用CmemoryCswap 参数来实现控制。

yUNfqez.png!web

能够发现,使用 256MB 进行压力测试时,因为超过了内存上限(128MB 内存+128MB swap), 进程被 OOM(out of memory)杀死。

使用 250MB 进行压力测试时,进程能够正常运行。

re6ZFvi.png!web

经过 docker stats 能够查看到容器的内存已经满负载了。

#docker stats test2

yUn2amM.png!web

对上面的命令建立的容器,能够查看到在 cgroups 的配置文件中,查看到容器的内存大小为

128MB (128×1024×1024=134217728B), 内 存 和 swap 加 起 来 大 小 为 256MB

(256×1024×1024=268435456B)。

#cat /sys/fs/cgroup/memory/docker-<容器的完整 ID>/memory.limit_in_bytes

134217728

#cat  /sys/fs/cgroup/memory/docker-< 容 器 的 完 整 ID>/memory.memsw.limit_in_bytes

268435456

YjyYj2z.jpg!web

盘 磁盘 IO  配额控制

主要包括如下参数:

--device-read-bps:限制此设备上的读速度(bytes per second),单位能够是 kb、mb 或者 gb。

--device-read-iops:经过每秒读 IO 次数来限制指定设备的读速度。

--device-write-bps :限制此设备上的写速度(bytes per second),单位能够是 kb、mb 或者 gb。

--device-write-iops:经过每秒写 IO 次数来限制指定设备的写速度。

--blkio-weight:容器默认磁盘 IO 的加权值,有效值范围为 10-1000。

--blkio-weight-device: 针对特定设备的 IO 加权控制。其格式为 DEVICE_NAME:WEIGHT

磁盘 IO 配额控制示例

blkio-weight

使用下面的命令建立两个Cblkio-weight 值不一样的容器:

在容器中同时执行下面的 dd 命令,进行测试

mmiIbmf.png!web

注:oflag=direct 规避掉文件系统的 cache,把写请求直接封装成 io 指令发到硬盘

2、学习 Docker 也有一段时间了,了解了 Docker 的基本实现原理,也知道了 Docker 的使

用方法,这里对 Docker 的一些典型应用场景作一个总结

一、配置简化

这是 Docker 的主要使用场景。将应用的全部配置工做写入 Dockerfile 中,建立好镜像,

之后就能够无限次使用这个镜像进行应用部署了。这大大简化了应用的部署,不须要为每次

部署都进行繁琐的配置工做,实现了一次打包,屡次部署。这大大加快了应用的开发效率,

使得程序员能够快速搭建起开发测试环境,不用关注繁琐的配置工做,而是将全部精力都尽

可能用到开发工做中去。

二、代码流水线管理

代码从开发环境到测试环境再到生产环境,须要通过不少次中间环节,Docker 给应用

提供了一个从开发到上线均一致的环境,开发测试人员均只需关注应用的代码,使得代码的

流水线变得很是简单,这样应用才能持续集成和发布。

三、快速部署

在虚拟机以前,引入新的硬件资源须要消耗几天的时间。Docker 的虚拟化技术将这个

时间降到了几分钟,Docker 只是建立一个容器进程而无需启动操做系统,这个过程只须要

秒级的时间。

四、应用隔离

资源隔离对于提供共享 hosting 服务的公司是个强需求。 若是使用 VM,虽然隔离性非

常完全,但部署密度相对较低,会形成成本增长。

Docker 容器充分利用 linux 内核的 namespace 提供资源隔离功能。结合 cgroups,能够方便

的设置每一个容器的资源配额。既能知足资源隔离的需求,又能方便的为不一样级别的用户设置

不一样级别的配额限制。

五、服务器资源整合

正如经过 VM 来整合多个应用,Docker 隔离应用的能力使得 Docker 一样能够整合服务

器资源。因为没有额外的操做系统的内存占用,以及能在多个实例之间共享没有使用的内存,

Docker 能够比 VM 提供更好的服务器整合解决方案。

一般数据中心的资源利用率只有 30%,经过使用 Docker 并进行有效的资源分配能够提升资

源的利用率。

六、多版本混合部署

随着产品的不断更新换代,一台服务器上部署多个应用或者同一个应用的多个版本在企

业内部很是常见。但一台服务器上部署同一个软件的多个版本,文件路径、端口等资源每每

会发生冲突,形成多个版本没法共存的问题。

若是用 docker,这个问题将很是简单。因为每一个容器都有本身独立的文件系统,因此根本不

存在文件路径冲突的问题; 对于端口冲突问题,只须要在启动容器时指定不一样的端口映射即

可解决问题。

七、版本升级回滚

一次升级,每每不只仅是应用软件自己的升级,经过还会包含依赖项的升级。 但新旧

软件的依赖项极可能是不一样的,甚至是有冲突的,因此在传统的环境下作回滚通常比较困难。

若是使用 docker,咱们只须要每次应用软件升级时制做一个新的 docker 镜像,升级时先停

掉旧的容器, 而后把新的容器启动。 须要回滚时,把新的容器停掉,旧的启动便可完成回

滚,整个过程各在秒级完成,很是方便。

八、内部开发环境

在容器技术出现以前,公司每每是经过为每一个开发人员提供一台或者多台虚拟机来充当

开发测试环境。开发测试环境通常负载较低,大量的系统资源都被浪费在虚拟机自己的进程

上了。

Docker容器没有任何CPU和内存上的额外开销,很适合用来提供公司内部的开发测试环境。

并且因为 Docker 镜像能够很方便的在公司内部共享,这对开发环境的规范性也有极大的帮

助。

九、PaaS

使用 Docker 搭建大规模集群,提供 PaaS。这一应用是最有前景的一个了,目前已有很

多创业公司在使用 Docker 作 PaaS 了,例如云雀云平台。用户只需提交代码,全部运维工做

均由服务公司来作。并且对用户来讲,整个应用部署上线是一键式的,很是方便。

十、云桌面

在每个容器内部运行一个图形化桌面,用户经过 RDP 或者 VNC 协议链接到容器。该

方案所提供的虚拟桌面相比于传统的基于硬件虚拟化的桌面方案更轻量级,运行速率大大提

升。不过该方案仍处于实验阶段,不知是否可行。能够参考一下 Docker-desktop 方案。

相关文章
相关标签/搜索