Docker 的资源限制
官方文档:https://docs.docker.com/engine/containers/resource_constraints/
默认情况下,容器没有资源的使用限制,可以使用主机内核调度程序允许的尽可能多的资源,Docker 提供了控制容器使用资源的方法,可以限制容器使用多少内存或 CPU等, 在 docker run 命令运行时配置标志实现资源限制功能。其中许多功能都要求宿主机的内核支持,要检查是否支持这些功能,可以使用 docker info 命令 ,如果内核中的某项特性可能会在输出结尾处看到警告。
1. Stress-ng 压力测试工具
stress-ng 是一个压力测试工具,可以通过软件仓库进行安装,也提供了 docker 版本的容器
官方链接:https://github.com/ColinIanKing/stress-ng
stress-ng 安装
# 软件包方式安装
yum -y install stress-ng
apt -y install stress-ng# 容器方式安装
docker pull lorel/docker-stress-ng# Stress-ng 使用 查看帮助
[root@Ubuntu2204 ~]#docker run -it --rm lorel/docker-stress-ng
2. OOM (Out of Memory Exception)
对于 Linux 主机,如果没有足够的内存来执行其他重要的系统任务,将会抛出 OOM (Out of Memory Exception,内存溢出、内存泄漏、内存异常 ),随后系统会开始杀死进程以释放内存, 凡是运行在宿主机的进程都有可能被 kill ,包括 Dockerd 和其它的应用程序, 如果重要的系统进程被 Kill,会导致和该进程相关的服务全部宕机。通常越消耗内存比较大的应用越容易被 kill,比如: MySQL数据库,Java程序等。
产生 OOM 异常时, Dockerd 尝试通过调整 Docker 守护程序上的 OOM 优先级来减轻这些风险,以便它比系统上的其他进程更不可能被杀死但是每个容器的 OOM 优先级并未调整, 这使得单个容器被杀死的可能性比 Docker 守护程序或其他系统进程被杀死的可能性更大,不推荐通过在守护程序或容器上手动设置 – oom -score-adj 为极端负数,或通过在容器上设置 – oom-kill-disable 来绕过这些安全措施
OOM 优先级机制: linux 会为每个进程计算一个分数,最终将分数最高的 kill
# 范围为 -1000 到 1000,值越高容易被宿主机 kill 掉,如果将该值设置为 -1000 ,则进程永远不会被宿主机 kernel kill
/proc/PID/oom_score_adj # 范围为 -17 到 +15 ,取值越高越容易被干掉,如果是 -17 , 则表示不能被 kill ,该设置参数的存在是为了和旧版本的 Linux 内核兼容。
/proc/PID/oom_adj # 这个值是系统综合进程的内存消耗量、 CPU 时间 (utime + 存活时间 (uptime - start time) 和 oom_adj 计算出的进程得分 ,消耗内存越多得分越高,容易被宿主机 kernel 强制杀死
/proc/PID/oom_score # docker 服务进程的 OOM 默认值
[root@Ubuntu2204 ~]#pidof dockerd
2756
[root@Ubuntu2204 ~]#cat /proc/2756/oom_adj
-8
[root@Ubuntu2204 ~]#cat /proc/2756/oom_score
345
[root@Ubuntu2204 ~]#cat /proc/2756/oom_score_adj
-500
3. 容器的内存限制
Docker 可以强制执行硬性内存限制,即只允许容器使用给定的内存大小。
Docker 也可以执行非硬性内存限制,即容器可以使用尽可能多的内存,除非内核检测到主机上的内存不够用了。
-m ,--memory= # 容器可以使用的最大物理内存量,硬限制,此选项最小允许值为 4m(4MB),此项较常用
使用 stress-ng 测试内存限制
假如一个容器未做内存使用限制,则该容器可以利用到系统内存最大空间,默认创建的容器没有做内存 资源限制。
# 默认一个workers 分配256M内存,2个即占512M内存
[root@Ubuntu2204 ~]#docker run --name c1 -it --rm lorel/docker-stress-ng --vm 2
# 指定内存为 300m
[root@Ubuntu2204 ~]#docker run --name c1 -it --rm -m 300m lorel/docker-stress-ng --vm 2# 因上一个命令是前台执行,下面在另一个终端窗口中执行,可以看到占用512M左右内存
[root@Ubuntu2204 ~]#docker stats
# 一次性查看资源使用情况
[root@Ubuntu2204 ~]#docker stats --no-stream
4. 容器的 CPU 限制
一个宿主机,有几十个核心的CPU,但是宿主机上可以同时运行成百上千个不同的进程用以处理不同的任务,多进程共用一个 CPU 的核心为可压缩资源,即一个核心的 CPU 可以通过调度而运行多个进程, 但是同一个单位时间内只能有一个进程在 CPU 上运行,那么这么多的进程怎么在 CPU 上执行和调度的呢?
Linux kernel 进程的调度基于 CFS(Completely Fair Scheduler),完全公平调度
服务器资源密集型
- CPU 密集型的场景: 计算密集型任务的特点是要进行大量的计算,消耗 CPU 资源,比如计算圆周率、数据处理、对视频进行高清解码等等,全靠 CPU 的运算能力。
- IO 密集型的场景: 涉及到网络、磁盘 IO 的任务都是 IO 密集型任务,这类任务的特点是 CPU 消耗很少,任务的大部分时间都在等待 IO 操作完成(因为 IO 的速度远远低于 CPU 和内存的速度),比如 Web 应用,高并发,数据量大的动态网站来说,数据库应该为 IO 密集型
CFS原理
cfs 定义了进程调度的新模型,它给 cfs_rq(cfs 的 run queue)中的每一个进程安排一个虚拟时钟 vruntime。如果一个进程得以执行,随着时间的增长,其 vruntime 将不断增大。没有得到执行的进程 vruntime 不变, 而调度器总是选择 vruntime 跑得最慢的那个进程来执行。这就是所谓的“完全公平”。为了区别不同优先级的进程,优先级高的进程 vruntime 增长得慢,以至于它可能得到更多的运行机会。 CFS 的意义在于, 在一个混杂着大量计算型进程和 IO 交互进程的系统中,CFS 调度器相对其它调度器在对待 IO 交互进程要更加友善和公平。
配置默认的CFS调度程序
默认情况下,每个容器对主机的 CPU 周期的访问都是不受限制的。可以设置各种约束,以限制给定容器对主机CPU 周期的访问。大多数用户使用并配置 默认的 CFS 调度程序。在 Docker 1.13 及更高版本中,还可以配置 realtime scheduler。
CFS 是用于常规 Linux 进程的 Linux 内核 CPU 调度程序。通过几个运行时标志,可以配置对容器拥有的 CPU 资源的访问量。使用这些设置时,Docker 会在主机上修改容器 cgroup 的设置。
--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 和 --cpu-quota 两个参数,从而使配置更简单,但是最大不能超出宿主机的 CPU 总核心数(在操作系统看到的 CPU 超线程后的数值),此项较常用--cpuset-cpus # 用于指定容器运行的 CPU 编号,也就是所谓的 CPU 绑定。如果一个或多个 CPU,则容器可以使用逗号分隔的列表或用连字符分隔的 CPU 范围。第一个 CPU 的编号为 0。有效值可能是 0-3 (使用第一,第二,第三和第四CPU)或 1,3(使用第二和第四CPU)--cpu-shares # 用于设置 cfs 中调度的相对最大比例权重, cpu-share 的值越高的容器,将会分得更多的时间片(宿主机多核 CPU 总数为 100%,假如容器 A 为1024,容器 B 为 2048,那么容器 B 将最大是容器 A 的可用 CPU 的两倍 ),默认的时间片 1024,最大 262144。这是一个软限制。注意:进程数要多个 CPU 的核数才能看到效果,此值不能设置太小。
使用 Stress-ng 测试 Cpu 配置
# 占用 2 个 CPU 资源.但只是平均的使用 CPU 资源
[root@Ubuntu2204 ~]#docker run -it --rm --name c1 lorel/docker-stress-ng --cpu 2
# 限制使用CPU 1.5 个
[root@Ubuntu2204 ~]#docker run -it --rm --name c1 --cpus 1.5 lorel/docker-stress-ng --cpu 2# 再打开两个新终端观察
[root@Ubuntu2204 ~]#docker stats
[root@Ubuntu2204 ~]#top