一文詳解docker容器中的memory限制
序
本文主要研究一下docker容器的memory限制
內(nèi)存限制
docker run -m 512M -p 8081:8080 --rm docker-demo
通過-m參數(shù)指定限制的內(nèi)存大小
buffer/cache
所謂Cache,就是為了彌補(bǔ)高速設(shè)備和低速設(shè)備之間的矛盾而設(shè)立的一個中間層。 緩沖(Buffer)是根據(jù)磁盤的讀寫設(shè)計的,它把分散的寫操作集中進(jìn)行,減少磁盤碎片和硬盤的反復(fù)尋道,從而提高系統(tǒng)性能。
區(qū)別與聯(lián)系
- 都是為了解決速度不對等的問題。
- 緩存(Cache)是把讀取過的數(shù)據(jù)保存起來,重新讀取時若命中(找到需要的數(shù)據(jù))就不要去讀硬盤了,若沒有命中再讀硬盤。其中的數(shù)據(jù)會根據(jù)讀取頻率進(jìn)行組織,把最頻繁讀取的內(nèi)容放在最容易找到的位置,把不再讀的內(nèi)容不斷往后排,直至從中刪除。
- Buffer是即將要被寫入磁盤的,而Cache是被從磁盤中讀出來的。
- 在應(yīng)用場景上,Buffer是由各種進(jìn)程分配的,被用在如輸入隊列等方面。一個簡單的例子,如某個進(jìn)程要求有多個字段讀入,在所有字段被讀入完整之前,進(jìn)程把先前讀入的字段放在Buffer中保存;Cache經(jīng)常被用在磁盤的I/O請求上,如果有多個進(jìn)程都要訪問某個文件,于是該文件便被做成 Cache以方便下次被訪問,這樣可提高系統(tǒng)性能。比如linux系統(tǒng)中有一個守護(hù)進(jìn)程定期清空緩沖內(nèi)容(即寫入磁盤),也可以通過 sync 命令手動清空緩沖。
操作系統(tǒng)中的Page Cache與Buffer Cache
磁盤數(shù)據(jù)會被讀取到Page Cache進(jìn)行緩存,程序要讀取數(shù)據(jù)的時候,可以直接從Page Cache讀取,這是讀取數(shù)據(jù)的一條線路。 此外,當(dāng)Page Cache的數(shù)據(jù)需要刷新時,Page Cache中的數(shù)據(jù)會交給Buffer Cache,而Buffer Cache中的所有數(shù)據(jù)都會定時刷新到磁盤。這是寫入數(shù)據(jù)的另一條線。 page-cache.png
- Page Cache:Page Cache是文件系統(tǒng)層級的緩存,它從磁盤里讀取的內(nèi)容都會存儲到這里,這樣程序讀取磁盤內(nèi)容就會非??臁@?,使用grep和find等命令查找內(nèi)容和文件時,第1次會比較慢,再次執(zhí)行就快好多倍,幾乎是瞬間。
- Buffer Cache:Buffer Cache是磁盤等塊設(shè)備的緩沖,這部分內(nèi)存數(shù)據(jù)是要寫入到磁盤的。這里需要注意,位于內(nèi)存 Buffer 中的數(shù)據(jù)不是即時寫入磁盤的,而是系統(tǒng)空閑或者 Buffer達(dá)到一定大小統(tǒng)一寫到磁盤中,所以斷電易失。為了防止數(shù)據(jù)丟失,最好正常關(guān)機(jī)或者多執(zhí)行幾次sync命令,讓位于Buffer上的數(shù)據(jù)立刻寫到磁盤里。 Page Cache可以極大地提高系統(tǒng)整體性能。例如,進(jìn)程A讀一個文件,內(nèi)核空間會申請Page Cache與此文件對應(yīng),并記錄對應(yīng)關(guān)系,進(jìn)程B再次讀同樣的文件就會直接命中上一次的Page Cache,讀寫速度顯著提升。但注意,Page Cache會根據(jù)LRU算法(最近最少使用)進(jìn)行替換。
實例
top(不支持docker)
top - 09:33:37 up 10 min, 0 users, load average: 0.03, 0.17, 0.18 Tasks: 4 total, 1 running, 3 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.3 hi, 0.0 si, 0.0 st MiB Mem : 1887.4 total, 463.7 free, 438.2 used, 985.6 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1303.0 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7 root 20 0 2553756 165584 16608 S 1.0 8.6 0:16.06 java 1 root 20 0 2388 756 692 S 0.0 0.0 0:00.02 sh 82 root 20 0 2388 1448 1356 S 0.0 0.1 0:00.01 sh 98 root 20 0 7980 3100 2672 R 0.0 0.2 0:00.00 top
上面顯示的mem也是宿主機(jī)的,不是docker實例的
free(不支持docker)
# free -h total used free shared buff/cache available Mem: 1.8Gi 437Mi 464Mi 2.0Mi 985Mi 1.3Gi Swap: 0B 0B 0B
這里顯示的是宿主機(jī)的,而非docker的
查看容器內(nèi)存指標(biāo)
# cat /sys/fs/cgroup/memory/memory.usage_in_bytes 240824320 # cat /sys/fs/cgroup/memory/memory.limit_in_bytes 536870912
通過/sys/fs/cgroup/memory/底下的文件查看到的就是docker實例使用的以及docker實例的內(nèi)存限制
docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS 7f2f15949afc practical_spence 0.75% 141.8MiB / 512MiB 27.70% 2.23kB / 0B 0B / 0B 45
docker status這里的MEM USAGE統(tǒng)計的是mem.Usage - mem.Stats["inactive_file"]
// calculateMemUsageUnixNoCache calculate memory usage of the container. // Cache is intentionally excluded to avoid misinterpretation of the output. // // On cgroup v1 host, the result is `mem.Usage - mem.Stats["total_inactive_file"]` . // On cgroup v2 host, the result is `mem.Usage - mem.Stats["inactive_file"] `. // // This definition is consistent with cadvisor and containerd/CRI. // * https://github.com/google/cadvisor/commit/307d1b1cb320fef66fab02db749f07a459245451 // * https://github.com/containerd/cri/commit/6b8846cdf8b8c98c1d965313d66bc8489166059a // // On Docker 19.03 and older, the result was `mem.Usage - mem.Stats["cache"]`. // See https://github.com/moby/moby/issues/40727 for the background. func calculateMemUsageUnixNoCache(mem types.MemoryStats) float64 { // cgroup v1 if v, isCgroup1 := mem.Stats["total_inactive_file"]; isCgroup1 && v < mem.Usage { return float64(mem.Usage - v) } // cgroup v2 if v := mem.Stats["inactive_file"]; v < mem.Usage { return float64(mem.Usage - v) } return float64(mem.Usage) } func calculateMemPercentUnixNoCache(limit float64, usedNoCache float64) float64 { // MemoryStats.Limit will never be 0 unless the container is not running and we haven't // got any data from cgroup if limit != 0 { return usedNoCache / limit * 100.0 } return 0 }
k8s中統(tǒng)計
func decodeMemory(target *resource.Quantity, memStats *stats.MemoryStats) error { if memStats == nil || memStats.WorkingSetBytes == nil { return fmt.Errorf("missing memory usage metric") } *target = *uint64Quantity(*memStats.WorkingSetBytes, 0) target.Format = resource.BinarySI return nil } func setMemoryStats(s *cgroups.Stats, ret *info.ContainerStats) { ret.Memory.Usage = s.MemoryStats.Usage.Usage ret.Memory.MaxUsage = s.MemoryStats.Usage.MaxUsage ret.Memory.Failcnt = s.MemoryStats.Usage.Failcnt if s.MemoryStats.UseHierarchy { ret.Memory.Cache = s.MemoryStats.Stats["total_cache"] ret.Memory.RSS = s.MemoryStats.Stats["total_rss"] ret.Memory.Swap = s.MemoryStats.Stats["total_swap"] ret.Memory.MappedFile = s.MemoryStats.Stats["total_mapped_file"] } else { ret.Memory.Cache = s.MemoryStats.Stats["cache"] ret.Memory.RSS = s.MemoryStats.Stats["rss"] ret.Memory.Swap = s.MemoryStats.Stats["swap"] ret.Memory.MappedFile = s.MemoryStats.Stats["mapped_file"] } if v, ok := s.MemoryStats.Stats["pgfault"]; ok { ret.Memory.ContainerData.Pgfault = v ret.Memory.HierarchicalData.Pgfault = v } if v, ok := s.MemoryStats.Stats["pgmajfault"]; ok { ret.Memory.ContainerData.Pgmajfault = v ret.Memory.HierarchicalData.Pgmajfault = v } workingSet := ret.Memory.Usage if v, ok := s.MemoryStats.Stats["total_inactive_file"]; ok { if workingSet < v { workingSet = 0 } else { workingSet -= v } } ret.Memory.WorkingSet = workingSet }
kubectl top pod命令查詢到的內(nèi)存使用為Memory WorkingSet = Memory.Usage - memory.stat[total_inactive_file]。 k8s的OOMKiller使用的是container_memory_working_set_bytes指標(biāo),其計算指標(biāo)如下:
container_memory_working_set_bytes = container_memory_usage_bytes - total_inactive_file = total_cache + total_rss - total_inactive_file = total_inactive_file + total_active_file + total_rss - total_inactive_file = total_active_file + total_rss
oom killed
"State": { "Status": "exited", "Running": false, "Paused": false, "Restarting": false, "OOMKilled": true, "Dead": false, "Pid": 0, "ExitCode": 137, "Error": "", "StartedAt": "2024-04-08T08:34:58.271711439Z", "FinishedAt": "2024-04-08T08:35:57.360091044Z" }
如果是因為內(nèi)存原因被kill的話,通過docker inspect 容器id,查看State部分,可以看到
"OOMKilled": true
小結(jié)
- docker容器的memory限制使用的是
mem.Usage - mem.Stats["inactive_file"]
與limit的對比,如果超出則會被kill;free及top顯示的都是宿主機(jī)的內(nèi)存信息 - kubectl top pod命令是通過memory_working_set(
Memory.Usage - memory.stat[total_inactive_file]
)來統(tǒng)計容器的內(nèi)存使用 - k8s的OOMKiller使用的是container_memory_working_set_bytes指標(biāo)(
total_active_file + total_rss
),如果超出該容器的limit,則會被OOMKiller銷毀掉
以上就是一文詳解docker容器中的memory限制的詳細(xì)內(nèi)容,更多關(guān)于docker memory限制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
docker可視化管理工具portainer忘記密碼重置教程的實現(xiàn)
本文主要介紹了docker可視化管理工具portainer忘記密碼重置教程的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08在Docker中的ubuntu中安裝Python3和Pip的問題
這篇文章主要介紹了在Docker中的ubuntu中安裝Python3和Pip的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02dockerfile結(jié)合go應(yīng)用程序的簡單應(yīng)用代碼示例
Dockerfile文件是一個包含了指令和參數(shù)的文本文件,用于自動化構(gòu)建Docker鏡像,這篇文章主要給大家介紹了關(guān)于dockerfile結(jié)合go應(yīng)用程序的簡單應(yīng)用,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03使用docker compose安裝harbor私有倉庫的詳細(xì)教程
harbor鏡像倉庫是由VMware開源的一款企業(yè)級鏡像倉庫,它包括權(quán)限管理(RBAC)、LDAP、日志審核、管理界面、自我注冊、鏡像復(fù)制等諸多功能,本文給大家介紹docker compose安裝harbor的方法,需要的朋友參考下吧2021-06-06Docker不能綁定靜態(tài)的外網(wǎng)固定ip的問題及解決辦法
這篇文章主要介紹了Docker不能綁定靜態(tài)的外網(wǎng)固定ip的問題及解決辦法,需要的朋友可以參考下2017-01-01