目錄
- 前言
- 一、使用內存限制
- 1. 不設置
- 2. 設置-m,–memory,不設置–memory-swap
- 3. 設置-m,–memory=a,–memory-swap=b,且b > a
- 4. 設置-m,–memory=a,–memory-swap=-1
- 二、Memory reservation
- 三、OOM killer
- 四、kernel-memory核心內存限制
- 五、驗證
- 總結
前言
本文介紹如何通過docker運行參數配置限制docker容器可以使用的內存上限。docker容器默認可以使用全部宿主機的所有內存和 swap 分區,比如宿主機的內存是32G,則運行一個docker容器最多可以分配到32G內存,如果啟用了多個docker容器,則很快宿主機的內存就耗盡了。
內存限制相關參數如下:
選項 |
描述 |
-m,–memory |
內存限制,格式是數字加單位,單位可以為 b,k,m,g。最小為 4M |
–memory-swap |
內存+交換分區大小總限制。格式同上。必須必-m設置的大 |
–memory-reservation |
內存的軟性限制。格式同上 |
–oom-kill-disable |
是否阻止 OOM killer 殺死容器,默認沒設置 |
–oom-score-adj |
容器被 OOM killer 殺死的優先級,范圍是[-1000, 1000],默認為 0 |
–memory-swappiness |
用于設置容器的虛擬內存控制行為。值為 0~100 之間的整數 |
–kernel-memory |
核心內存限制。格式同上,最小為 4M |
一、使用內存限制
用戶內存限制就是對容器能使用的內存和交換分區的大小作出限制。使用時要遵循兩條直觀的規則:-m,–memory選項的參數最小為 4 M。–memory-swap不是交換分區,而是內存加交換分區的總大小,所以–memory-swap必須比-m,–memory大。在這兩條規則下,一般有四種設置方式。
你可能在進行內存限制的實驗時發現 docker run命令報錯:WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.
這是因為宿主機內核的相關功能沒有打開。按照下面的設置就行。
step 1:編輯 /etc/default/grub文件,將 GRUB_CMDLINE_LINUX一行改為 GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"
step 2:更新 GRUB,即執行 $ sudo update-grub
step 3: 重啟系統。
1. 不設置
如果不設置-m,–memory和–memory-swap,容器默認可以用完宿舍機的所有內存和 swap 分區。不過注意,如果容器占用宿主機的所有內存和 swap 分區超過一段時間后,會被宿主機系統殺死(如果沒有設置–00m-kill-disable=true的話)。
2. 設置-m,–memory,不設置–memory-swap
給-m或–memory設置一個不小于 4M 的值,假設為 a,不設置–memory-swap,或將–memory-swap設置為 0。這種情況下,容器能使用的內存大小為 a,能使用的交換分區大小也為 a。因為 Docker 默認容器交換分區的大小和內存相同。
如果在容器中運行一個一直不停申請內存的程序,你會觀察到該程序最終能占用的內存大小為 2a。
比如$ docker run -m 1G ubuntu:16.04,該容器能使用的內存大小為 1G,能使用的 swap 分區大小也為 1G。容器內的進程能申請到的總內存大小為 2G。
3. 設置-m,–memory=a,–memory-swap=b,且b > a
給-m設置一個參數 a,給–memory-swap設置一個參數 b。a 時容器能使用的內存大小,b是容器能使用的 內存大小 + swap 分區大小。所以 b 必須大于 a。b -a 即為容器能使用的 swap 分區大小。
比如$ docker run -m 1G –memory-swap 3G ubuntu:16.04,該容器能使用的內存大小為 1G,能使用的 swap 分區大小為 2G。容器內的進程能申請到的總內存大小為 3G。
4. 設置-m,–memory=a,–memory-swap=-1
給-m參數設置一個正常值,而給–memory-swap設置成 -1。這種情況表示限制容器能使用的內存大小為 a,而不限制容器能使用的 swap 分區大小。
這時候,容器內進程能申請到的內存大小為 a + 宿主機的 swap 大小。
二、Memory reservation
這種 memory reservation 機制不知道怎么翻譯比較形象。Memory reservation 是一種軟性限制,用于節制容器內存使用。給–memory-reservation設置一個比-m小的值后,雖然容器最多可以使用-m使用的內存大小,但在宿主機內存資源緊張時,在系統的下次內存回收時,系統會回收容器的部分內存頁,強迫容器的內存占用回到–memory-reservation設置的值大小。
沒有設置時(默認情況下)–memory-reservation的值和-m的限定的值相同。將它設置為 0 會設置的比-m的參數大 等同于沒有設置。
Memory reservation 是一種軟性機制,它不保證任何時刻容器使用的內存不會超過–memory-reservation限定的值,它只是確保容器不會長時間占用超過–memory-reservation限制的內存大小。
例如:
$ docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash1
如果容器使用了大于 200M 但小于 500M 內存時,下次系統的內存回收會嘗試將容器的內存鎖緊到 200M 以下。
例如:
$ docker run -it--memory-reservation 1G ubuntu:16.04 /bin/bash1
容器可以使用盡可能多的內存。–memory-reservation確保容器不會長時間占用太多內存。
三、OOM killer
默認情況下,在出現 out-of-memory(OOM) 錯誤時,系統會殺死容器內的進程來獲取更多空閑內存。這個殺死進程來節省內存的進程,我們姑且叫它 OOM killer。我們可以通過設置–oom-kill-disable選項來禁止 OOM killer 殺死容器內進程。但請確保只有在使用了-m/–memory選項時才使用–oom-kill-disable禁用 OOM killer。如果沒有設置-m選項,卻禁用了 OOM-killer,可能會造成出現 out-of-memory 錯誤時,系統通過殺死宿主機進程或獲取更改內存。
下面的例子限制了容器的內存為 100M 并禁止了 OOM killer:
$ docker run -it-m100M --oom-kill-disable ubuntu:16.04 /bin/bash1
是正確的使用方法。
而下面這個容器沒設置內存限制,卻禁用了 OOM killer 是非常危險的:
$ docker run -it--oom-kill-disable ubuntu:16.04 /bin/bash1
容器沒用內存限制,可能或導致系統無內存可用,并嘗試時殺死系統進程來獲取更多可用內存。
一般一個容器只有一個進程,這個唯一進程被殺死,容器也就被殺死了。我們可以通過–oom-score-adj選項來設置在系統內存不夠時,容器被殺死的優先級。負值更教不可能被殺死,而正值更有可能被殺死。
四、kernel-memory核心內存限制
核心內存和用戶內存不同的地方在于核心內存不能被交換出。不能交換出去的特性使得容器可以通過消耗太多內存來堵塞一些系統服務。核心內存包括:
- stack pages(棧頁面)
- slab pages
- socket memory pressure
- tcp memory pressure
可以通過設置核心內存限制來約束這些內存。例如,每個進程都要消耗一些棧頁面,通過限制核心內存,可以在核心內存使用過多時阻止新進程被創建。
核心內存和用戶內存并不是獨立的,必須在用戶內存限制的上下文中限制核心內存。
假設用戶內存的限制值為 U,核心內存的限制值為 K。有三種可能地限制核心內存的方式:
- U != 0,不限制核心內存。這是默認的標準設置方式
- K < U,核心內存時用戶內存的子集。這種設置在部署時,每個 cgroup 的內存總量被過度使用。過度使用核心內存限制是絕不推薦的,因為系統還是會用完不能回收的內存。在這種情況下,你可以設置 K,這樣 groups 的總數就不會超過總內存了。然后,根據系統服務的質量自有地設置 U。
- K > U,因為核心內存的變化也會導致用戶計數器的變化,容器核心內存和用戶內存都會觸發回收行為。這種配置可以讓管理員以一種統一的視圖看待內存。對想跟蹤核心內存使用情況的用戶也是有用的。
例如:
$ docker run -it -m 500M --kernel-memory 50M ubuntu:16.04 /bin/bash1
容器中的進程最多能使用 500M 內存,在這 500M 中,最多只有 50M 核心內存。
$ docker run -it--kernel-memory 50M ubuntu:16.04 /bin/bash1
沒用設置用戶內存限制,所以容器中的進程可以使用盡可能多的內存,但是最多能使用 50M 核心內存。
五、驗證
可以通過docker stats命令查看docker容器的內存使用情況,來驗證內存設置是否生效。可以看到容器默認可使用的最大內存上限與宿主機內存一致,設置–memory 1G參數后,最大可以使用的內存被限制為1G。
docker stats