Docker(二十)-Docker容器CPU、memory资源限制

更新时间:2023-06-26 02:43:32 阅读: 评论:0

Docker(⼆⼗)-Docker容器CPU、memory资源限制
背景
在使⽤ docker 运⾏容器时,默认的情况下,docker没有对容器进⾏硬件资源的限制,当⼀台主机上运⾏⼏百个容器,这些容器虽然互相隔离,但是底层却使⽤着相同的 CPU、内存和磁盘资源。如果不对容器使⽤的资源进⾏限制,那么容器之间会互相影响,⼩的来说会导致容器资源使⽤不公平;⼤的来说,可能会导致主机和集群资源耗尽,服务完全不可⽤。
docker 作为容器的管理者,⾃然提供了控制容器资源的功能。正如使⽤内核的 namespace 来做容器之间的隔离,docker 也是通过内核的cgroups 来做容器的资源限制;包括CPU、内存、磁盘三⼤⽅⾯,基本覆盖了常见的资源配额和使⽤量控制。
Docker内存控制OOME在linxu系统上,如果内核探测到当前宿主机已经没有可⽤内存使⽤,那么会抛出⼀个OOME(Out Of Memory Exception:内存异常 ),并且会开启killing去杀掉⼀些进程。
⼀旦发⽣OOME,任何进程都有可能被杀死,包括docker daemon在内,为此,docker特地调整了docker daemon的OOM_Odj优先级,以免他被杀掉,但容器的优先级并未被调整。经过系统内部复制的计算后,每个系统进程都会有⼀个OOM_Score得分,OOM_Odj越⾼,得分越⾼,(在docker run的时
候可以调整OOM_Odj)得分最⾼的优先被kill掉,当然,也可以指定⼀些特定的重要的容器禁⽌被OMM杀掉,在启动容器时使⽤ –oom-kill-disable=true指定。
参考:
cgroup简介
cgroup是Control Groups的缩写,是Linux 内核提供的⼀种可以限制、记录、隔离进程组所使⽤的物理资源(如 cpu、memory、磁盘IO等等)的机制,被LXC、docker等很多项⽬⽤于实现进程资源控制。cgroup将任意进程进⾏分组化管理的 Linux 内核功能。cgroup本⾝是提供将进程进⾏分组化管理的功能和接⼝的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为cgroup⼦系统,有以下⼏⼤⼦系统实现:
1. blkio:设置限制每个块设备的输⼊输出控制。例如:磁盘,光盘以及usb等等。
2. cpu:使⽤调度程序为cgroup任务提供cpu的访问。
3. cpuacct:产⽣cgroup任务的cpu资源报告。
4. cput:如果是多核⼼的cpu,这个⼦系统会为cgroup任务分配单独的cpu和内存。
5. devices:允许或拒绝cgroup任务对设备的访问。
6. freezer:暂停和恢复cgroup任务。
7. memory:设置每个cgroup的内存限制以及产⽣内存资源报告。
8. net_cls:标记每个⽹络包以供cgroup⽅便使⽤。
9. ns:命名空间⼦系统。
10. perf_event:增加了对每group的监测跟踪的能⼒,即可以监测属于某个特定的group的所有线程以及运⾏在特定CPU上的线程。
⽬前docker只是⽤了其中⼀部分⼦系统,实现对资源配额和使⽤的控制。
可以使⽤stress⼯具来测试CPU和内存。使⽤下⾯的Dockerfile来创建⼀个基于Ubuntu的stress⼯具镜像。
FROM ubuntu:14.04
RUN apt-get update &&apt-get install stress
资源监控的关键⽬录:cat读出
已使⽤内存:
/sys/fs/cgroup/memory/docker/应⽤ID/memory.usage_in_bytes
分配的总内存:
/sys/fs/cgroup/memory/docker/应⽤ID/memory.limit_in_bytes
已使⽤的cpu:单位纳秒
/sys/fs/cgroup/cpuacct/docker/应⽤ID/cpuacct.usage
系统当前cpu:
$ cat /proc/stat | grep 'cpu '(周期/时间⽚/jiffies)
#得到的数字相加/HZ(cat /boot/config-`uname -r` | grep '^CONFIG_HZ='
ubuntu 14.04为250)就是系统时间(秒)
#再乘以10*9就是系统时间(纳秒)
例⼦
[~]$ cat /proc/stat
cpu 432661 13295 86656 422145968 171474 233 5346
cpu0 123075 2462 23494 105543694 16586 0 4615
rider universitycpu1 111917 4124 23858 105503820 69697 123 371
cpu2 103164 3554 21530 105521167 64032 106 334
cpu3 94504 3153 17772 105577285 21158 4 24
intr 1065711094 1057275779 92 0 6 6 0 4 0 3527 0 0 0 70 0 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 19067887
btime 1139187531
process 270014
procs_running 1
procs_blocked 0
输出解释
CPU 以及CPU0、CPU1、CPU2、CPU3每⾏的每个参数意思(以第⼀⾏为例)为:
参数解释
ur (432661) 从系统启动开始累计到当前时刻,⽤户态的CPU时间(单位:jiffies),不包含 nice值为负进程。
nice (13295) 从系统启动开始累计到当前时刻,nice值为负的进程所占⽤的CPU时间(单位:jiffies)
system (86656) 从系统启动开始累计到当前时刻,核⼼时间(单位:jiffies)
idle (422145968) 从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它等待时间(单位:jiffies)
iowait (171474) 从系统启动开始累计到当前时刻,硬盘IO等待时间(单位:jiffies),
irq (233) 从系统启动开始累计到当前时刻,硬中断时间(单位:jiffies)
softirq (5346) 从系统启动开始累计到当前时刻,软中断时间(单位:jiffies)
cpu使⽤率:
(已使⽤2-已使⽤1)/(系统当前2-系统当前1)*100%
内存限制
Docker 提供的内存限制功能有以下⼏点:
容器能使⽤的内存和交换分区⼤⼩。
容器的核⼼内存⼤⼩。impress
容器虚拟内存的交换⾏为。
容器内存的软性限制。
是否杀死占⽤过多内存的容器。
容器被杀死的优先级
⼀般情况下,达到内存限制的容器过段时间后就会被系统杀死。
内存限制相关的参数
执⾏docker run命令时能使⽤的和内存限制相关的所有选项如下。
选项描述
-m,--memory内存限制,格式是数字加单位,单位可以为 b,k,m,g。最⼩为 4M
--memory-swap内存+交换分区⼤⼩总限制。格式同上。必须必-m设置的⼤
--memory-rervation内存的软性限制。格式同上
--oom-kill-disable是否阻⽌ OOM killer 杀死容器,默认没设置
--oom-score-adj容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
-
-memory-swappiness⽤于设置容器的虚拟内存控制⾏为。值为 0~100 之间的整数
--kernel-memory核⼼内存限制。格式同上,最⼩为 4M
⽤户内存限制
⽤户内存限制就是对容器能使⽤的内存和交换分区的⼤⼩作出限制。使⽤时要遵循两条直观的规则:-m,--memory选项的参数最⼩为 4 M。--memory-swap不是交换分区,⽽是内存加交换分区的总⼤⼩,所以--memory-swap必须⽐-m,--memory⼤。在这两条规则下,⼀般有四种设置⽅式。
你可能在进⾏内存限制的实验时发现docker run命令报错:WARNING: Your kernel does not support swap limit capabilities,
memory limited without swap.
这是因为宿主机内核的相关功能没有打开。按照下⾯的设置就⾏。
step 1:编辑/etc/default/grub⽂件,将GRUB_CMDLINE_LINUX⼀⾏改为GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
step 2:更新 GRUB,即执⾏$ sudo update-grub
step 3: 重启系统。
1. 不设置
如果不设置-m,--memory和--memory-swap,容器默认可以⽤完宿舍机的所有内存和 swap 分区。不过注意,如果容器占⽤宿主机的所有内存和swap 分区超过⼀段时间后,会被宿主机系统杀死(如果没有设置--00m-kill-disable=true的话)。
2. 设置-m,--memory,不设置--memory-swap
给-m或--memory设置⼀个不⼩于 4M 的值,假设为 a,不设置--memory-swap,或将--memory-swap设置为 0。这种情况下,容器能使⽤的内存⼤⼩为 a,能使⽤的交换分区⼤⼩也为 a。因为 Docker 默认容器交换分区的⼤⼩和内存相同。
如果在容器中运⾏⼀个⼀直不停申请内存的程序,你会观察到该程序最终能占⽤的内存⼤⼩为 2a。
⽐如$ docker run -m 1G ubuntu:16.04,该容器能使⽤的内存⼤⼩为 1G,能使⽤的 swap 分区⼤⼩也为 1G。容器内的进程能申请到的总内存⼤⼩为 2G。
3. 设置-m,--memory=a,--memory-swap=b,且b > a
给-m设置⼀个参数 a,给--memory-swap设置⼀个参数 b。a 时容器能使⽤的内存⼤⼩,b是容器能使⽤的内存⼤⼩ + swap 分区⼤⼩。所以 b 必须⼤于 a。b -a 即为容器能使⽤的 swap 分区⼤⼩。
⽐如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使⽤的内存⼤⼩为 1G,能使⽤的 swap 分区⼤⼩为 2G。容器内的进程能申请到的总内存⼤⼩为 3G。
4. 设置-m,--memory=a,--memory-swap=-1
给-m参数设置⼀个正常值,⽽给--memory-swap设置成 -1。这种情况表⽰限制容器能使⽤的内存⼤⼩为 a,⽽不限制容器能使⽤的 swap 分区⼤⼩。
埃及艳后 泰勒这时候,容器内进程能申请到的内存⼤⼩为 a + 宿主机的 swap ⼤⼩。
Memory rervation
这种 memory rervation 机制不知道怎么翻译⽐较形象。Memory rervation 是⼀种软性限制,⽤于节制容器内存使⽤。给--memory-rervation设置⼀个⽐-m⼩的值后,虽然容器最多可以使⽤-m使⽤的内存⼤⼩,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统
会回收容器的部分内存页,强迫容器的内存占⽤回到--memory-rervation设置的值⼤⼩。
没有设置时(默认情况下)--memory-rervation的值和-m的限定的值相同。将它设置为 0 会设置的⽐-m的参数⼤等同于没有设置。iken
Memory rervation 是⼀种软性机制,它不保证任何时刻容器使⽤的内存不会超过--memory-rervation限定的值,它只是确保容器不会长时间占⽤超过--memory-rervation限制的内存⼤⼩。
例如:
$ docker run -it -m 500M --memory-rervation 200M ubuntu:16.04 /bin/bash
如果容器使⽤了⼤于 200M 但⼩于 500M 内存时,下次系统的内存回收会尝试将容器的内存锁紧到 200M 以下。
例如:
$ docker run -it --memory-rervation 1G ubuntu:16.04 /bin/bash
容器可以使⽤尽可能多的内存。--memory-rervation确保容器不会长时间占⽤太多内存。
OOM killer
华尔街英语怎么样
默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。这个杀死进程来节省内存的进程,我们姑且叫它 OOM killer。我们可以通过设置--oom-kill-disable选项来禁⽌ OOM killer 杀死容器内进程。但请确保只有在使⽤了-m/--memory选项时才使⽤--oom-kill-disable禁⽤ OOM killer。如果没有设置-m选项,却禁⽤了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。
下⾯的例⼦限制了容器的内存为 100M 并禁⽌了 OOM killer:
$ docker run -it -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash
是正确的使⽤⽅法。
minister
⽽下⾯这个容器没设置内存限制,却禁⽤了 OOM killer 是⾮常危险的:
$ docker run -it --oom-kill-disable ubuntu:16.04 /bin/bash
容器没⽤内存限制,可能或导致系统⽆内存可⽤,并尝试时杀死系统进程来获取更多可⽤内存。
⼀般⼀个容器只有⼀个进程,这个唯⼀进程被杀死,容器也就被杀死了。我们可以通过--oom-score-adj选项来设置在系统内存不够时,容器被杀死的优先级。负值更教不可能被杀死,⽽正值更有可能被杀死。
核⼼内存
核⼼内存和⽤户内存不同的地⽅在于核⼼内存不能被交换出。不能交换出去的特性使得容器可以通过消耗太多内存来堵塞⼀些系统服务。核
⼼内存包括:
stack pages(栈页⾯)
slab pages
同义词socket memory pressure
做人的原则
tcp memory pressure
可以通过设置核⼼内存限制来约束这些内存。例如,每个进程都要消耗⼀些栈页⾯,通过限制核⼼内存,可以在核⼼内存使⽤过多时阻⽌新进程被创建。
核⼼内存和⽤户内存并不是独⽴的,必须在⽤户内存限制的上下⽂中限制核⼼内存。
假设⽤户内存的限制值为 U,核⼼内存的限制值为 K。有三种可能地限制核⼼内存的⽅式:
1. U != 0,不限制核⼼内存。这是默认的标准设置⽅式
2. K < U,核⼼内存时⽤户内存的⼦集。这种设置在部署时,每个 cgroup 的内存总量被过度使⽤。过度使⽤核⼼内存限制是绝不推荐
的,因为系统还是会⽤完不能回收的内存。在这种情况下,你可以设置 K,这样 groups 的总数就不会超过总内存了。然后,根据系统服务的质量⾃有地设置 U。
3. K > U,因为核⼼内存的变化也会导致⽤户计数器的变化,容器核⼼内存和⽤户内存都会触发回收⾏为。这种配置可以让管理员以⼀种
统⼀的视图看待内存。对想跟踪核⼼内存使⽤情况的⽤户也是有⽤的。
例如:
$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash
容器中的进程最多能使⽤ 500M 内存,在这 500M 中,最多只有 50M 核⼼内存。
$ docker run -it --kernel-memory 50M ubuntu:16.04 /bin/bash
没⽤设置⽤户内存限制,所以容器中的进程可以使⽤尽可能多的内存,但是最多能使⽤ 50M 核⼼内存。
Swappiness
默认情况下,容器的内核可以交换出⼀定⽐例的匿名页。--memory-swappiness就是⽤来设置这个⽐例的。--memory-swappiness可以设置为从 0 到100。0 表⽰关闭匿名页⾯交换。100 表⽰所有的匿名页都可以交换。默认情况下,如果不适⽤--memory-swappiness,则该值从⽗进程继承⽽来。
例如:
$ docker run -it --memory-swappiness=0 ubuntu:16.04 /bin/bash
将--memory-swappiness设置为 0 可以保持容器的⼯作集,避免交换代理的性能损失。
$ docker run -tid —name mem1 —memory 128m ubuntu:16.04 /bin/bash
$ cat /sys/fs/cgroup/memory/docker/<;容器的完整ID>/memory.limit_in_bytes
$ cat /sys/fs/cgroup/memory/docker/<;容器的完整ID>/sw.limit_in_bytes
CPU 限制
概述media center
Docker 的资源限制和隔离完全基于 Linux cgroups。对 CPU 资源的限制⽅式也和 cgroups 相同。Docker 提供的 CPU 资源限制选项可以在多核系统上限制容器能利⽤哪些 vCPU。⽽对容器最多能使⽤的 CPU 时间有两种限制⽅式:⼀是有多个 CPU 密集型的容器竞争 CPU 时,设置各个容器能使⽤的 CPU 时间相对⽐例。⼆是以绝对的⽅式设置容器在每个调度周期内最多能使⽤的 CPU 时间。
CPU 限制相关参数
docker run命令和 CPU 限制相关的所有选项如下:
选项描述老师的英文
--cput-cpus=""允许使⽤的 CPU 集,值可以为 0-3,0,1
-c,--cpu-shares=0CPU 共享权值(相对权重)
cpu-period=0限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000]
-
-cpu-quota=0限制 CPU CFS 配额,必须不⼩于1ms,即 >= 1000
--cput-mems=""允许在上执⾏的内存节点(MEMs),只对 NUMA 系统有效
其中--cput-cpus⽤于设置容器可以使⽤的 vCPU 核。-c,--cpu-shares⽤于设置多个容器竞争 CPU 时,各个容器相对能分配到的 CPU 时间⽐例。--cpu-period和--cpu-quata⽤于绝对设置容器能使⽤ CPU 时间。
--cput-mems暂⽤不上,这⾥不谈。
CPU 集
我们可以设置容器可以在哪些 CPU 核上运⾏。
例如:
$ docker run -it --cput-cpus="1,3" ubuntu:14.04 /bin/bash
表⽰容器中的进程可以在 cpu 1 和 cpu 3 上执⾏。
$ docker run -it --cput-cpus="0-2" ubuntu:14.04 /bin/bash
$ cat /sys/fs/cgroup/cput/docker/<;容器的完整长ID>/cput.cpus
表⽰容器中的进程可以在 cpu 0、cpu 1 及 cpu 3 上执⾏。
在 NUMA 系统上,我们可以设置容器可以使⽤的内存节点。
例如:
$ docker run -it --cput-mems="1,3" ubuntu:14.04 /bin/bash
表⽰容器中的进程只能使⽤内存节点 1 和 3 上的内存。
$ docker run -it --cput-mems="0-2" ubuntu:14.04 /bin/bash
表⽰容器中的进程只能使⽤内存节点 0、1、2 上的内存。
CPU 资源的相对限制
默认情况下,所有的容器得到同等⽐例的 CPU 周期。在有多个容器竞争 CPU 时我们可以设置每个容器能使⽤的 CPU 时间⽐例。这个⽐例叫作共享权值,通过-c或--cpu-shares设置。Docker 默认每个容器的权值为 1024。不设置或将其设置为 0,都将使⽤这个默认值。系统会根据每个容器的共享权值
和所有容器共享权值和⽐例来给容器分配 CPU 时间。
假设有三个正在运⾏的容器,这三个容器中的任务都是 CPU 密集型的。第⼀个容器的 cpu 共享权值是 1024,其它两个容器的 cpu 共享权值是 512。第⼀个容器将得到 50% 的 CPU 时间,⽽其它两个容器就只能各得到 25% 的 CPU 时间了。如果再添加第四个 cpu 共享值为1024 的容器,每个容器得到的 CPU 时间将重新计算。第⼀个容器的CPU 时间变为 33%,其它容器分得的 CPU 时间分别为 16.5%、16.5%、33%。
必须注意的是,这个⽐例只有在 CPU 密集型的任务执⾏时才有⽤。在四核的系统上,假设有四个单进程的容器,它们都能各⾃使⽤⼀个核的 100% CPU 时间,不管它们的 cpu 共享权值是多少。
在多核系统上,CPU 时间权值是在所有 CPU 核上计算的。即使某个容器的 CPU 时间限制少于 100%,它也能使⽤各个 CPU 核的 100%时间。
例如,假设有⼀个不⽌三核的系统。⽤-c=512的选项启动容器{C0},并且该容器只有⼀个进程,⽤-c=1024的启动选项为启动容器C2,并且该容器有两个进程。CPU 权值的分布可能是这样的:
PID    container    CPU CPU share
100    {C0}    0  100% of CPU0
101    {C1}    1  100% of CPU1
102    {C1}    2  100% of CPU2
$ docker run -it --cpu-shares=100 ubuntu:14.04 /bin/bash
$ cat /sys/fs/cgroup/cpu/docker/<;容器的完整长ID>/cpu.shares
表⽰容器中的进程CPU份额值为100。
CPU 资源的绝对限制
Linux 通过 CFS(Completely Fair Scheduler,完全公平调度器)来调度各个进程对 CPU 的使⽤。CFS 默认的调度周期是 100ms。
关于 CFS 的更多信息,参考。
我们可以设置每个容器进程的调度周期,以及在这个周期内各个容器最多能使⽤多少 CPU 时间。使⽤--cpu-period即可设置调度周期,使⽤--cpu-quota即可设置在每个周期内容器能使⽤的 CPU 时间。两者⼀般配合使⽤。
例如:
$ docker run -it --cpu-period=50000 --cpu-quota=25000 ubuntu:16.04 /bin/bash
将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000,表⽰该容器每 50ms 可以得到 50% 的 CPU 运⾏时间。
$ docker run -it --cpu-period=10000 --cpu-quota=20000 ubuntu:16.04 /bin/bash
$ cat /sys/fs/cgroup/cpu/docker/<;容器的完整长ID>/cpu.cfs_period_us

本文发布于:2023-06-26 02:43:32,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/78/1040622.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:内存   容器   限制   进程   系统
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图