写点什么

一次搞明白 Docker 容器资源限制

发布于: 2021 年 02 月 21 日

前言


在使用容器时(未被 Kubernetes 进行管理的情况下),我们单台主机上可能会跑几十个容器,容器虽然都相互隔离,但是用的却是与宿主机相同的内核,CPU、内存、磁盘等硬件资源。注:容器没有内核。默认情况下,容器没有资源限制,可以使用主机内核调度程序允许的尽可能多的给定资源;如果不对容器资源进行限制,容器之间就会相互影响,一些占用硬件资源较高的容器会吞噬掉所有的硬件资源,从而导致其它容器无硬件资源可用,发生停服状态。Docker 提供了限制内存,CPU 或磁盘 IO 的方法, 可以对容器所占用的硬件资源大小以及多少进行限制,我们在使用 docker create 创建一个容器或者 docker run 运行一个容器的时候就可以来对此容器的硬件资源做限制。


Docker 核心


Docker 分别使用 Namespace 和 CGroup 实现了对容器的资源隔离和资源限制,本文将会讲到怎么使用内核来调用 CGroup 对容器资源做限制。


OOM 介绍


out of memorty

OOM:out of memorty 的简称,称之为内存溢出


1.如果内存耗尽,内存将无法给予内存空间,内核检测到没有足够的内存来执行重要的系统功能,它会抛出 OOM 或 Out of Memory 异常,内存将会溢出,随之会有选择性的杀死相应的进程。2.内存属于不可压缩性资源,如果执行这个进程的内存不够使用,这个进程它就会一直申请内存资源,直到内存溢出。3.CPU 属于可压缩性资源,所以 CPU 并不会出现这种情况,例如一个进程占用了一个核心 100%的 CPU,那么原本这个进程是需要占用 200%的 CPU 资源,如果其它进程所占用的 CPU 核心并不需要多高的 CPU 频率,那么此进程就会占用掉空闲的 CPU,如果其它进程也需要他自己核心的 CPU 频率,那么此进程就只能使用对它自己所使用 CPU 核心 100%,因此叫做可压缩。4.内存的有选择性:为什么不是杀死占用内存最高的进程呢?举个例子:例如我们运行了一个 MySQL 和一个 Tomcat;这个 MySQL 原本是需要占用 2G 的内存资源,但他占用了 1.9G;而 Tomcat 原本是需要占用 500M 的内存空间,可他占用了 1G 内存空间,这个时候当内存报异常 OOM 的时候,就会选择 Tomcat 进程进行发送 kill -9 的信号,进行杀死以释放内存。5.当我们的一个重要的进程占用的内存超标而导致内存 OOM 的情况下,我们不希望内核来 Kill 掉我们这个进程,怎么办?我们可以调整内存 OOM 后 kill 进程的优先级,优先级越高越优先杀死,则反之 为此,Docker 特地调整了 docker daemon 的 OOM 优先级,以免它被内核的杀死,但容器的优先级并未被调整

导致内存 OOM

1.加载对象过大;2.相应资源过多,来不及加载;3.应用运行时间较长未重启,从而一点一点占用内存资源,长期积累导致占用内存空间较多;4.代码存在内存泄漏 bug。

解决 OOM 办法

1.内存引用上做一些处理,常用的有软引用;2.内存中加载图片直接在内存中做处理,(如边界压缩);3.动态回收内存;4.优化 Delivk 虚拟机的堆内存分配;5.自定义堆内存大小;6.定期重启应用以释放内存。

压测工具 stress


下载 stress









docker pull lorel/docker-stress-ng:latestlatest:Pullingfrom lorel/docker-stress-ngc52e3ed763ff:Pull complete a3ed95caeb02:Pull complete 7f831269c70e:Pull complete Digest: sha256:c8776b750869e274b340f8e8eb9a7d8fb2472edd5b25ff5b7d55728bca681322Status:Downloaded newer image for lorel/docker-stress-ng:latest
复制代码


使用方法








docker run --name stress -it --rm lorel/docker-stress-ng:latest stress --help--name 指定lorel/docker-stress-ng:latest所启动的测试得容器名称为stress--it:打开一个伪终端,并提供交互式--rm:容器停止即删除lorel/docker-stress-ng:latest:压测stress工具镜像名称stress:lorel/docker-stress-ng:latest镜像内所内置的命令,必须使用此命令来指定--help支持的选项
复制代码


stress 常用选项













--cpu N:启动几个子进程来做压测,默认一个进程使用一个CPU核心,选项可简写为-c Ndocker run --name stress -it --rm lorel/docker-stress-ng:latest stress --help | grep "cpu N"-c N,--cpu N start N workers spinning on sqrt(rand())


--vm N:启动几个进程来做匿名页压测,选项可简写为-m Ndocker run --name stress -it --rm lorel/docker-stress-ng:latest stress --help | grep "vm N"-m N,--vm N start N workers spinning on anonymous mmap


--vm-bytes N:为--vm N指定的进程提供内存分配,每个进程可以分配到的内存数量,默认为256Mdocker run --name stress -it --rm lorel/docker-stress-ng:latest stress --help | grep "vm-bytes N"--vm-bytes N allocate N bytes per vm worker (default256MB)


Docker 内存限制


限制内存注意事项

1.为需要限制容器内的应用提前做好压测,例如 Nginx 容器自身所占内存空间,以及预算业务量大小所需占用的内存空间,进行压测后,才能进入生产环境使用;2.保证宿主机内存资源充足,监控及时上报容器内的内存使用情况,一旦容器所占用的内存不足,立刻对容器内存限制做调整或者打包此容器为镜像到其它内存充足的机器上进行启动;3.如果物理内存充足,尽量不要使用 swap 交换内存,swap 会导致内存计算复杂。

设置内存选项

注意:可限制的内存单位:b、k、m、g;分别对应 bytes、KB、MB、GB


-m or --memory=:容器能使用的最大内存大小,最小值为 4M--memory-swap=:容器能够使用 swap 内存的大小,使用—memory-swap 选项必须要使用—memory 选项,否则—memory-swap 不生效--memory-swappiness:默认情况下,主机可以把容器使用的匿名页 swap 出来,你可以设置一个 0-100 之间的值,代表 swap 出来的比例,如果此值设置为 0,容器就会先使用物理内存,能不用就不用 swap 空间,如果设置为 100,则反之,能用 swap 就会用,哪怕有一丝可以用到 swap 空间的可能性就会使用 swap 空间--memory-reservation:预留的一个内存空间,设置一个内存使用 soft limit,如果 docker 发现主机内存不足,会执行 OOM 操作,这个值必须小于—memory 设置的值--kernel-memory:容器能够使用 kernel memory 大小,最小值为 4M--oom-kill-disable:是否运行 OOM 的时候杀死容器,只有设置了-m 或者-memory,才可以设置此值,值为 flase 或者 true,设置为 false 之后当此容器的内存溢出的时候就会对此容器执行 kill 操作,否则容器会耗尽主机内存,而且导致主机应用被杀死,如果这个容器运行的应用非常重要,就把—oom-kill-disable 设置为 true,就是禁止被 oom 杀掉

--memory-swap 详解: swap:交换内存 ram:物理内存


null


查看内存大小:


null


限制容器内存

使用 docker 的--memory 选项来限制容器能够使用物理内存的大小,使用 stress 命令的选项--vm 指定启动几个占用内存的进程和每个占用内存进程所占用的内存空间大小 我们指定了容器最多使用物理内存 512M,启动两个占用内存的进程,每个进程分别占用 512M 的空间,那么两个进程理论上需要占用 1024 的空间,我们只给了 1024 的空间,显然是不够的:





docker run --name stress-memory  -it --rm -m 512M  lorel/docker-stress-ng:latest stress --vm 2--vm-bytes 512Mstress-ng: info:[1] defaulting to a 86400 second run per stressorstress-ng: info:[1] dispatching hogs:2 vm
复制代码


使用 docker stats 命令来查看容器硬件资源的使用情况 可以看到我们的 stress-memory 容器的总内存为 512M,使用了 500 多点,但为超过 521M,内存占用的百分比为 99.3%:


null


使用 htop 命令来查看资源情况:


null


限制容器 swap 内存

设置 oom 时是否杀掉进程

Docker CPU 限制


查看 CPU 核心数以及编码:


null


设置 CPU 选项

--cpu-shares:共享式 CPU 资源,是按比例切分 CPU 资源;比如当前系统上一共运行了两个容器,第一个容器上权重是 1024,第二个容器权重是 512, 第二个容器启动之后没有运行任何进程,自己身上的 512 都没有用完,而第一台容器的进程有很多,这个时候它完全可以占用容器二的 CPU 空闲资源,这就是共享式 CPU 资源;如果容器二也跑了进程,那么就会把自己的 512 给要回来,按照正常权重 1024:512 划分,为自己的进程提供 CPU 资源。如果容器二不用 CPU 资源,那容器一就能够给容器二的 CPU 资源所占用,如果容器二也需要 CPU 资源,那么就按照比例划分,这就是 CPU 共享式,也证明了 CPU 为可压缩性资源。--cpus:限制容器运行的核数;从 docker1.13 版本之后,docker 提供了--cpus 参数可以限定容器能使用的 CPU 核数。这个功能可以让我们更精确地设置容器 CPU 使用量,是一种更容易理解也常用的手段。-cpuset-cpus:限制容器运行在指定的 CPU 核心;运行容器运行在哪个 CPU 核心上,例如主机有 4 个 CPU 核心,CPU 核心标识为 0-3,我启动一台容器,只想让这台容器运行在标识 0 和 3 的两个 CPU 核心上,可以使用 cpuset 来指定。

限制 CPU Share

启动 stress 压测工具,并使用 stress 命令加--cpu 选项来启动四个进程,默认一个进程占用一颗 CPU 核心:





docker run --name stress-share  -it --rm --cpu-shares 512  lorel/docker-stress-ng:latest stress -c 2stress-ng: info:[1] defaulting to a 86400 second run per stressorstress-ng: info:[1] dispatching hogs:2 cpu
复制代码


压测工具会吃掉俩个核心的所有 CPU 资源 再次打开一个窗口,使用 htop 命令来查看硬件资源损耗情况 我们一共开了两个进程,默认会占用两个 cpu 核心,每个核心的资源为 100%,两个也就是为 200%,由于没有指定限制在某个 CPU 核心上,所以它是动态的跑在四核 CPU 核心数,但是 stress 占用 CPU 的资源不会超出 200%:


null


再次打开一个窗口,使用 docker top container 也可以查看到两个进程一共消耗了多少 cpu 资源:


null


限制 CPU 核数

我们使用 docker 的--cpus 选项来限制 cpu 运行的核心数,使用 stress 命令的选项--cpu 来限制启动的进程数 显示 cpu 只运行两个核心数,也就是只能运行 200%CPU 资源,启动 4 个进程,也就是讲这 4 个进程只能跑在 200%的 cpu 资源上:





docker run --name stress-cpus  -it --rm --cpus 2  lorel/docker-stress-ng:latest stress -c 4stress-ng: info:[1] defaulting to a 86400 second run per stressorstress-ng: info:[1] dispatching hogs:4 cpu
复制代码


使用 htop 命令查看 cpu 占用资源 可以看到四个进程,但是跑在了四颗 cpu 上面,但是每颗 cpu 只运行了 50%左右的资源,4*50 也就是 200%左右的 cpu 资源:


null


使用 docker top container 也可以查看到四个进程一共消耗了多少 cpu 资源:


null


限制容器运行在指定核心

我们使用 docker 的--cpuset-cpus 选项来指定 cpu 运行在哪个核心上,使用 stress 的选项--cpu 来指定启动的进程数 我们指定 4 个进程运行在编号为 0 和 2 的 cpu 上:





docker run --name stress-cpuset  -it --rm --cpuset-cpus=0,2  lorel/docker-stress-ng:latest stress -c 4stress-ng: info:[1] defaulting to a 86400 second run per stressorstress-ng: info:[1] dispatching hogs:4 cpu
复制代码


使用 htop 查看系统硬件占用资源 可以看到和预期一样,占用了第一颗和第三颗 cpu,每个进程占用 cpu 资源为 50%,总资源为 200%,两颗 cpu 的量:


null


作者:Wayne

来源:https://zhuanlan.zhihu.com/p/162699218


用户头像

还未添加个人签名 2020.09.07 加入

还未添加个人简介

评论

发布
暂无评论
一次搞明白 Docker 容器资源限制