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的用法html

cpu subsystemnode

cgroup使用以下2種方式來對cpu進行調度mysql

  • 徹底公平調度程序(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_uslinux

cpu.cfs_period_us用於設置cpu時間週期長度,單位爲微秒us。cpu.cfs_quota_us設置cpu.cfs_period_us週期內cgroup可運行的cpu的總時間,多核場景下,如配置cpu.cfs_period_us=10000,而cpu.cfs_period_us=20000,表示該cgroup能夠徹底使用2個cpu。git

首先在/sys/fs/cgroup/cpu下面新建一個cgroup,將cpu週期設置爲100000,cgroup在單個週期中佔用時長爲50000,即單個cpu的50%github

複製代碼

# 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)。sql

啓動一個bash執行while true; do a=a+1;done並將該進程加入到cgroup.procs,使用top命令能夠看到1個cpu使用率已經達到100%docker

複製代碼

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%shell

複製代碼

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路徑
 

參考:

https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/7/html/resource_management_guide/sec-cpu

https://segmentfault.com/a/1190000008323952

http://cenalulu.github.io/linux/numa/

https://www.kernel.org/doc/Documentation/cgroup-v1/cpusets.txt

相關文章
相關標籤/搜索