Docker資源限制與Cgroups


一、Linux control groups

簡介

 
   Linux CGroup全稱Linux Control Group, 是Linux內核的一個功能,用來限制,控制與分離一個進程組群的資源(如CPU、內存、磁盤輸入輸出等)。這個項目最早是由Google的工程師在2006年發起(主要是Paul Menage和Rohit Seth),最早的名稱為進程容器(process containers)。在2007年時,因為在Linux內核中,容器(container)這個名詞太過廣泛,為避免混亂,被重命名為cgroup,並且被合並到2.6.24版的內核中。
主要提供了如下功能:
  • Resource limitation: 限制資源使用,比如內存使用上限以及文件系統的緩存限制。
  • Prioritization: 優先級控制,比如:CPU利用和磁盤IO吞吐。
  • Accounting: 一些審計或一些統計,主要目的是為了計費。
  • Control: 掛起進程,恢復執行進程。 

CGroup的術語

任務(Tasks):就是系統的一個進程。
控制組(Control Group):一組按照某種標准划分的進程,其表示了某進程組,Cgroups中的資源控制都是以控制組為單位實現,一個進程可以加入到某個控制組。而資源的限制是定義在這個組上,簡單點說,cgroup的呈現就是一個目錄帶一系列的可配置文件。
層級(Hierarchy):控制組可以組織成hierarchical的形式,既一顆控制組的樹(目錄結構)。控制組樹上的子節點繼承父結點的屬性。簡單點說,hierarchy就是在一個或多個子系統上的cgroups目錄樹。
子系統(Subsystem):一個子系統就是一個資源控制器,比如CPU子系統就是控制CPU時間分配的一個控制器。子系統必須附加到一個層級上才能起作用,一個子系統附加到某個層級以后,這個層級上的所有控制族群都受到這個子系統的控制。Cgroup的子系統可以有很多,也在不斷增加中。以下為內核3.10+支持的子系統(可以通過 ls /sys/fs/cgroup 查看到): 
  • blkio — 這個子系統為塊設備設定輸入/輸出限制,比如物理設備(磁盤,固態硬盤,USB 等等)。
  • cpu — 這個子系統使用調度程序提供對 CPU 的 cgroup 任務訪問。
  • cpuacct — 這個子系統自動生成 cgroup 中任務所使用的 CPU 報告。
  • cpuset — 這個子系統為 cgroup 中的任務分配獨立 CPU(在多核系統)和內存節點。
  • devices — 這個子系統可允許或者拒絕 cgroup 中的任務訪問設備。
  • freezer — 這個子系統掛起或者恢復 cgroup 中的任務。
  • memory — 這個子系統設定 cgroup 中任務使用的內存限制,並自動生成內存資源使用報告。
  • net_cls — 這個子系統使用等級識別符(classid)標記網絡數據包,可允許 Linux 流量控制程序(tc)識別從具體 cgroup 中生成的數據包。
  • net_prio — 這個子系統用來設計網絡流量的優先級
  • hugetlb — 這個子系統主要針對於HugeTLB系統進行限制,這是一個大頁文件系統。 

操作接口

  在linux系統中一皆文件,當然對CGroup的接口操作也是通過文件來實現的,我們可以通過mount命令查看其掛載目錄:

以上目錄都可以是限制的對象,也就是對應的術語中的各個子系統。例如查看這里的CPU限制目錄:

如果你熟悉CPU管理的話,cfs_period 和 cfs_quota兩個可能不會陌生,這兩個參數需要組合使用,可以用來限制進程在長度為cfs_period 的一段時間內,只能被分配到總量為cfs_quota 的 CPU 時間。以下示例將會演示如何限制。

限制CPU

限制CPU的使用需要在子系統目錄(/sys/fs/cgroup/cpu)下創建目錄,這里創建一個cpu_limit_demo目錄:

可以看到當我們在CPU子系統下創建目錄時候,其對應的限制配置文件會自動進行創建,現在在后台寫個死循環跑滿CPU:

此時使用top命令查看pid為10069的進程CPU使用狀況:

如圖可見,此時該進程使用的CPU1已經是100%,即%Cpu :100.0 us,我們先來查看我們剛才創建的cpu_limit_demo目錄下這兩個參數默認值是多少:

cpu.cfs_quota_us值為-1代表沒有任何限制,cpu.cfs_period_us 則是默認的 100 ms,即100000us,下面將向cpu_limit_demo控制組的cpu.cfs_quota_us文件寫入50ms即50000us,這表示在100ms周期內,cpu最多使用%50,同時將該進程的pid號為10069寫入對應的tasks文件,表示對那個進程限制:

於是pid為10069的進程cpu就被限制成了%Cpu :50.0 us,此時利用top在此查看cpu使用情況(top后按1可看到每個邏輯cpu具體使用):

以上可以看到此時pid10069的進程使用率已經變成了50%了,說明限制生效了。同樣的道理,在/sys/fs/cgroup下的子系統都可以限制不通的資源使用如Block IO、Memory等。

 

限制內存

首先在 /sys/fs/cgroup/memory 下新建一個名為 limit_memory_demo 的 cgroup:

mkdir /sys/fs/cgroup/memory/limit_memory_demo

限制該 cgroup 的內存使用量為 500 MB:

# 物理內存500M(下面單位Byte) MB並且不使用swap
echo 524288000 > /sys/fs/cgroup/memory/limit_memory/memory.limit_in_bytes

echo 0 > /sys/fs/cgroup/memory/limit_memory/memory.swappiness

最后將需要限制的進程號的pid寫入task文件就可以了:

echo [pid] > /sys/fs/cgroup/memory/limit_memory_demo/tasks

限制磁盤IO

   使用dd寫到null設備:

[root@app51 blkio]# dd if=/dev/sda of=/dev/null  bs=1M

使用iotop(安裝使用yum install -y iotop)查看io速率,此時讀的速率為2.17G/s

創建一個blkio(塊設備IO)的cgroup,並查看設備號,然后將pid和限制的設備以及速度寫入文件:

然后在觀察dd命令的進程:10178,如圖顯示該進程讀的的速率已經變成了3.8M/s了
注:8:0 是設備號,你可以通過ls -l /dev/sda1查看。
可以簡寫為以下步驟:
mkdir /sys/fs/cgroup/blkio/
echo '8:0 1048576'  > /sys/fs/cgroup/blkio/limit_io/blkio.throttle.read_bps_device
echo [pid] > /sys/fs/cgroup/blkio/limit_io/tasks

 

二、 Docker中資源限制原理

  在了解了Cgroup對資源的限制方法,再來理解Docker中的資源限制其實就變的容易了。默認情況下,Docker會在需要限制的子系統下創建一個目錄為docker的控制組如下:

當容器運行后,會在這些目錄生成以容器ID為目錄的子目錄用於限制的容器資源。例如,以cpu為例,docker run啟動參數中--cpu-quota 參數可以指定默認時間內使用的cpu:

[root@app51 docker]# docker run -d --name nginx-c1 --cpu-quota 50000  nginx:latest        
e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29
[root@app51 docker]#

此時我們查看cpu對應的容器資源限制:

[root@app51 docker]# cd /sys/fs/cgroup/cpu/docker/
[root@app51 docker]# cat e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29/cpu.cfs_quota_us
50000
[root@app51 docker]# cat e9432a513e4bed0a744a29a8eaba2b27d9e40efabfe479d19d32f9558888ed29/tasks 
10561
10598

從上面結果我們能看出,docker run的啟動限制參數值,被注入到了Cgroups的cpu控制組的對應配置文件中了。同時還有看到有兩個進程同時被限制了,這是因為我們啟動的nginx,nginx的主進程會啟動多個子進程,使用ps -ef可以查看:

[root@app51 docker]# ps -ef |grep 10561
root     10561 10544  0 16:32 ?        00:00:00 nginx: master process nginx -g daemon off;
101      10598 10561  0 16:32 ?        00:00:00 nginx: worker process
root     10614 10179  0 16:38 pts/2    00:00:00 grep --color=auto 10561
[root@app51 docker]#

不難看到,其中主進程為10561(nginx master),子進程為10598(nginx worker)。以上則是docker的資源限制原理。Docker對資源限制主要是CPU和內存,其中還有很多參數可以使用,以下將會介紹docker中CPU和MEMERY限制參數。

三、CPU限制參數

  默認情況下,容器的資源是不受限制的,宿主機提供多少資源,容器就可以使用多少資源,如果不對容器做資源限制,很有可能一個或幾個容器把宿主機的資源耗盡,而導致應用或者服務不可用。所以對容器資源的限制顯得非常重要,而docker主要提供兩種類別的資源限制:CPU和內存,通過docker run 時指定參數實現。cpu限制資源限制有多種維度,以下將詳細介紹。

限制cpu配額

    參數通過--cpu-period=<value>和--cpu-quota=<value>共同起作用,即介紹上述Cgroups使用的例子。表示在cpu-period時間(默認100ms)內,可用的cpu配額。
示例:
docker run -d --cpu-period=100000 --cpu-quota=250000 --name test-c1 nginx:latest

限制cpu可用數量 

  參數通過--cpus=<value>指定,意思限制可用cpu個數,列如--cpus=2.5表示該容器可使用的cpu個數最多是2.5個,這相當與設置了--cpu-period=100000和 --cpu-quota=250000該指令在docker版本1.13以及以上使用。 
示例:
[root@app51 ~]# docker run -d --cpus=2 --name test-c2 nginx:latest
5347269d0974e37af843b303124d8799c6f4336a14f61334d21ce9356b1535bc

使用固定的cpu

   通過--cpuset-cpus參數指定,表示指定容器運行在某個或某些個固定的cpu上,多個cpu使用逗號隔開。例如四個cpu,0代表第一個cpu,--cpuset-cpus=1,3代表該容器只能運行在第二個或第四個cpu上。查看cpu可以通過cat /proc/cpuinfo查看。

示例:

[root@app51 ~]# docker run -d --cpuset-cpus=1,3 --name test-c3 nginx:latest  
276056fce04982c2de7969ca309560ce60b0ebf960cf7197808616d65aa112d4

設置CPU比例(權重)

  通過參數--cpu-shares指定,值為大於或小於1024的整數(默認1024),代表當主機cpu資源使用緊張時,每個容器所使用的cpu資源的比例(權重)。當有足夠的CPU資源時,所有容器都會根據需要使用盡可能多的CPU。當第一個容器設置該參數的值為2048,第二個容器設置該參數的值為4096,那么當cpu資源不夠用時候,兩個容器cpu資源使用比例為1:2,
示例: 
[root@app51 ~]# docker run -d --cpu-shares=2048 --name test-c4 nginx:latest                    
578506d61324b38d7a01bf1d2ec87cb5d1ab50276ef6f7b28858f2d2e78b2860
[root@app51 ~]# docker run -d --cpu-shares=4096 --name test-c5 nginx:latest      
d56a90bf080b70d11d112468348874e48fe4a78d09d98813a0377b34fa382924

四、 MEMORY限制參數

  內存是一種不可壓縮資源,一旦某個進程或者容器中應用內存不足就會引起OOM異常(Out Of Memory Exception),這將導致應用不可用,並且在Linux主機上,如果內核檢測到沒有足夠的內存來執行重要的系統功能,系統會按照一定優先級殺死進程來釋放系統內存。docker對內存的限制分為swap限制和物理內存限制,單位可以使用b,k, m,g。

限制最大物理內存使用

  通過-m或者—memory指定,是硬限制,如果設置此選項,則允許設置的最小值為4m,該參數是最常用參數。

示例:

[root@app51 ~]# docker run -d  --memory=512m --name mem-c1 nginx:latest                       
67b0cb645c401bc6df3235d27d629185870716351396c71dfa3877abbbd377c8

限制swap使用  

通過--memory-swap參數指定,不過此參數設置時候,情況比較較多。當容器中的物理內存使用不足時候,swap才會被允許使用,所以當--memory參數設置時候,此參數才會有意義:
  • --memory-swap 設置為正整數,那么這兩個--memory和 --memory-swap 必須設置。--memory-swap 表示可以使用的memory 和 swap,並且--memory控制非交換內存(物理內存)使用的量。列如--memory=300m 和--memory-swap=1g,容器可以使用300m的內存和700m(1g - 300m)swap。
  • --memory-swap 設置為0,則忽略該設置,並將該值視為未設置。
  • --memory-swap 設置為與值相同的值--memory,並且--memory設置為正整數,則容器不能使用swap,可以通過此方法限制容器不使用swap。
  • --memory-swap 未設置並--memory 設置,則容器可以使用兩倍於--memory設置的swap,主機容器需要配置有swap。例如,如果設置--memory="300m" 和--memory-swap 未設置,容器可以使用300m的內存和600m的swap。
  • --memory-swap 設置為-1,則允許容器使用無限制swap,最多可達宿主機系統上可用的數量。

示例:

[root@app51 ~]# docker run -d  --memory=512m --memory-swap=512m --name mem-c2 nginx:latest    
6b52c015a53be2c3e0e509eea918125a760c1c14df4cc977f05b5b31b83161d5

其他

  • --oom-kill-disable  :默認情況下,如果發生內存不足(OOM)錯誤,內核會終止容器中的進程,如要更改此行為,使用該--oom-kill-disable選項,但是該選項有個前提就是-m選項必須設置。
  • --memory-swappiness:默認情況下,容器的內核可以交換出一定比例的匿名頁。--memory-swappiness就是用來設置這個比例的。--memory-swappiness可以設置為從 0 到 100。0 表示關閉匿名頁面交換。100 表示所有的匿名頁都可以交換。默認情況下,如果不適用--memory-swappiness,則該值從父進程繼承而來。
  • --memory-reservation:Memory reservation 是一種軟性限制,用於限制物理內存的最大用值,它只是確保容器不會長時間占用超過--memory-reservation限制的內存大小。給--memory-reservation設置一個比-m小的值后,雖然容器最多可以使用-m使用的內存大小,但在宿主機內存資源緊張時,在系統的下次內存回收時,系統會回收容器的部分內存頁,強迫容器的內存占用回到--memory-reservation設置的值大小。

 

五、總結

    本文簡單介紹了Linux Cgroups以及它對資源限制的使用方法,並提供了簡單的示例。此外還介紹了Docker如何使用該方式對容器進行資源限制,本質上是docker run時將限制參數注入到Cgroups的各個資源限制的配置文件中,從而達到對容器的資源限制。希望大家對容器的資源限制有一定的理解。

 


免責聲明!

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



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