docker cgroup技术之cpu和cpuset

  在centos7的/sys/fs/cgroup下面可以看到与cpu相关的有cpu,cpuacct和cpuset 3个subsystem。cpu用于对cpu使用率的划分;cpuset用于设置cpu的亲和性等,主要用于numa架构的os;cpuacct记录了cpu的部分信息。对cpu资源的设置可以从2个维度考察:cpu使用百分比和cpu核数目。前者使用cpu subsystem进行配置,后者使用cpuset subsystem进程配置。首先看cpu subsystem的用法

cpu subsystem

cgroup使用如下2种方式来对cpu进行调度

  • 完全公平调度程序(CFS):按照比例进行cpu分配调度,具体实现可以参考CFS
  • 实时调度程序(RT):与CFS类似,用于限制实时任务对cpu的获取,一般用不到。(注:Linux的进程分普通进程和实时进程,实时进程比普通进程的优先级高,由于其在进程死亡之前始终是活动进程,故占用cpu资源大)

cpu subsystem主要涉及5接口:cpu.cfs_period_us,cpu.cfs_quota_us,cpu.shares,cpu.rt_period_us,cpu.rt_runtime_us

cfs_quota_us为-1,表示使用的CPU不受cgroup限制。cfs_quota_us的最小值为1ms(1000),最大值为1s,参见CFS Bandwidth Control

cpu.cfs_period_us用于设置cpu带宽(bandwidth),单位为微秒us。cpu.cfs_quota_us设置cpu.cfs_period_us周期内cgroup可使用的cpu。多核场景下,如配置cpu.cfs_period_us=10000,而cfs_quota_us=20000,表示该cgroup可以完全使用2个cpu。较大的cfs_period_us可以提高吞吐量(可以为CPU密集型任务提供更多运行时间)。cfs_period_us表示一个CPU的宽度,系统上可用的总的CPU宽度为:(cpus on the host) * (cpu.cfs_period_us)。当出现如下条件时,cpu.stat中的nr_throttled统计会+1。

  1. 周期(period)内使用的CPU达到quota的CPU
  2. 父cgroup中使用的CPU达到其quota的CPU

首先在/sys/fs/cgroup/cpu下面新建一个cgroup,将cpu周期设置为100000,cgroup在单个周期中占用时长为50000,即单个cpu的50%

# cat cpu.cfs_period_us
100000
# echo 50000 > cpu.cfs_quota_us
# bash
# cat tasks
# echo $$
40768
# echo $$ > cgroup.procs
# while true; do a=a+1;done
PID   USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM   TIME+   COMMAND
40768 root      20  0   116872   3644   1808 R  50.3  0.4   0:19.75  bash

下例中将cpu周期设置为100000,cgroup在单个周期中占用时长为300000,即该cgroup可以完全占用3个cpu(当前环境4 cpu)。

启动一个bash执行while true; do a=a+1;done并将该进程加入到cgroup.procs,使用top命令可以看到1个cpu使用率已经达到100%

top - 13:20:06 up 19:24,7 users,load average: 3.21,1)">2.03,1)">0.95
Tasks: 252 total,1)">3 running,1)">249 sleeping,1)">0 stopped,1)">0 zombie
%Cpu0  :  0.3 us,1)">0.0 sy,1)">0.0 ni,1)">99.7 id,1)">0.0 wa,1)">0.0 hi,1)">0.0 si,1)">0.0 st
%Cpu1  :  0.0 us,1)">100.0 id,1)"> st
%Cpu2  :100.0 us,1)">0.0 id,1)"> st
%Cpu3  :  0.3 sy,1)"> st
KiB Mem :   995896 total,1)">75412 free,1)">547276 used,1)">373208 buff/cache
KiB Swap:  2097148 total,1)">1651216 free,1)">445932 used.   138856 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 41416 root      20   0  99.7  5:41.19 bash

(新shell中)再启动一个bash执行while true; do a=a+1;done,将该进程加入到cgroup.procs,使用top命令可以看到2个cpu使用率已经达到100%

top - 22:51 up 27,1)">1.42,1)">1.65,1)">0.98 st
%Cpu1  :100.0 us,1)"> st
%Cpu2  :75124 free,1)">547544 used,1)">373228 buff/1382521808 R 100.0  8:26.67 bash
 41528 root      3652   4:22.60 bash

(新shell中)再启动一个bash执行while true; do a=a+1;done,将该进程加入到cgroup.procs,使用top命令可以看到3个cpu使用率已经达到100%

top - 25:58 up 30,1)">2.28,1)">1.88,1)">1.18251 total,1)">4 running,1)">247 sleeping,1)"> st
%Cpu3  :547584 used,1)">372900 buff/1382281808 R 100.0  7:29.101808 R  99.7  0.4  11:33.1241593 root      116784   3368   1648 R  0.3   2:30.04 bash

(新shell中)再启动一个bash执行while true; do a=a+1;done,将该进程加入到cgroup.procs,此时有4个进程同时消耗cpu,但总体消耗限制在3个cpu,如下图中,每个bash消耗的cpu约75%

top - 26:49 up 31,1)">2.95,1)">2.12,1)">1.305 running,1)">246 sleeping,1)"> zombie
%Cpu0  : 74.8 us,1)">25.2 id,1)"> st
%Cpu1  :  st
%Cpu2  : 74.2 us,1)">25.8 id,1)"> st
%Cpu3  : 75.7 us,1)">24.3 id,1)">75536 free,1)">547460 used,1)">13835275.7  3:16.7674.8  15.6774.1  12:19.7441654 root      1:41.05 bash

 cpu.cfs_quota_us和cpu.cfs_period_us以绝对比例限制cgroup的cpu,而cpu.shares以相对比例限制cgroup的cpu。

在/sys/fs/cgroup/cpu/下创建2个cgroup:test1和test2,设置test1的cpu.shares=50,test2的cpu.shares=200,则意味着test1在cpu竞争下最多可以使用所有cpu的20%,而test2在cpu竞争下最多可以使用所有cpu的80%(不考虑系统基本进程占用)。为方便验证,将系统的cpu设置为1个。创建2个bash进程分别加入2个cgroup后执行while true; do a=a+1;done

 PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
9683 root      80.0  16.77 bash
9629 root      20.0  09.73 bash

使用cpu.shares需要注意的是,对cpu的相对比例是在cpu竞争的条件下,如果一个cgroup使用的相对比例是50%,但实际仅使用了10%,那么多余的cpu会被回收,给其他cgroup使用,参见CPU

当一个 cgroup 中的任务处于闲置状态且不使用任何 CPU 时间时,剩余的时间会被收集到未使用的 CPU 循环全局池中。其它 cgroup 可以从这个池中借用 CPU 循环

下例中test1 cgroup设定50,test2 cgroup设定200,但test1中运行的进程非常消耗cpu,而test2中运行的进程仅使用很小一部分cpu,且sleep操作会导致其进程进入sleep状态

Test1 cgroup
# echo $$
9629
[root@ test1]# cat cpu.shares
50
[root@ test1]# while true; do a=a+1;done

Test2 cgroup
# echo $$
9683
[root@ test2]# cat cpu.shares
200
[root@ test2]# do sleep 1 ;done

查看cpu占用,可以看到test1中的进程占用了99.3%的cpu,而其相对比例为20%

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+3660   99.3  9:32.48 bash

 

cpuset subsystem

cpuset主要是为了NUMA(非均匀访问存储模型)使用的,NUMA技术将CPU划分成不同的组(Node),每个Node由多个CPU组成,并且有独立的本地内存、I/O等资源(硬件上保证)。可以使用numactl查看当前系统的node清空,如下面表示系统只有一个node,含cpu 0-3,内存大小约1G

# numactl -H
available: 1 nodes ()
node 0 cpus: 0 1 2 3
node 0 size: 1023 MB
node 0 free: 70 MB
node distances:
node   0
  0:  10

可以使用dmesg | grep -i numa命令查看当前系统是否开启了numa下·

numa的基本架构如下,当cpu访问直接attach的内存时(local access)时会有较大效率,而访问其他cpu attach的内存(remote access)会导致效率下降。

 

Numa内存分配策略有一下四种,一般采用默认方式

  • 缺省default:总是在本地节点分配(当前进程运行的节点上)。
  • 绑定bind:强制分配到指定节点上。
  • 交叉interleavel:在所有节点或者指定节点上交叉分配内存。
  • 优先preferred:在指定节点上分配,失败则在其他节点上分配。

numa场景下可能会出现一个性能问题,NUMA架构的CPUThe MySQL “swap insanity” problem and the effects of the NUMA architectureA brief update on NUMA and MySQL。发生性能的主要原因是因为more策略下可能会发生swap,即总是在本地节点分配内存,当本地内存不足时会发生swap,可以尝试使用如下方式进行

  • 设置numa interleave=all,意味着整个进程的内存是均匀分布在所有的node之上,进程可以以最快的方式访问本地内存
  • 使用mlock方式申请内存,这样这段内存不会使用swap
  • 使用mmap的MAP_POPULATE,预先分配匿名页,后续访问此内存时不会发生缺页
  • 调节系统的vm.swappiness,对于数据库应用服务器,设置为0,可以提高物理内存的使用率,进而提高数据库服务的响应性能

默认方式下,进程总是使用本地节点进程内存分配,可以使用numastat查看内存分配情况

# numastat
                           node0
numa_hit                 6711656
numa_miss                      
numa_foreign                   
interleave_hit             19532
local_node               
other_node                     0

 

cpuset调用sched_setaffinity来设置进程的cpu亲和性,调用mbind和set_mempolicy来设置内存的亲和性。可以通过查看/proc/$pid/status查看当前进程cpu和mem的亲和性。cpuset使用中应该遵循以下3点

  1. 子cpuset的cpu和memory node必须是父cgoup的子集
  2. 除非父cgroup标记了exclusive,否则子cgoup无法标记该flag
  3. 如果cgroup的cpu或memory标记了exclusive,那么该cgroup的cpu不能与兄弟cgroup有重合,且父子之间必须重合(参见第一条)

如下例中,在/sys/fs/cgroup/cpuset中创建2个cgroup,按照如下步骤,可以看出,当test1和test2有重合时,设置cpuset失败

# rmdir test1
[root@ cpuset]# mkdir test1
[root@ cpuset]# mkdir test2
[root@ cpuset]# echo 1 > test1/cpuset.cpu_exclusive
[root@ cpuset]# echo 1 > test2/0,1)">cpuset.cpus
[root@ cpuset]# echo 1,1)">2 > test2/cpuset.cpus
-bash: echo: write error: Invalid argument
[root@ cpuset]# echo 2 > test2/cpuset.cpus

cpuset.cpu_exclusive:包含标签(0 或者 1),它可以指定:其它 cpuset 及其父、子 cpuset 是否可共享该 cpuset 的特定 CPU。默认情况下(0),CPU 不会专门分配给某个 cpuset 。

上面介绍了设置该标志后兄弟cpuset之间的cpuset.cpus不能有重合,但父子cpuset之间是必须重合的。cpu_exclusive标记并不能实现完全的cpu隔离(不隶属于cgroup管辖的进程默认拥有所有的cpu权限),如下例中启动了6个消耗cpu的bash进程,仅对其中一个bash进程进行了cpuset的exclusive,可以看到exclusive并不能保证cpu的隔离,只用于保证不于其他兄弟cpuset定义的cpus重叠。核隔离可以使用内核启动参数isolcpus,隔离的cpu不会对其进行负载均衡操作。

Tasks: 243 total,1)">7 running,1)">236 sleeping,1)"> zombie
%Cpu0  :99.7 us,1)">68208 free,1)">654032 used,1)">273656 buff/1928188 free,1)">168960 used.    9238874809 root      116652   3300   1656 R  86.0  27.8674753 root      66.4  57.4174890 root      3304   11.0774081 root      116740   3572   66.1  23.1270206 root      3120   1376 R  62.5  38.8972544 root      3556   1800 R  52.2  57.57 bash

cpuset.memory_spread_page用于设定文件系统缓冲是否应在该 cpuset 的内存节点中均匀分布,cpuset.memory_spread_slab用于设定slab缓冲(如inode和dentries)是否应在该 cpuset 的内存节点中均匀分布,默认否。该策略在将(大的)数据文件分布到多个node时可以提升性能(平均分布)。

cpuset.sched_load_balance和cpuset.sched_relax_domain_level与cpu负载均衡有关。linux使用sched domains(调度域)为单位进行负载均衡。当sched_load_balance设置为enable时,会在该cpuset中的cpu上进行负载均衡,否则不会在该cpuset中的cpu上进行负载均衡(不同cpuset中重叠的cpu上可能也会有负载均衡)。当root cpuset的sched_load_balance为enable时,会在所有的cpu上进行负载均衡,此时会忽略所有子cpuset中对该值的设置,因此只有在root cpuset disable之后,子cpuset才能生效。cpu负载均衡会影响系统性能,在以下两种情况下可以不需要该功能:

  • 大型系统中存在很多cpu,如果对单独进程分配了独立的cpu,此时无需使用cpu负载均衡
  • 实时系统上需要减少cpu的损耗,此时可以不适用负载均衡

cpuset.sched_relax_domain_level表示 kernel 应尝试平衡负载的 CPU 宽度范围,仅当cpuset.sched_load_balance enable时生效。一般无需改动。

cpuset.memory_migrate包含一个标签(0 或者 1),用来指定当 cpuset.mems 的值更改时,是否应该将内存中的页迁移到新节点。

 

总结:

使用cpu subsystem可以在cpu时间上限制进程,而使用cpuset可以在cpu/mem number上限制进程。但如果cpu和cpuset不匹配时应该如何处理?如下例中,在cpuset中限制该cgroup中的进程只能运行在2号核上,但在cpu中该cgroup的进程最多可以使用2个核

# mkdir cpuset/cpusettest
# mkdir cpu/cputest

# cd cpuset/cpusettest
# echo 0 > cpuset.mems
# echo 2 > cpuset.cpus

# cd cpu/cputest
# echo 1000 > cpu.cfs_period_us
# echo 2000 > cpu.cfs_quota_us

启动3个bash执行while true; do a=a+1;done,并将其pid加入到cpu和cpuset的cgroup.procs中,观察top命令可以看到3个bash进程仅占用了2号核,每个cpu占用率都约等于33%。由此可知,cpu中规定了进程可以使用的cpu的上限,但并不一定能达到上限

%Cpu0  :  68408 free,1)">647088 used,1)">280400 buff/1928444 free,1)">168704 used.    9276833.9  19.0333.2  58.1705.08 bash

 

TIPS:

  • 在设置cpuset时必须首先设置cpuset.cpus和cpuset.mems,否则可能出现"No space left on device"的错误
  • 可以使用docker inspect DOCKERID来查看该容器挂载的cgroup路径

参考:

sec-cpu

限制cgroup的CPU使用(subsystem之cpu)

numa

cpusets

CFS Bandwidth Control

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


最近一直在开发Apworks框架的案例代码,同时也在一起修复Apworks框架中的Bug和一些设计上的不足。遇到的一个普遍问题是,代码的调试过程需要依赖很多外部系统,比如MongoDB、PostgreSQL、RabbitMQ等。当然可以在本机逐一安装这些服务,然后对服务进行配置,使其满足自己开发调试
最近每天都在空闲时间努力编写Apworks框架的案例代码WeText。在文本发布和处理微服务中,我打算使用微软的SQL Server for Linux来做演示,于是也就在自己的docker-compose中加入了MS SQL Server的服务。其实在Docker中运行SQL Server是非常容
在《Kubernetes中分布式存储Rook-Ceph部署快速演练》文章中,我快速介绍了Kubernetes中分布式存储Rook-Ceph的部署过程,这里介绍如何在部署于Kubernetes的ASP.NET Core MVC的应用程序中使用Rook-Ceph所创建的存储对象。 构建ASP.NET C
最近在项目中有涉及到Kubernetes的分布式存储部分的内容,也抽空多了解了一些。项目主要基于Rook-Ceph运行,考虑到Rook-Ceph部署也不那么简单,官方文档的步骤起点也不算低,因此,在整合官方文档的某些步骤的基础上,写篇文章简单总结一下。 Rook-Ceph是Kubernetes中分布
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
构建镜像最具挑战性的一点是使镜像大小尽可能的小。Dockerfile中的每条指令都为图像添加了一个图层,您需要记住在移动到下一层之前清理任何不需要的工件。对于多阶段构建,您可以在Dockerfile中使用多个FROM语句。每个FROM指令可以使用不同的基础,并且每个指令都开始一个新的构建。您可以选择
本文介绍compose配置文件参数的使用,熟练编写compose文件 [root@docker lnmp]# cat lnmp.yaml version: '3' services: nginx: build: /root/docker_demo/nginx/ ports: - &q
环境 docker-machine主机:192.168.1.9 docker主机:192.168.1.10 步骤: 安装docker-machine 创建ssh密钥对,实现两主机无密登录 创建docker主机,命名host1 变更docker环境变量 运行容器查看两端是否同步 镜像容器同步测试成功
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
https://blog.csdn.net/wanglei_storage/article/details/77508620 实践中会发现,生产环境中使用单个 Docker 节点是远远不够的,搭建 Docker 集群势在必行。然而,面对 Kubernetes, Mesos 以及 Swarm 等众多容
1.引言 紧接上篇.NET Core容器化@Docker,这一节我们先来介绍如何使用Nginx来完成.NET Core应用的反向代理,然后再介绍多容器应用的部署问题。 2. Why Need Nginx .NET Core中默认的Web Server为Kestrel。 Kestrel is grea
docker rm `docker ps -a | grep Exited | awk '{print $1}'` 删除异常停止的docker容器 docker rmi -f `docker images | grep '<none>' | awk &#3
什么是Docker Compose 在微服务盛行的今天,我们通常是这么定义Compose的:对容器的统一启动和关闭的编排工具。 但是我以前还是有个疑惑,谁会用Compose在一台服务器上部署多个服务呢?干脆直接用单体服务就行了!直到我遇到了以下的一个需求,让我明白了在一台服务器上不得不用多个服务的时
CentOS下Docker与.netcore(一) 之 安装 CentOS下Docker与.netcore(二) 之 Dockerfile CentOS下Docker与.netcore(三)之 三剑客之一Docker-Compose CentOS下Docker与.netcore(四)之 三剑客之一D
很多时候,我们在本地开发过程中程序运行很正常,但是发布到线上之后由于环境的原因,可能会有一些异常。通常我们会通过日志来分析问题,除了日志还有一种常用的调试手段就是:附加进程。 VS中的附加进程非常强大,目前提供了9种常用的附加方式。 在当前.Net Core支持跨平台的大背景下,其中Linux环境和
https://www.cnblogs.com/bigberg/p/8867326.html 一、简介 Docker有个编排工具docker-compose,可以将组成某个应该的多个docker容器编排在一起,同时管理。同样在Swarm集群中,可以使用docker stack 将一组相关联的服务进行
.Net6中想实现对某个网址截屏,可通过Selenium模拟访问网址并实现截图。 实现 安装Nuget包 <PackageReference Include="Selenium.Chrome.WebDriver" Version="85.0.0" /&g
原文 https://www.cnblogs.com/gispathfinder/p/5871043.html 我们在使用docker run创建Docker容器时,可以用--net选项指定容器的网络模式,Docker有以下4种网络模式: host模式,使用--net=host指定。 co