一文詳解docker容器中的memory限制
序
本文主要研究一下docker容器的memory限制
內(nèi)存限制
docker run -m 512M -p 8081:8080 --rm docker-demo
通過(guò)-m參數(shù)指定限制的內(nèi)存大小
buffer/cache
所謂Cache,就是為了彌補(bǔ)高速設(shè)備和低速設(shè)備之間的矛盾而設(shè)立的一個(gè)中間層。 緩沖(Buffer)是根據(jù)磁盤(pán)的讀寫(xiě)設(shè)計(jì)的,它把分散的寫(xiě)操作集中進(jìn)行,減少磁盤(pán)碎片和硬盤(pán)的反復(fù)尋道,從而提高系統(tǒng)性能。
區(qū)別與聯(lián)系
- 都是為了解決速度不對(duì)等的問(wèn)題。
- 緩存(Cache)是把讀取過(guò)的數(shù)據(jù)保存起來(lái),重新讀取時(shí)若命中(找到需要的數(shù)據(jù))就不要去讀硬盤(pán)了,若沒(méi)有命中再讀硬盤(pán)。其中的數(shù)據(jù)會(huì)根據(jù)讀取頻率進(jìn)行組織,把最頻繁讀取的內(nèi)容放在最容易找到的位置,把不再讀的內(nèi)容不斷往后排,直至從中刪除。
- Buffer是即將要被寫(xiě)入磁盤(pán)的,而Cache是被從磁盤(pán)中讀出來(lái)的。
- 在應(yīng)用場(chǎng)景上,Buffer是由各種進(jìn)程分配的,被用在如輸入隊(duì)列等方面。一個(gè)簡(jiǎn)單的例子,如某個(gè)進(jìn)程要求有多個(gè)字段讀入,在所有字段被讀入完整之前,進(jìn)程把先前讀入的字段放在Buffer中保存;Cache經(jīng)常被用在磁盤(pán)的I/O請(qǐng)求上,如果有多個(gè)進(jìn)程都要訪(fǎng)問(wèn)某個(gè)文件,于是該文件便被做成 Cache以方便下次被訪(fǎng)問(wèn),這樣可提高系統(tǒng)性能。比如linux系統(tǒng)中有一個(gè)守護(hù)進(jìn)程定期清空緩沖內(nèi)容(即寫(xiě)入磁盤(pán)),也可以通過(guò) sync 命令手動(dòng)清空緩沖。
操作系統(tǒng)中的Page Cache與Buffer Cache
磁盤(pán)數(shù)據(jù)會(huì)被讀取到Page Cache進(jìn)行緩存,程序要讀取數(shù)據(jù)的時(shí)候,可以直接從Page Cache讀取,這是讀取數(shù)據(jù)的一條線(xiàn)路。 此外,當(dāng)Page Cache的數(shù)據(jù)需要刷新時(shí),Page Cache中的數(shù)據(jù)會(huì)交給Buffer Cache,而B(niǎo)uffer Cache中的所有數(shù)據(jù)都會(huì)定時(shí)刷新到磁盤(pán)。這是寫(xiě)入數(shù)據(jù)的另一條線(xiàn)。 page-cache.png
- Page Cache:Page Cache是文件系統(tǒng)層級(jí)的緩存,它從磁盤(pán)里讀取的內(nèi)容都會(huì)存儲(chǔ)到這里,這樣程序讀取磁盤(pán)內(nèi)容就會(huì)非???。例如,使用grep和find等命令查找內(nèi)容和文件時(shí),第1次會(huì)比較慢,再次執(zhí)行就快好多倍,幾乎是瞬間。
- Buffer Cache:Buffer Cache是磁盤(pán)等塊設(shè)備的緩沖,這部分內(nèi)存數(shù)據(jù)是要寫(xiě)入到磁盤(pán)的。這里需要注意,位于內(nèi)存 Buffer 中的數(shù)據(jù)不是即時(shí)寫(xiě)入磁盤(pán)的,而是系統(tǒng)空閑或者 Buffer達(dá)到一定大小統(tǒng)一寫(xiě)到磁盤(pán)中,所以斷電易失。為了防止數(shù)據(jù)丟失,最好正常關(guān)機(jī)或者多執(zhí)行幾次sync命令,讓位于Buffer上的數(shù)據(jù)立刻寫(xiě)到磁盤(pán)里。 Page Cache可以極大地提高系統(tǒng)整體性能。例如,進(jìn)程A讀一個(gè)文件,內(nèi)核空間會(huì)申請(qǐng)Page Cache與此文件對(duì)應(yīng),并記錄對(duì)應(yīng)關(guān)系,進(jìn)程B再次讀同樣的文件就會(huì)直接命中上一次的Page Cache,讀寫(xiě)速度顯著提升。但注意,Page Cache會(huì)根據(jù)LRU算法(最近最少使用)進(jìn)行替換。
實(shí)例
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實(shí)例的
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
通過(guò)/sys/fs/cgroup/memory/底下的文件查看到的就是docker實(shí)例使用的以及docker實(shí)例的內(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)計(jì)的是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)計(jì)
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命令查詢(xún)到的內(nèi)存使用為Memory WorkingSet = Memory.Usage - memory.stat[total_inactive_file]。 k8s的OOMKiller使用的是container_memory_working_set_bytes指標(biāo),其計(jì)算指標(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"
}
如果是因?yàn)閮?nèi)存原因被kill的話(huà),通過(guò)docker inspect 容器id,查看State部分,可以看到
"OOMKilled": true
小結(jié)
- docker容器的memory限制使用的是
mem.Usage - mem.Stats["inactive_file"]與limit的對(duì)比,如果超出則會(huì)被kill;free及top顯示的都是宿主機(jī)的內(nèi)存信息 - kubectl top pod命令是通過(guò)memory_working_set(
Memory.Usage - memory.stat[total_inactive_file])來(lái)統(tǒng)計(jì)容器的內(nèi)存使用 - k8s的OOMKiller使用的是container_memory_working_set_bytes指標(biāo)(
total_active_file + total_rss),如果超出該容器的limit,則會(huì)被OOMKiller銷(xiāo)毀掉
以上就是一文詳解docker容器中的memory限制的詳細(xì)內(nèi)容,更多關(guān)于docker memory限制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
如何解決docker commit后鏡像越來(lái)越大問(wèn)題
解決Docker Commit后鏡像變大問(wèn)題的方法:方法1直接打包容器并導(dǎo)入為鏡像;方法2將容器根目錄文件壓縮后導(dǎo)入為鏡像,方法1更優(yōu),在其他設(shè)備運(yùn)行時(shí)可能出現(xiàn)內(nèi)存不足錯(cuò)誤2025-03-03
docker可視化管理工具portainer忘記密碼重置教程的實(shí)現(xiàn)
本文主要介紹了docker可視化管理工具portainer忘記密碼重置教程的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-08-08
在Docker中的ubuntu中安裝Python3和Pip的問(wèn)題
這篇文章主要介紹了在Docker中的ubuntu中安裝Python3和Pip的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02
dockerfile結(jié)合go應(yīng)用程序的簡(jiǎn)單應(yīng)用代碼示例
Dockerfile文件是一個(gè)包含了指令和參數(shù)的文本文件,用于自動(dòng)化構(gòu)建Docker鏡像,這篇文章主要給大家介紹了關(guān)于dockerfile結(jié)合go應(yīng)用程序的簡(jiǎn)單應(yīng)用,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
使用docker compose安裝harbor私有倉(cāng)庫(kù)的詳細(xì)教程
harbor鏡像倉(cāng)庫(kù)是由VMware開(kāi)源的一款企業(yè)級(jí)鏡像倉(cāng)庫(kù),它包括權(quán)限管理(RBAC)、LDAP、日志審核、管理界面、自我注冊(cè)、鏡像復(fù)制等諸多功能,本文給大家介紹docker compose安裝harbor的方法,需要的朋友參考下吧2021-06-06
Docker不能綁定靜態(tài)的外網(wǎng)固定ip的問(wèn)題及解決辦法
這篇文章主要介紹了Docker不能綁定靜態(tài)的外網(wǎng)固定ip的問(wèn)題及解決辦法,需要的朋友可以參考下2017-01-01

