docker(十五)资源限制

docker资源限制

官网:https://docs.docker.com/config/containers/resource_constraints/

默认情况下, 容器没有资源限制, 可以使用主机内核调度程序允许的尽可能多的给定资源, Docker 提供了控制容器可以限制容器使用多少内存或 CPU 的方法,设置 docker run 命令的运行时配置标志。
其中许多功能都要求宿主机的内核支持 Linux 功能, 要检查支持, 可以使用docker info 命令,如果内核中禁用了某项功能, 可能会在输出结尾处看到警告,

如下所示:

WARNING: No swap limit support

对于 Linux 主机, 如果没有足够的内容来执行其他重要的系统任务, 将会抛出OOM (Out of Memory Exception,内存溢出、 内存泄漏、 内存异常), 随后系统会开始杀死进程以释放内存, 凡是运行在宿主机的进程都有可能被 kill, 包括 Dockerd和其它的应用程序, 如果重要的系统进程被 Kill,会导致和该进程相关的服务全部宕机。

产生 OOM 异常时, Dockerd 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死,但是容器的 OOM优先级未调整, 这使得单个容器被杀死的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动设置–oomscore-adj 为极端负数,或通过在容器上设置–oom-kill-disable 来绕过这些安全措施。

OOM 优先级机制:

linux 会为每个进程算一个分数,最终他会将分数最高的进程 kill。

/proc/PID/oom_score_adj :范围为-1000 到 1000,值越高越容易被宿主机 kill掉,如果将该值设置为-1000,则进程永远不会被宿主机 kernel kill。

/proc/PID/oom_adj :范围为-17 到+15,取值越高越容易被干掉,如果是-17,则表示不能被 kill,该设置参数的存在是为了和旧版本的 Linux 内核兼容。

/proc/PID/oom_score :这个值是系统综合进程的内存消耗量、 CPU 时间(utime + stime)、存活时间(uptime - start time)和 oom_adj 计算出的,消耗内存越多分越高,存活时间越长分越低。

容器的内存限制:

Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。
Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了。

–oom-score-adj: 宿主机 kernel 对进程使用的内存进行评分, 评分最高的将被宿主机内核 kill 掉, 可以指定一个容器的评分制但是不推荐手动指定。
–oom-kill-disable: 对某个容器关闭 oom 机制。


内存限制参数:

-m, –memory:容器可以使用的最大内存量,如果设置此选项,则允许的最内存值为 4m (4 兆字节)。
–memory-swap: 容器可以使用的交换分区大小, 必须要在设置了物理内存限制的前提才能设置交换分区的限制
–memory-swappiness: 设置容器使用交换分区的倾向性,值越高表示越倾向于使用 swap 分区,范围为 0-100, 0 为能不用就不用, 100 为能用就用。
–kernel-memory: 容器可以使用的最大内核内存量,最小为 4m,由于内核内存与用户空间内存隔离,因此无法与用户空间内存直接交换,因此内核内存不足的容器可能会阻塞宿主主机资源,这会对主机和其他容器或者其他服务进程产生影响,因此不要设置内核内存大小。
–memory-reservation:允许指定小于–memory 的软限制,当 Docker 检测到主机上的争用或内存不足时会激活该限制,如果使用–memory-reservation,则必须将其设置为低于–memory 才能使其优先。 因为它是软限制,所以不能保证容器不超过限制。
–oom-kill-disable 默认情况下,发生 OOM 时, kernel 会杀死容器内进程,但是可以使用–oom-kill-disable 参数,可以禁止 oom 发生在指定的容器上,即 仅在已设置-m选项的容器上禁用 OOM,如果-m 参数未配置,产生 OOM 时,主机为了释放内存还会杀死系统进程。


swap 限制:

–memory-swap:只有在设置了 --memory 后才会有意义。使用 Swap,可以让容器将超出限制部分的内存置换到磁盘上, WARNING:经常将内存交换到磁盘的应用程序会降低性能。

不同 –memory-swap 的设置会产生不同的效果:
值为正数, 那么–memory 和–memory-swap 都必须要设置, --memory-swap 表示你能使用的内存和 swap 分区大小的总和,例如: --memory=300m, --memory-swap=1g, 那么该容器能够使用 300m 内存和 700m swap,即--memory 是实际物理内存大小值不变,而 swap 的实际大小计算方式为
( m e m o r y s w a p ) ( m e m o r y ) = s w a p (--memory-swap)-(--memory)=容器可用 swap

如果设置为 0:则忽略该设置,并将该值视为未设置,即未设置交换分区。

如果等于–memory 的值,并且–memory 设置为正整数: 容器无权访问 swap 即也没有设置交换分区。

如果设置为 unset:如果宿主机开启了 swap,则实际容器的swap 值为 2x( --memory),即两倍于物理内存大小,但是并不准确(在容器中使用free 命令所看到的 swap 空间并不精确, 毕竟每个容器都可以看到具体大小,但是宿主机的 swap 是有上限而且不是所有容器看到的累计大小)。
如果设置为-1:如果宿主机开启了 swap,则容器可以使用主机上 swap 的最大空间。

动态修改容器内存,先计算出所需要的内存的字节数:如268435456(256M),只能调大不能调小

echo "268435456" > /sys/fs/cgroup/memory/docker/9fa20d824b18.../memory.limit_in_bytes

9fa20d824b18… 是容器ID

内存限制验证:

假如一个容器未做内存使用限制, 则该容器可以利用到系统内存最大空间, 默认创建的容器没有做内存资源限制。

#测试镜像
docker pull lorel/docker-stress-ng 

查看帮助

apt install stress-ng
stress-ng --help 

或者这样也行

docker run -it --rm lorel/docker-stress-ng -help

内存大小硬限制

查看docker状态

docker stats
  1. 启动两个工作进程,每个工作进程最大允许使用内存 256M,且宿主机不限制当前容器最大内存:
docker run -it --rm --name c1 lorel/docker-stress-ng \
      --vm 2 --vm-bytes 256M

–vm 启动多少个进程
–vm-bytes 为每个进程分配的内存

  1. 宿主机限制容器最大内存使用:
docker run -it --rm -m 256m --name c2 lorel/docker-stress-ng \
      --vm 2 --vm-bytes 256M
  1. 宿主机 cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器 ID /memory.limit_in_bytes
268435456

宿主机基于 cgroup 对容器进行内存资源的大小限制
注:通过 echo 命令可以改内存限制的值,但是可以在原基础之上增大内存限制,缩小内存限制会报错 write error: Device or resource busy

内存大小软限制:

docker run -it --rm -m 256m --memory-reservation 128m \
      --name c3 lorel/docker-stress-ng --vm 2 --vm-bytes 256M
  1. 宿主机 cgroup 验证:
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.soft_limit_in_bytes
134217728 

返回的软限制结果

关闭 OOM 机制

docker run -it --rm -m 256m --oom-kill-disable \
      --name c4 lorel/dockerstress-ng --vm 2 --vm-bytes 256M
# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.oom_control
oom_kill_disable 1
under_oom 1
oom_kill 0

交换分区限制

docker run -it --rm -m 256m --memory-swap 512m \
      --name magedu-c1 centos bash

宿主机 cgroup 验证:

# cat /sys/fs/cgroup/memory/docker/容器 ID/memory.memsw.limit_in_bytes
536870912 

容器的 CPU 限制

一个宿主机,有几十个核心的 CPU, 但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务, 多进程共用一个 CPU 的核心依赖计数就是为可压缩资源, 即一个核心的 CPU 可以通过调度而运行多个进程, 但是同一个单位时间内只能有一个进程在 CPU 上运行, 那么这么多的进程怎么在 CPU 上执行和调度的呢?

实时优先级: 0 - 99

非实时优先级(nice): -20 - 19, 对应 100 - 139 的进程优先级

Linux kernel 进程的调度基于 CFS(Completely Fair Scheduler), 完全公平调度

CPU 密集型的场景:优先级越低越好, 计算密集型任务的特点是要进行大量的计算,消耗 CPU 资源,比如计算圆周率、 数据处理、 对视频进行高清解码等等,全靠 CPU 的运算能力。

IO 密集型的场景:优先级值高点, 涉及到网络、磁盘 IO 的任务都是 IO 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度),比如 Web 应用, 高并发,数据量大的动态网站来说,数据库应该为 IO 密集型。

磁盘的调度算法

# cat /sys/block/sda/queue/scheduler
noop deadline [cfq]

默认情况下,每个容器对主机 CPU 周期的访问权限是不受限制的, 但是我们可以设置各种约束来限制给定容器访问主机的 CPU 周期,大多数用户使用的是默认的 CFS 调度方式, 在 Docker 1.13 及更高版本中, 还可以配置实时优先级。

参数:

–cpus :指定容器可以使用多少可用 CPU 资源, 例如,如果主机有两个 CPU,并且设置了–cpus =“1.5”,那么该容器将保证最多可以访问 1.5 个的 CPU(如果是 4 核 CPU, 那么还可以是 4 核心上每核用一点,但是总计是 1.5 核心的CPU), 这相当于设置–cpu-period =“100000”和–cpu-quota =“150000”
主要在 Docker 1.13 和更高版本中使用, 目的是替代--cpu-period--cpuquota 两个参数,从而使配置更简单, 最大不能超出宿主机的 CPU 总核心数(在操作系统看到的 CPU 超线程后的数值)。

分配给容器的 CPU 超出了宿主机 CPU 总数。

# docker run -it --rm --cpus 2 centos bash
docker: Error response from daemon: Range of CPUs is from 0.01 to 1.00, as there are only 1 CPUs available.
See 'docker run --help'. 

–cpu-period:(CPU 调度周期)设置 CPU 的 CFS 调度程序周期,必须与--cpuquota 一起使用,默认周期为 100 微秒
–cpu-quota: 在容器上添加 CPU CFS 配额, 计算方式为 cpu-quota/cpu-period的结果值, 早期的 docker(1.12 及之前)使用此方式设置对容器的 CPU 限制值,新版本 docker(1.13 及以上版本)通常使用–cpus 设置此值。

–cpuset-cpus:用于指定容器运行的 CPU 编号,也就是我们所谓的绑核。
–cpuset-mem:设置使用哪个 cpu 的内存,仅对 非统一内存访问(NUMA)架构有效。
–cpu-shares:用于设置 cfs 中调度的相对最大比例权重,cpu-share 的值越高的容器,将会分得更多的时间片(宿主机多核 CPU 总数为 100%, 假如容器 A 为1024, 容器 B 为 2048, 那么容器 B 将最大是容器 A 的可用 CPU 的两倍 ),默认的时间片 1024,最大 262144。

测试 CPU 限制

  1. 未限制容器 CPU
    对于一台四核的服务器,如果不做限制, 容器会把宿主机的 CPU 全部占完。
    分配 4 核 CPU 并启动 4 个工作线程
docker run -it --rm --name c10 lorel/docker-stress-ng \
      --cpu 4 --vm 4

在宿主机使用 dokcer top 命令查看容器运行状态

docker top CONTAINER [ps OPTIONS]

容器运行状态:

docker stats

在宿主机查看 CPU 限制参数:

# cat /sys/fs/cgroup/cpuset/docker/${容器ID}/cpuset.cpus
0-3
  1. 限制容器 CPU
    只给容器分配最多两核宿主机 CPU 利用率
docker run -it --rm --name c11 \
      --cpus 2 lorel/docker-stress-ng --cpu 4 --vm 4

宿主机 cgroup 验证

# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.cfs_quota_us
200000

每核心 CPU 会按照 1000 为单位转换成百分比进行资源划分, 2 个核心的 CPU 就是 200000/1000=200%, 4 个核心 400000/1000=400%,以此类推

宿主机 CPU 利用率

  1. 将容器运行到指定的 CPU 上
docker run -it --rm --name c12 --cpus 1 \
      --cpuset-cpus 1,3 lorel/docker-stress-ng --cpu 2 --vm 2
# cat /sys/fs/cgroup/cpuset/docker/容器 ID /cpuset.cpus
1,3

容器运行状态

docker stats
  1. 基于 cpu—shares 对 CPU 进行切分
    启动两个容器, c13 的--cpu-shares 值为 1000, c14 的--cpu-shares为 500, 观察最终效果, --cpu-shares 值为 1000 的 c13 的 CPU 利用率基本是--cpu-shares 为 500 的 c14 的两倍:
docker run -it --rm --name c13 --cpu-shares 10 \
      lorel/docker-stress-ng --cpu 1 --vm 2

docker run -it --rm --name c14 --cpu-shares 5 \
      lorel/docker-stress-ng --cpu 1 --vm 2

验证容器运行状态

docker stats

宿主机 cgroup 验证

# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares
1000
# cat /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares
500
  1. 动态修改 CPU shares 值:
    --cpu-shares 的值可以在宿主机 cgroup 动态修改, 修改完成后立即生效,其值可以调大也可以减小。
echo 2000 > /sys/fs/cgroup/cpu,cpuacct/docker/容器 ID/cpu.shares

验证修改后的容器运行状态

docker stats
发布了254 篇原创文章 · 获赞 346 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_42758707/article/details/100996513