Docker和容器內(nèi)進(jìn)程內(nèi)存計(jì)算差別
生產(chǎn)環(huán)境上了大規(guī)模容器集群后,肯定需要對(duì)容器進(jìn)行監(jiān)控,容器的編排我們采用了Kubernetes,監(jiān)控前期采用了Weave Scope,后期采用了cAdvisor+Heapster+InfluxDB的方案,剛開始監(jiān)控感覺并沒有什么異常,但是集群運(yùn)行久了之后,發(fā)現(xiàn)監(jiān)控的指標(biāo)和預(yù)想的并不一樣。
我們有對(duì)應(yīng)的Java應(yīng)用跑在容器中,每個(gè)Container的資源限制了4C16G(看起來和虛機(jī)一樣夸張),JVM設(shè)置了Xmx2G的限制,但是發(fā)現(xiàn)有些容器的內(nèi)存使用率一直在16G左,完全沒有釋放出來。Weave Scope和Heapster監(jiān)控采集到的指標(biāo)都是一樣的,但是實(shí)際登錄容器后發(fā)現(xiàn)JVM才用了800M不到。Weave Scope上其實(shí)可看到container和container內(nèi)process的內(nèi)存消耗,這個(gè)和實(shí)際登錄容器看到的是一樣的結(jié)果,如下圖(剩余的那些sleep等進(jìn)程其實(shí)占內(nèi)存很少,忽略不計(jì))
我們可以用docker stats查看對(duì)應(yīng)容器的資源消耗,看到內(nèi)存使用率在16G左,如下圖
其實(shí)容器內(nèi)部就是Linux對(duì)于內(nèi)存的計(jì)算方式,我Baidu了一下發(fā)現(xiàn)Linux和Docker對(duì)于容器內(nèi)存的計(jì)算是有差異的,Linux中buffer/cache是不計(jì)算進(jìn)used memory的,所以下圖中total=free+used+buff/cache,free命令的數(shù)據(jù)來源于/proc/meminfo
Docker本身的內(nèi)存要通過cgroup文件中相關(guān)數(shù)據(jù)計(jì)算,我們進(jìn)入對(duì)應(yīng)容器的cgroup目錄中,我們的容器以Pod為最小單位運(yùn)行在Kubernetes環(huán)境中,所以路徑在/sys/fs/cgroup/memory/kubepods下,后面跟著Pod的***標(biāo)識(shí)和container的***標(biāo)志
- /sys/fs/cgroup/memory/kubepods/pod2534673f-68bd-11e8-9fff-325ce3dddf77/245ac9f74c34b666ba14fadab30ef51c0f6259324b4b2f100e9b6b732b4c0933
在該目錄中有一個(gè)memory.limit_in_bytes文件,這個(gè)就是Kubernetes的YAML文件通過resource.limit將值傳遞給Docker API下發(fā)的內(nèi)存限制,cat查看為17179869184,單位Byte,轉(zhuǎn)換后剛好為16G;然后我們看到有一個(gè)memory.usage_in_bytes,為當(dāng)前容器的內(nèi)存使用量,cat查看為17179824128,轉(zhuǎn)換后15.9999G,Docker的原生監(jiān)控,就是我們?cè)谟玫倪@些Weave Scope和Heapster其實(shí)都是查詢這個(gè)memory.usage_in_bytes的值;那么我們?cè)趺赐扑愠鰧?shí)際容器中應(yīng)用消耗的內(nèi)存總量呢,我們需要查看該目錄下的memory.stat文件,cat看到如下內(nèi)容
- [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
對(duì)其中常用項(xiàng)的解釋如下表
實(shí)際Container中實(shí)際內(nèi)存使用量real_used =memory.usage_in_bytes - (rss + active_file + inactive_file),但是一個(gè)resource.limit為16G的Container,JVM應(yīng)用只是用了幾百兆,但監(jiān)控查到使用了16G,多出的15G用在哪里。我們查詢了InfluxDB數(shù)據(jù)庫,拉出了Pod的監(jiān)控歷史,發(fā)現(xiàn)有一個(gè)時(shí)間點(diǎn),內(nèi)存使用率從16G一下子掉到了11G,并且在1分鐘后上升到了16G,此時(shí)間點(diǎn)通過工作記錄發(fā)現(xiàn)是有清容器日志的操作,應(yīng)用將日志通過文件的形式寫在宿主機(jī)的文件系統(tǒng)中,我們找了測(cè)試環(huán)境嘗試,將一個(gè)內(nèi)存消耗為7G的Container的5G日志清掉,docker stats可以看到Container的內(nèi)存使用率一下子掉到了4G,并且active_file和inactive_flie都有大幅度的下降,這里為什么清了日志內(nèi)存使用率只掉到4G,因?yàn)槲覀冞€有其他的寫文件日志沒有清,以及Container本身輸出在Stdout的json.log。這樣的高內(nèi)存使用率長達(dá)1周,但是我們發(fā)現(xiàn)并沒有讓容器因?yàn)镺OM(Out-Of-Memory)而退出,推算出容器中的Cache其實(shí)會(huì)根據(jù)實(shí)際的內(nèi)存分配量而使用,并不像程序?yàn)槌~使用導(dǎo)致容器OOM退出。