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, 2.03, 0.95
Tasks: 252 total,   3 running, 249 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.3 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    75412 free,   547276 used,   373208 buff/cache
KiB Swap:  2097148 total,  1651216 free,   445932 used.   138856 avail Mem

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

(新shell中)再啟動一個bash執行while true; do a=a+1;done,將該進程加入到cgroup.procs,使用top命令可以看到2個cpu使用率已經達到100%

top - 13:22:51 up 19:27,  7 users,  load average: 1.42, 1.65, 0.98
Tasks: 252 total,   3 running, 249 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.3 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    75124 free,   547544 used,   373228 buff/cache
KiB Swap:  2097148 total,  1651216 free,   445932 used.   138252 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 41416 root      20   0  116872   3644   1808 R 100.0  0.4   8:26.67 bash
 41528 root      20   0  116872   3652   1808 R 100.0  0.4   4:22.60 bash

(新shell中)再啟動一個bash執行while true; do a=a+1;done,將該進程加入到cgroup.procs,使用top命令可以看到3個cpu使用率已經達到100%

top - 13:25:58 up 19:30,  7 users,  load average: 2.28, 1.88, 1.18
Tasks: 251 total,   4 running, 247 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    75412 free,   547584 used,   372900 buff/cache
KiB Swap:  2097148 total,  1651216 free,   445932 used.   138228 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 41528 root      20   0  116872   3652   1808 R 100.0  0.4   7:29.10 bash
 41416 root      20   0  116872   3644   1808 R  99.7  0.4  11:33.12 bash
 41593 root      20   0  116784   3368   1648 R  99.7  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 - 13:26:49 up 19:31,  7 users,  load average: 2.95, 2.12, 1.30
Tasks: 251 total,   5 running, 246 sleeping,   0 stopped,   0 zombie
%Cpu0  : 74.8 us,  0.0 sy,  0.0 ni, 25.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  : 74.8 us,  0.0 sy,  0.0 ni, 25.2 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  : 74.2 us,  0.0 sy,  0.0 ni, 25.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  : 75.7 us,  0.0 sy,  0.0 ni, 24.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    75536 free,   547460 used,   372900 buff/cache
KiB Swap:  2097148 total,  1651216 free,   445932 used.   138352 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 41593 root      20   0  116784   3368   1648 R  75.7  0.3   3:16.76 bash
 41528 root      20   0  116872   3652   1808 R  74.8  0.4   8:15.67 bash
 41416 root      20   0  116872   3644   1808 R  74.1  0.4  12:19.74 bash
 41654 root      20   0  116784   3368   1648 R  74.1  0.3   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      20   0  116872   3644   1808 R 80.0  0.4   0:16.77 bash
9629 root      20   0  116872   3644   1808 R 20.0  0.4   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]# while true; do sleep 1 ;done

查看cpu占用,可以看到test1中的進程占用了99.3%的cpu,而其相對比例為20%

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
9629 root      20   0  116872   3660   1808 R 99.3  0.4   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 (0)
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                      0
numa_foreign                   0
interleave_hit             19532
local_node               6711656
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/cpuset.cpu_exclusive
[root@ cpuset]# echo 0,1 > test1/cpuset.cpus
[root@ cpuset]# echo 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,   7 running, 236 sleeping,   0 stopped,   0 zombie
%Cpu0  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  : 99.7 us,  0.3 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    68208 free,   654032 used,   273656 buff/cache
KiB Swap:  2097148 total,  1928188 free,   168960 used.    92388 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 74809 root      20   0  116652   3300   1656 R  86.0  0.3   1:27.86 bash
 74753 root      20   0  116652   3300   1656 R  66.4  0.3   1:57.41 bash
 74890 root      20   0  116652   3304   1656 R  66.4  0.3   0:11.07 bash
 74081 root      20   0  116740   3572   1808 R  66.1  0.4   3:23.12 bash
 70206 root      20   0  116784   3120   1376 R  62.5  0.3   1:38.89 bash
 72544 root      20   0  116784   3556   1800 R  52.2  0.4   1: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  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu2  :100.0 us,  0.0 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :   995896 total,    68408 free,   647088 used,   280400 buff/cache
KiB Swap:  2097148 total,  1928444 free,   168704 used.    92768 avail Mem

   PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 72544 root      20   0  116784   3556   1800 R  33.9  0.4   4:19.03 bash
 70206 root      20   0  116784   3120   1376 R  33.2  0.3   3:58.17 bash
 74753 root      20   0  116652   3300   1656 R  33.2  0.3   5:05.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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM