Docker的資源限制

misakivv發表於2024-06-08

目錄
  • 一、什麼是資源限制
    • 1、Docker的資源限制
    • 2、核心支援Linux功能
    • 3、OOM異常
    • 4、調整/設定程序OOM評分和優先順序
      • 4.1、/proc/PID/oom_score_adj
      • 4.2、/proc/PID/oom_adj
      • 4.3、/proc/PID/oom_score
  • 二、容器的記憶體限制
    • 1、實現原理
    • 2、命令格式及指令引數
      • 2.1、命令格式
      • 2.2、指令引數
    • 3、案例
      • 3.1、拉取容器壓測工具映象
      • 3.2、文件的Example選項說明
      • 3.3、測試記憶體使用限制
      • 3.4、驗證
      • 3.5、記憶體軟限制
      • 3.6、交換分割槽限制
    • 4、擴大記憶體限制
  • 三、容器的CPU限制
    • 1、實現原理
    • 2、命令格式及指令引數
      • 2.1、命令格式
      • 2.2、指令引數
    • 3、案例
      • 3.1、啟動一個程序,佔用8核CPU
      • 3.2、限制容器CPU
      • 3.3、將容器執行到指定的cpu上
      • 3.4、基於cpu-shares對cpu進行切分
  • 四、容器的塊I/O頻寬限制
    • 1、實現原理
    • 2、指令格式及指令引數
      • 2.1、指令格式
      • 2.2、指令引數
    • 3、案例
      • 3.1、建立兩個不同塊I/O頻寬權重的容器
      • 3.2、限制讀取速率
      • 3.3、限制寫入速率
      • 3.4、限制讀取IOPS(每秒I/O操作次數)
      • 3.5、限制寫入IOPS

一、什麼是資源限制

1、Docker的資源限制

預設情況下,容器是沒有資源限制的,它會盡可能地使用宿主機能夠分配給它的資源。Docker提供了一種控制分配多少量的記憶體、CPU或阻塞I/O給一個容器的方式,即透過在docker rundocker create命令時設定執行時配置的標誌。

2、核心支援Linux功能

docker info

其中許多功能都要求您的核心支援Linux功能,可以透過docker info命令來檢查是否支援,如果核心中禁用了某項功能,那你可能會在下邊收到一條Warning。

3、OOM異常

對於Linux主機,如果沒有足夠的內容來執行其他重要的系統任務,將會丟擲OOM異常(記憶體溢位、記憶體洩漏、記憶體異常),隨後系統會開始殺死程序以釋放記憶體,凡是執行在宿主機的程序都有可能被kill,包括docker daemon和其他重要的應用程式。如果重要的系統程序被kill,會導致和該程序相關的服務全部當機。

產生OOM異常時,Docker嘗試透過調整docker守護程式上的OOM優先順序來減輕這些風險,以便它比系統上的其他程序更不可能被殺死,但是容器的OOM優先順序未調整時單個容器被殺死的可能性更大(不推薦調整容器的優先順序這種方式)。

4、調整/設定程序OOM評分和優先順序

Linux會每個程序算一個分數,最終他會將分數最高的程序kill掉

4.1、/proc/PID/oom_score_adj

  • 這個檔案控制著一個程序在發生記憶體不足(OOM)時被核心選中殺死的傾向性。

  • 值的範圍是-1000到1000。預設情況下,大多數程序的值為0。正值增加程序被選中殺掉的可能性,負值則減少這種可能性。

  • 如果設定為-1000,則表示程序永遠不會被宿主機kernel kill,即便在極端的記憶體壓力下也是如此。這個值允許使用者或系統管理員根據程序的重要性來調整其在記憶體壓力下的生存優先順序。

4.2、/proc/PID/oom_adj

  • 這個檔案在較老的Linux核心版本中用於類似的目的,控制程序在OOM時被殺死的優先順序。

  • 它的取值範圍是-17到15,作用和oom_score_adj相似,該設定引數的存在是為了和舊版本的Linux核心相容。值越低,程序越不容易被殺死。

  • -17表示程序不會被killer終止。

4.3、/proc/PID/oom_score

  • 這個檔案顯示了一個經過計算的分數,反映了在記憶體不足情況下,核心選擇終止該程序的傾向性。

  • 這個分數是基於多個因素動態計算出來的,包括oom_score_adj(或oom_adj)的值、程序當前的記憶體使用量、程序的CPU使用時間(user time + system time)、程序的存活時間(uptime - start time)等因素。

  • 系統在決定哪個程序應該被OOM killer終結時,會參考這個分數,分數越高,表示該程序在記憶體壓力下被選中殺死的機率越大。透過觀察這個值,管理員可以瞭解核心是如何評估各個程序的記憶體壓力狀況的。

二、容器的記憶體限制

容器可以使用的記憶體為:實體記憶體交換空間(Swap)

1、實現原理

Docker實際上是使用Linux Cgroups中的Memory Subsystem來實現的對記憶體資源的限制。

對於每個容器建立後,Docker都會在Linux Cgroups的Memory Subsystem中建立一個Control Group,並將容器中的所有程序都加入到這個cgroup中。之後Docker就可以透過對該cgroup的Memory Subsystem相關引數進行調整來實現對容器進行記憶體資源的限制了。

對於記憶體限制,Docker透過memory.limit_in_bytes屬性來設定容器可用的最大記憶體容量,一旦容器達到這個限制,根據設定的不同,可能被終止或受到其他型別的約束。

2、命令格式及指令引數

2.1、命令格式

dcoker run [options]
#執行容器時設定
docker update [options]
#容器執行後修改

2.2、指令引數

引數 描述
-m 或 -memory= 容器可以使用的最大記憶體量。單位可以是b, k, m, g。允許的最小值為4m(4MB)。
--memory-swap 容器可以使用的交換分割槽和實體記憶體大小總和,必須要在設定了實體記憶體限制的前提才能設定交換分割槽的限制。如果該引數設定未-1,則容器可以使用主機上swap的最大空間
--memory-swappiness 設定容器使用交換分割槽的傾向性,值越高表示越傾向於使用swap分割槽,範圍為0-100,0為能不用就不用,100為能用就用。
--kernel-memory 容器可以使用的最大核心記憶體量,最小為4m,由於核心記憶體與使用者空間記憶體隔離,因此無法與使用者空間記憶體直接交換,因此核心記憶體不足的容器可能會阻塞宿主機主機資源,這會對主機和其他容器或者其他服務程序產生影響,因此不要設定核心記憶體大小
--memory-reservation 允許指定小於--memory的軟限制當Docker檢測到主機上的爭用或記憶體不足時會啟用該限制,如果使用--memory-reservation,則必須將其設定為低於--memory才能使其優先。因為它是軟限制,所以不能保證容器不超過限制。
--oom-kill-disable 預設情況下,發生OOM時kernel會殺死容器內程序,但是可以使用該引數可以禁止oom發生在指定的容器上,僅在已設定-m選項的容器上禁用oom,如果-m引數未配置,產生oom時主機為了釋放記憶體還會殺死程序

3、案例

如果一個容器未作記憶體使用限制,則該容器可以利用到系統記憶體最大空間,預設建立的容器沒有做記憶體資源限制

3.1、拉取容器壓測工具映象

docker pull lorel/docker-stress-ng

docker run -it --rm lorel/docker-stress-ng -help
#檢視docker-stress-ng的用法

3.2、文件的Example選項說明

stress-ng --cpu 8 --io 4 --vm 2 --vm-bytes 128M --fork 4 --timeout 10s
  • -c N, --cpu N 啟動 N 個子程序( cpu )
  • --vm N 啟動 N 個程序對記憶體進行壓測
  • --vm-bytes 128M 每個子程序使用多少記憶體(預設 256M )

3.3、測試記憶體使用限制

docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress --vm 2
#限制記憶體最多使用256M

限制記憶體使用最多256M

開啟壓測啟動兩個程序,每個程序使用256M

3.4、驗證

docker stats stress

可以看到,無論啟動多少個使用256M的程序做壓測(這裡啟動了2個程序,按理會使用512MB記憶體),stress容器的最大記憶體使用量始終維持在256MB。

3.5、記憶體軟限制

記憶體軟限制不會真正限制到記憶體的使用

docker run -it --rm -m 256m --memory-reservation 128m --name test1 lorel/docker-stress-ng --vm 2 --vm-bytes 256m
docker stats
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O         PIDS
0ffb4b8fdbde   test1     174.52%   255.9MiB / 256MiB   99.95%    648B / 0B   5.33GB / 18.1GB   5
  • -m 256m:這是記憶體的硬限制(hard limit),容器使用的記憶體不能超過這個值。
  • --memory-reservation 128m:這是記憶體的軟限制(soft limit),嘗試保證至少有這麼多記憶體可用給容器,但實際上容器可以使用超過這個值直到達到硬限制,前提是系統中有足夠的空閒記憶體。

3.6、交換分割槽限制

docker run -it --rm -m 256m --memory-swap 512m --name test1 lorel/docker-stress-ng --vm 2 --vm-bytes 256m

容器可以使用的交換分割槽和實體記憶體大小總和

必須要在設定了實體記憶體限制的前提才能設定交換分割槽的限制。

4、擴大記憶體限制

cat /sys/fs/cgroup/memory/memory.limit_in_bytes
9223372036854771712
#透過修改cgroup檔案擴大記憶體限制,縮小會報錯

三、容器的CPU限制

1、實現原理

Docker實際上是使用Linux Cgroups中的CPU Subsystem來實現的對CPU資源的限制。

Docker 會為每個容器在CPU Subsystem中建立一個Control Group,並且將該容器中的所有程序都加入到該cgroup中。之後Docker就可以透過對該cgroup的CPU Subsystem相關引數進行調整來實現對容器進行 CPU 資源的限制了。限制命令中的–cpu-shares引數,實際上就是在配置CPU Subsystem中某個容器所對應的Control Group中引數cpu.shares的值。

2、命令格式及指令引數

2.1、命令格式

dcoker run [options]
#執行容器時設定
docker update [options]
#容器執行後修改

2.2、指令引數

引數 描述
--cpus 指定容器可以使用的CPU資源比例。例如,在雙核CPU主機上設定--cpus=1.5,容器理論上可使用相當於1.5個CPU的資源。若為4核,可跨核心分配,總使用量不超過1.5核心。
--cpu-period 設定CPU排程週期(單位微秒),通常與--cpu-quota搭配使用,以限制容器在每個週期內的CPU使用量。
--cpu-quota 在給定的--cpu-period內,設定容器可以使用的CPU時間(單位微秒)。與--cpu-period配合,實現CPU使用量的絕對控制。
--cpuset-cpus 允許指定容器可執行在哪些CPU核心上,實現CPU繫結(“綁核”),提高CPU訪問效率或滿足特定隔離需求。
--cpuset-mems 僅對具有非統一記憶體訪問(NUMA)架構的系統有效,用於指定容器可使用的記憶體節點,最佳化記憶體訪問速度。
--cpu-shares 設定容器的CPU份額權重,與其他容器共享CPU資源時,權重高的容器將獲得更多的CPU時間。預設值為1024,最大值為262144。

3、案例

3.1、啟動一個程序,佔用8核CPU

docker run -it --rm --name test1 lorel/docker-stress-ng --vm 1 --cpu 8 

docker stats

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O        PIDS
7eb5882b9379   test1     812.96%   1.387GiB / 1.781GiB   77.88%    648B / 0B   4.02GB / 412MB   25

3.2、限制容器CPU

docker run -it --rm  --cpus 4 --name test1 lorel/docker-stress-ng --vm 1 --cpu 8 

docker stats

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O        PIDS
0a1c3805e5c9   test1     398.99%   1.414GiB / 1.781GiB   79.40%    648B / 0B   1.14GB / 127MB   25

top

top - 21:19:53 up  2:33,  2 users,  load average: 12.50, 9.48, 4.62
Tasks: 175 total,  17 running, 158 sleeping,   0 stopped,   0 zombie
%Cpu0  : 52.1 us,  0.3 sy,  0.0 ni, 45.5 id,  1.7 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu1  : 49.8 us,  1.4 sy,  0.0 ni, 15.2 id, 32.5 wa,  0.0 hi,  1.0 si,  0.0 st
%Cpu2  : 48.6 us,  1.7 sy,  0.0 ni,  6.2 id, 41.4 wa,  0.0 hi,  2.1 si,  0.0 st
%Cpu3  : 49.1 us,  2.1 sy,  0.0 ni,  8.0 id, 39.4 wa,  0.0 hi,  1.4 si,  0.0 st
%Cpu4  : 51.6 us,  0.7 sy,  0.0 ni, 46.0 id,  1.4 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu5  : 48.4 us,  2.4 sy,  0.0 ni,  1.4 id, 46.3 wa,  0.0 hi,  1.4 si,  0.0 st
%Cpu6  : 50.2 us,  1.0 sy,  0.0 ni, 23.9 id, 24.2 wa,  0.0 hi,  0.7 si,  0.0 st
%Cpu7  : 45.3 us,  5.9 sy,  0.0 ni, 21.1 id, 26.6 wa,  0.0 hi,  1.0 si,  0.0 st

3.3、將容器執行到指定的cpu上

docker run -it --rm  --cpus 2 --cpuset-cpus 1,3 --name test1 lorel/docker-stress-ng --vm 1 --cpu 8 

docker stats

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O         PIDS
ee11d834dde5   test1     186.68%   1.488GiB / 1.781GiB   83.60%    648B / 0B   44.8GB / 95.7MB   25

top

top - 21:27:31 up  2:41,  2 users,  load average: 14.97, 9.00, 5.77
Tasks: 176 total,  19 running, 157 sleeping,   0 stopped,   0 zombie
%Cpu0  :  0.0 us,  0.3 sy,  0.0 ni, 99.0 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
%Cpu1  : 87.5 us, 10.9 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi,  1.7 si,  0.0 st
%Cpu2  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu3  : 32.9 us, 46.1 sy,  0.0 ni,  0.0 id,  0.0 wa,  0.0 hi, 21.1 si,  0.0 st
%Cpu4  :  0.0 us, 17.3 sy,  0.0 ni, 82.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu5  :  0.0 us, 12.7 sy,  0.0 ni, 79.2 id,  0.0 wa,  0.0 hi,  8.2 si,  0.0 st
%Cpu6  :  0.0 us,  0.0 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.3 si,  0.0 st
%Cpu7  :  0.0 us,  0.3 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

3.4、基於cpu-shares對cpu進行切分

docker run -it --rm  -d --cpu-shares 1000 --name test1 lorel/docker-stress-ng --vm 1 --cpu 4 

docker run -it --rm  -d --cpu-shares 500 --name test2 lorel/docker-stress-ng --vm 1 --cpu 4 

docker stats

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O     BLOCK I/O       PIDS
d6dd34edb722   test1     543.41%   819.6MiB / 1.781GiB   44.95%    648B / 0B   102MB / 154MB   13
154b07a94e2f   test2     241.15%   711.1MiB / 1.781GiB   39.00%    648B / 0B   406MB / 145MB

四、容器的塊I/O頻寬限制

塊I/O頻寬(Block I/O Bandwidth,Blkio)用於管理容器在讀寫磁碟時的吞吐量,Docker可透過設定權重、限制每秒位元組數(B/s)和每秒I/O次數(IO/s)的方式控制容器讀寫盤的頻寬。確保各容器之間以及與宿主機的資源使用保持合理平衡

1、實現原理

Docker利用cgroups的blkio子系統,透過修改特定cgroup目錄下的控制檔案來實施這些限制。這些檔案通常位於/sys/fs/cgroup/blkio/目錄下,具體路徑會包含容器的唯一cgroup路徑,如/sys/fs/cgroup/blkio/docker/<container-id>/...

  • 權重分配:透過修改blkio.weight檔案。值範圍為10到1000,值越大,優先順序越高。
  • 每秒位元組數限制:
    • 讀限制:blkio.throttle.read_bps_device,格式為<major>:<minor> <bytes_per_second>
    • 寫限制:blkio.throttle.write_bps_device,格式相同。
  • 每秒I/O操作限制:
    • 讀IOPS限制:blkio.throttle.read_iops_device,格式同上。
    • 寫IOPS限制:blkio.throttle.write_iops_device,格式相同。

2、指令格式及指令引數

2.1、指令格式

dcoker run [options]
#執行容器時設定
docker update [options]
#容器執行後修改

2.2、指令引數

指令引數 描述
--blkio-weight 設定容器塊I/O頻寬權重,影響容器相對於其他容器的I/O排程優先順序。預設值為500。
--device-read-bps 限制容器對指定裝置的讀取速率,單位可以是kbps, mbps, gbps等。
--device-write-bps 限制容器對指定裝置的寫入速率,用法同上。
--device-read-iops 限制容器對指定裝置的每秒讀取I/O次數。
--device-write-iops 限制容器對指定裝置的每秒寫入I/O次數。

3、案例

3.1、建立兩個不同塊I/O頻寬權重的容器

docker run -itd --name io1 --blkio-weight 200 centos:7
docker run -itd --name io2 --blkio-weight 600 centos:7

3.2、限制讀取速率

docker run -it --device-read-bps /dev/sda:1mb centos:7

限制對/dev/sda裝置的讀取速率為每秒1MB

3.3、限制寫入速率

docker run -it --device-write-bps /dev/sda:2mb centos:7

限制對/dev/sda裝置的寫入速率為每秒2MB

3.4、限制讀取IOPS(每秒I/O操作次數)

docker run -it --device-read-iops /dev/sda:1000 centos:7

限制對/dev/sda裝置的讀取每秒不能超過1000次讀I/O操作

3.5、限制寫入IOPS

docker run -it --device-write-iops /dev/sda:500 centos:7

限制對/dev/sda裝置的讀取每秒不能超過500次寫I/O操作

相關文章