Cgroup限制內存使用


一、簡述:

1、不設置

如果不設置-m,--memory--memory-swap,容器默認可以用完宿舍機的所有內存和 swap 分區。不過注意,如果容器占用宿主機的所有內存和 swap 分區超過一段時間后,會被宿主機系統殺死

2、設置-m,--memory,不設置--memory-swap

如果在容器中運行一個一直不停申請內存的程序,你會觀察到該程序最終能占用的內存大小為 2a。

比如$ docker run -m 1G ubuntu:16.04,該容器能使用的內存大小為 1G,能使用的 swap 分區大小也為 1G。容器內的進程能申請到的總內存大小為 2G。

3、Memory reservation

這種 memory reservation 機制不知道怎么翻譯比較形象。Memory reservation 是一種軟性限制,用於節制容器內存使用。給--memory-reservation設置一個比-m小的值后,雖然容器最多可以使用-m使用的內存大小,但在宿主機內存資源緊張時,在系統的下次內存回收時,系統會回收容器的部分內存頁,強迫容器的內存占用回到--memory-reservation設置的值大小。

docker run -it -m 500M --memory-reservation 200M ubuntu:16.04 /bin/bash

如果容器使用了大於 200M 但小於 500M 內存時,下次系統的內存回收會嘗試將容器的內存鎖緊到 200M 以下。

4、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 -m 100M --oom-kill-disable ubuntu:16.04 /bin/bash

 二、案例

OOM排查

背景:

            微服務架構,幾百個服務,運行在不同的容器上,總是莫名的同時出現十幾個服務不可用,伴隨着各個容器的狀態異常,無法ping通,無法ssh上去,大量告警。。。總是莫名的有物理機宕機,每次查的時候總是無疾而終

1、大量的容器無法ping通,登錄上主機,查看資源搶占情況*(示例圖):

 

 

在以上的圖中,主要看四個指標,一個是load,Emmm,假設CPU是56個,實際的load值達到了3K,那么這個時候,系統已經不堪重負了;

一個是Tasks,主要看任務的運行數量,在圖中,才2個在運行,實際也就幾百個,但是sleeping狀態的有幾萬個,而zombie狀態的有300多個;

一個就是內存的使用量,在實際情況中,內存基本使用完畢;最后一個就是使用交換空間,圖中的未使用,實際上已經全部使用完畢

CPU的load太高,那么說起來其實也就兩個隊列,一個是運行的隊列,一個block的隊列,從而需要收集相關的進程信息,從而可以使用ps來查看進程的狀態信息

1、查看R狀態,D狀態和Z狀態的進程

ps h -eo pid,state,ucmd,start | awk '{if ($2=="D"||$2=="R"){print $0}}' | grep -v ps| sort | uniq -c |sort -nr -k1

2、查看所有R狀態,D狀態的線程

ps -eL h o pid,state,ucmd | awk '{if ($2=="D"||$2=="R"){print $0}}' | grep -v ps| sort | uniq -c |sort -nr -k1

-e 查看所有進程,等價於-A

-o 自定義輸出格式

-f  全部列出,列出每個進程更詳細的信息

-L 顯示線程

 

 以上主要是為了統計:一個是進程的數量,一個是線程的數量。。。(需要多次執行,從而進行對比,可以知道哪些進程造成了相關的阻塞,數量龐大的必有蹊蹺。。),統計的結果是大量狀態為D的進程。。。在線程多的結果中,可以看到相關的PID,從而可以知道是哪個進程產生了大量的阻塞

統計容器的數量,從容器的內存限制來查看是否容器的內存都達到了限制

排查相關的日志:

 

在日志里面查看到大量的OOM信息,也就可以看到相關的進程被殺死,從而可以找到是哪些進程導致了內存泄漏,從而進行整改。。。在kern日志中可以看到被殺死的進程名,在dmesg中可以看到OOM,在message中,能追查到相關的容器id

總結:

排查思路:根據load值偏高,查詢進程的數量和線程的數量,從而從增加的數量查看到是什么樣的進程阻塞了CPU的調度,查看系統日志,主要查看oom,從而對比兩者的結果進程是否一致,從而看哪個進程OOM需要整改。。

疑問:

在以上的問題追蹤中,可以產生兩個疑點:第一既然oom都殺死了進程,為什么內存還會溢出,殺死了進程應該已經將相關的內存進行回收了;第二:是什么導致了那么高的load值

回答第一個問題就是:在oom killer進行殺死進程的時候,使用的是kill -9 ,從而能強行殺死進程,但是在進行oom的時候,oom的分值是給占用內存大的進程,而這個進程在等待IO,也就是等待分配內存,Emm。。。讀取內存也是一種IO,所謂的缺頁中斷。。。在殺死這個進程的時候,這個進程的狀態為D,也就是表示這個進程是不可中斷睡眠,在等待分配內存。。。從而殺死這個進程可能根本就無法殺死。。。

       還有一種情況是,進程已經變成了僵屍進程,從而在oom killer在進行殺死進程的時候,根據當前的進程號id殺,而僵屍進程要想殺死,必須殺掉其父進程,而當僵屍進程的父進程為1的時候,這個時候就相當於服務器重啟了


免責聲明!

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



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