這篇文章是我研究容器內存整理出的相關內容. 前後內容並沒有上下文關係, 每個知識點都可以單獨查看.
內存控制
使用這樣的命令啓動一個容器docker run -d -m 300M xxx
. 可以限制容器使用的內存最大爲300M
. 那麼docker
是如何實現容器的內存限制呢?
其實是操作系統已經做好了支持. Linux
中實現容器的兩大技術是:
- namespace: 使用不同的命名空間實現資源的隔離. 包括:
- PID: 進程隔離
- Net: 網絡環境隔離
- VFS: 文件系統隔離
- IPC: 進程通信隔離
- 等等, 可查看維基百科
- cgroups: 實現對進程資源的限制. 包括: cpu/內存/最大進程數量等等. 詳情可查看官方文檔
好, namespace
實現了多個容器間不同進程的隔離, cgroups
實現了對單個容器的資源限制. 就是這兩個技術支撐了容器化的實現.
要查看一個進程的cgroups
限制, 可查看文件/proc/<pid>/cgroup
. 如果是一個容器進程, 你會看到類似於這樣的內容:
...
10:memory:/docker/<docker_id>
9:cpuset:/docker/<docker_id>
8:blkio:/docker/<docker_id>
...
將限制指向了一個路徑, 這個路徑存放在/sys/fs/cgroup
這裏, 比如memory
的限制路徑爲: /sys/fs/cgroup/memory/docker/<docker_id>
. 在這裏能夠看到對此進程的所有內存限制. 其中每個文件的含義在官方文檔中也有說明.
至於具體的原理, 這裏不做深究.
這裏額外說一點, /sys/fs/cgroup
是一個樹形結構, 子控制組的資源限制必定小於等於父控制組.
OOM Kill
如果一個進程的內存使用超過了限制, 會發生什麼呢?
隨便寫一個腳本實驗一下, 就會發現進程突然消失了, 被操作系統殺掉了.
使用docker inspect
命令查看, 發現OOMKilled
的值爲true
. 或者直接查看系統日誌/sys/log/message
也能夠看到進程被殺掉的log.
Page Cache
我們可能會碰到這樣奇怪的現象, 容器的內存限制爲200M
, 且已經使用200M
內存, 此時再啓動一個進程申請20M
內存仍然成功, 且申請後總的內存仍然是200M
.
造成這個奇怪現象的原因, 是因爲在調用函數read
讀取文件的時候, 會將文件臨時存放在內存中, 以加速後續讀取. 我們使用free
命令查看時, 其中的buff/cache
就是文件的緩存, RSS
則是實際使用的物理內存, VIRT
則是進程申請的虛擬內存. 某個進程的具體內存分佈可查看文件proc/<pid>/smaps
.
容器RSS
和buff/cache
的和, 就是容器實際使用的物理內存總值. 應該與cgroup
路徑下的memory.usage_in_bytes
值相同. (容器的內存分佈也可以查看文件memory.stat
)
這麼一說, 這個奇怪的現象是不是就可以解釋了? 當內存不足的時候, 系統會回收文件緩存以供進程使用.
交換內存
如果容器開啓了交換內存, 你就會驚奇的發現, memory cgroup
限制失效了. 容器申請了超過限制的內存仍然可以繼續執行, 只不過部分內存被交換到磁盤上了.
如果同時又Page Cache
和交換內存, 操作系統優先選擇哪一個呢? 可以通過修改memory.swappiness
的值來修改優先級, 其值爲0-100
, 值越大, 使用交換內存的概率越大. 當值爲100時, 則Page Cache
與叫換內存的概率相同. 官網介紹
不過一般啓動容器的時候, 都是將swap
關閉的, 應該沒什麼需要開啓的場景吧.
還有一些內存相關的其他知識點, 大部分都可查看memory group
的官方文檔