默认情况下,容器可以使用的主机CPU资源是无限的。和内存资源的使用一样,如果不限制容器可以使用的CPU资源,一旦容器内的程序异常使用CPU,很可能会耗尽整个宿主机的CPU资源,造成更大的灾难。本文介绍如何限制容器可以使用的CPU资源。本文的demo会继续使用《Docker: 限制容器可用的内存》一文中创建的docker镜像u-stress进行压力测试,文中不再过多解释。1.限制可用CPU数量在docker1.13及之后的版本上,很容易限制一个容器可以使用的宿主机CPU数量。你只需要通过--cpus选项指定容器可以使用的CPU数量,也可以指定一个小数,比如1.5。接下来,我们将在一台有四个CPU且负载很低的主机上进行demo演示:使用以下命令创建一个容器,--cpus=2表示容器最多可以使用主机上的两个CPU:$dockerrun-it--rm--cpus=2u-stress:latest/bin/bash那么stress命令创建了四个繁忙的进程来消耗CPU资源:#stress-c4先来看看dockerstats命令的输出:containerCPUload是200%,也就是说是单个CPU的负载的两倍。我们也可以理解为有两个CPU100%为它工作。下面我们用top命令看看宿主机CPU的真实负载情况:哈哈,有点意外!实际情况并不是两个CPU负载100%,而另外两个CPU负载0%。四个CPU的负载是50%,容器消耗的CPU总和是两个CPU负载的100%。一个进程似乎没有CPU个数的概念。内核只能通过进程消耗的CPU时间片来统计进程占用CPU的百分比。这就是为什么我们看到各种工具使用百分比来说明CPU使用率。为了严谨起见,我们先看看docker官方文档中--cpus选项是怎么解释的:指定一个容器可以使用多少可用的CPU资源。果然人家用的是“多少”,是不可数的!而--cpus选项支持设置为小数,这也从侧面说明CPU的度量只能是百分比。这篇文章中作者写的“CPU数量”似乎并不准确。既然不准确,为什么要用呢?当然是为了便于理解。而且,作者认为在--cpus选项的上下文中理解为“CPU个数”不是问题(有兴趣的同学可以阅读--cpus选项的由来,本意是为了表示CPU的数量)。虽然--cpus选项使用起来很酷,但它毕竟只在1.13中受支持。早期版本要完成同样的功能,我们需要同时使用两个选项:--cpu-period和--cpu-quota(1.13及之后的版本仍然支持这两个选项)。下面的命令也能达到同样的效果:$dockerrun-it--rm--cpu-period=100000--cpu-quota=200000u-stress:latest/bin/bash这样的配置选项让人哭笑不得!什么是100000?200000又是什么?它们的单位是微秒,100000代表100毫秒,200000代表200毫秒。他们在这里的意思是:每100毫秒,正在运行的进程最多使用200毫秒的CPU时间(需要两个CPU各执行100毫秒)。想彻底了解这两个选项的同学可以参考:CFSBandWithControl。我们要知道,这两种选择都是真相,但真相往往是残酷的!幸运的是,--cpus选项成功地拯救了我们。事实上,它包装了--cpu-period和--cpu-quota。2.指定一个固定的CPU通过--cpus选项,我们不能让容器一直运行在一个或几个CPU上,但是我们可以通过--cpuset-cpus选项做到!这是很有意义的,因为现在的多核系统中每个核都有自己的缓存,如果频繁的调度过程在不同的核上执行,势必会带来缓存失效等开销。下面我们来演示如何设置容器使用固定的CPU。以下命令为容器设置--cpuset-cpus选项,指定运行容器的CPU编号为1:$dockerrun-it--rm--cpuset-cpus="1"u-stress:latest/bin/bash然后启动压力测试命令:#stress-c4然后查看宿主机CPU的负载情况:此时只有Cpu1达到了100%,其他CPU没有被容器使用。我们也可以反复执行stress-c4命令,但是Cpu1一直在工作。查看容器的CPU负载,只有100%:--cpuset-cpus选项也可以一次指定多个CPU:$dockerrun-it--rm--cpuset-cpus="1,3"u-stress:latest/bin/bash这次我们指定1和3两个CPU,运行stress-c4命令,然后查看宿主机的CPU负载:Cpu1和Cpu3的负载已经达到100%。容器的CPU负载也达到了200%:--cpuset-cpus选项的一个缺点是必须指定操作系统中的CPU数量,这对于动态调度环境很有用(无法预测哪个hosts容器将在上面运行,只能通过程序动态检测系统中的CPU数量并生成dockerrun命令)会带来一些不便。3、设置CPU的权重===============当CPU资源充足的时候,设置CPU的权重是没有意义的。只有当容器竞争CPU资源时,CPU权重才能让不同的容器分配不同的CPU使用率。--cpu-shares选项用于设置CPU权重,其默认值为1024。我们可以将其设置为2以获得非常低的权重,但将其设置为0以使用默认值1024。下面我们分别运行两个容器,指定它们都使用Cpu0,并分别设置--cpu-shares为512和1024:$dockerrun-it--rm--cpuset-cpus="0"--cpu-shares=512u-stress:latest/bin/bash`$dockerrun-it--rm--cpuset-cpus="0"--cpu-shares=1024u-stress:latest/bin/bash在两个容器中运行压力-c4命令。此时宿主机CPU0的负载为100%:容器内CPU的负载为:两个容器共享一个CPU,所以总和应该是100%。具体每个容器分担的负载取决于--cpu-shares选项的设置!我们的设置分别是512和1024,所以它们的比例是1:2。在此示例中,如果您希望两个容器各占50%,只需将--cpu-shares选项设置为相同的值即可。4.总结====相比限制容器使用的内存,限制CPU的选项要简单的多。但简单绝对不是简单。大多数使复杂事情变得简单的过程都会丢失细节或模糊一些概念,例如从--cpu-period和--cpu-quota选项到--cpus选项的演变。这对用户来说当然是一件好事,它可以减缓我们的学习曲线并快速上手。
