一、簡述:
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的時候,這個時候就相當於服務器重啟了
