Docker和容器內進程內存計算差別
生產環境上了大規模容器集群后,肯定需要對容器進行監控,容器的編排我們采用了Kubernetes,監控前期采用了Weave Scope,后期采用了cAdvisor+Heapster+InfluxDB的方案,剛開始監控感覺并沒有什么異常,但是集群運行久了之后,發現監控的指標和預想的并不一樣。
我們有對應的Java應用跑在容器中,每個Container的資源限制了4C16G(看起來和虛機一樣夸張),JVM設置了Xmx2G的限制,但是發現有些容器的內存使用率一直在16G左,完全沒有釋放出來。Weave Scope和Heapster監控采集到的指標都是一樣的,但是實際登錄容器后發現JVM才用了800M不到。Weave Scope上其實可看到container和container內process的內存消耗,這個和實際登錄容器看到的是一樣的結果,如下圖(剩余的那些sleep等進程其實占內存很少,忽略不計)
我們可以用docker stats查看對應容器的資源消耗,看到內存使用率在16G左,如下圖
其實容器內部就是Linux對于內存的計算方式,我Baidu了一下發現Linux和Docker對于容器內存的計算是有差異的,Linux中buffer/cache是不計算進used memory的,所以下圖中total=free+used+buff/cache,free命令的數據來源于/proc/meminfo
Docker本身的內存要通過cgroup文件中相關數據計算,我們進入對應容器的cgroup目錄中,我們的容器以Pod為最小單位運行在Kubernetes環境中,所以路徑在/sys/fs/cgroup/memory/kubepods下,后面跟著Pod的***標識和container的***標志
- /sys/fs/cgroup/memory/kubepods/pod2534673f-68bd-11e8-9fff-325ce3dddf77/245ac9f74c34b666ba14fadab30ef51c0f6259324b4b2f100e9b6b732b4c0933
在該目錄中有一個memory.limit_in_bytes文件,這個就是Kubernetes的YAML文件通過resource.limit將值傳遞給Docker API下發的內存限制,cat查看為17179869184,單位Byte,轉換后剛好為16G;然后我們看到有一個memory.usage_in_bytes,為當前容器的內存使用量,cat查看為17179824128,轉換后15.9999G,Docker的原生監控,就是我們在用的這些Weave Scope和Heapster其實都是查詢這個memory.usage_in_bytes的值;那么我們怎么推算出實際容器中應用消耗的內存總量呢,我們需要查看該目錄下的memory.stat文件,cat看到如下內容
- [root@docker 245ac9f74c34b666ba14fadab30ef51c0f6259324b4b2f100e9b6b732b4c0933]# cat memory.stat
- cache 15749447680
- rss 817262592
- rss_huge 715128832
- mapped_file 3653632
- swap 0
- pgpgin 59308038
- pgpgout 55485716
- pgfault 97852409
- pgmajfault 1048
- inactive_anon 0
- active_anon 817238016
- inactive_file 7932751872
- active_file 7816630272
- unevictable 0
- hierarchical_memory_limit 17179869184
- hierarchical_memsw_limit 34359738368
- total_cache 15749447680
- total_rss 817262592
- total_rss_huge 715128832
- total_mapped_file 3653632
- total_swap 0
- total_pgpgin 59308038
- total_pgpgout 55485716
- total_pgfault 97852409
- total_pgmajfault 1048
- total_inactive_anon 0
- total_active_anon 817238016
- total_inactive_file 7932751872
- total_active_file 7816630272
- total_unevictable 0
對其中常用項的解釋如下表
實際Container中實際內存使用量real_used =memory.usage_in_bytes - (rss + active_file + inactive_file),但是一個resource.limit為16G的Container,JVM應用只是用了幾百兆,但監控查到使用了16G,多出的15G用在哪里。我們查詢了InfluxDB數據庫,拉出了Pod的監控歷史,發現有一個時間點,內存使用率從16G一下子掉到了11G,并且在1分鐘后上升到了16G,此時間點通過工作記錄發現是有清容器日志的操作,應用將日志通過文件的形式寫在宿主機的文件系統中,我們找了測試環境嘗試,將一個內存消耗為7G的Container的5G日志清掉,docker stats可以看到Container的內存使用率一下子掉到了4G,并且active_file和inactive_flie都有大幅度的下降,這里為什么清了日志內存使用率只掉到4G,因為我們還有其他的寫文件日志沒有清,以及Container本身輸出在Stdout的json.log。這樣的高內存使用率長達1周,但是我們發現并沒有讓容器因為OOM(Out-Of-Memory)而退出,推算出容器中的Cache其實會根據實際的內存分配量而使用,并不像程序為超額使用導致容器OOM退出。