聊聊docker容器的memory限制

本文主要研究一下docker容器的memory限制

内存限制

docker run -m 512M -p 8081:8080 --rm docker-demo

通过-m参数指定限制的内存大小

buffer/cache

所谓Cache,就是为了弥补高速设备和低速设备之间的矛盾而设立的一个中间层。
缓冲(Buffer)是根据磁盘的读写设计的,它把分散的写操作集中进行,减少磁盘碎片和硬盘的反复寻道,从而提高系统性能。

区别与联系

  • 都是为了解决速度不对等的问题。
  • 缓存(Cache)是把读取过的数据保存起来,重新读取时若命中(找到需要的数据)就不要去读硬盘了,若没有命中再读硬盘。其中的数据会根据读取频率进行组织,把最频繁读取的内容放在最容易找到的位置,把不再读的内容不断往后排,直至从中删除。
  • Buffer是即将要被写入磁盘的,而Cache是被从磁盘中读出来的。
  • 在应用场景上,Buffer是由各种进程分配的,被用在如输入队列等方面。一个简单的例子,如某个进程要求有多个字段读入,在所有字段被读入完整之前,进程把先前读入的字段放在Buffer中保存;Cache经常被用在磁盘的I/O请求上,如果有多个进程都要访问某个文件,于是该文件便被做成 Cache以方便下次被访问,这样可提高系统性能。比如linux系统中有一个守护进程定期清空缓冲内容(即写入磁盘),也可以通过 sync 命令手动清空缓冲。

操作系统中的Page Cache与Buffer Cache

磁盘数据会被读取到Page Cache进行缓存,程序要读取数据的时候,可以直接从Page Cache读取,这是读取数据的一条线路。 此外,当Page Cache的数据需要刷新时,Page Cache中的数据会交给Buffer Cache,而Buffer Cache中的所有数据都会定时刷新到磁盘。这是写入数据的另一条线。
page-cache.png

  • Page Cache:Page Cache是文件系统层级的缓存,它从磁盘里读取的内容都会存储到这里,这样程序读取磁盘内容就会非常快。例如,使用grep和find等命令查找内容和文件时,第1次会比较慢,再次执行就快好多倍,几乎是瞬间。
  • Buffer Cache:Buffer Cache是磁盘等块设备的缓冲,这部分内存数据是要写入到磁盘的。这里需要注意,位于内存 Buffer 中的数据不是即时写入磁盘的,而是系统空闲或者 Buffer达到一定大小统一写到磁盘中,所以断电易失。为了防止数据丢失,最好正常关机或者多执行几次sync命令,让位于Buffer上的数据立刻写到磁盘里。
    Page Cache可以极大地提高系统整体性能。例如,进程A读一个文件,内核空间会申请Page Cache与此文件对应,并记录对应关系,进程B再次读同样的文件就会直接命中上一次的Page Cache,读写速度显著提升。但注意,Page Cache会根据LRU算法(最近最少使用)进行替换。

实例

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也是宿主机的,不是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

这里显示的是宿主机的,而非docker的

查看容器内存指标

# 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实例的内存限制

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统计的是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
}

https://github.com/docker/cli/blob/master/cli/command/container/stats_helpers.go

k8s中统计

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命令查询到的内存使用为Memory WorkingSet = Memory.Usage - memory.stat[total_inactive_file]。
k8s的OOMKiller使用的是container_memory_working_set_bytes指标,其计算指标如下:

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"
        }

如果是因为内存原因被kill的话,通过docker inspect 容器id,查看State部分,可以看到"OOMKilled": true

小结

  • docker容器的memory限制使用的是mem.Usage - mem.Stats["inactive_file"]与limit的对比,如果超出则会被kill;free及top显示的都是宿主机的内存信息
  • kubectl top pod命令是通过memory_working_set(Memory.Usage - memory.stat[total_inactive_file])来统计容器的内存使用
  • k8s的OOMKiller使用的是container_memory_working_set_bytes指标(total_active_file + total_rss),如果超出该容器的limit,则会被OOMKiller销毁掉

doc

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容