当前位置: 代码网 > 服务器>服务器>云虚拟主机 > 一文详解docker容器中的memory限制

一文详解docker容器中的memory限制

2024年05月27日 云虚拟主机 我要评论
序本文主要研究一下docker容器的memory限制内存限制docker run -m 512m -p 8081:8080 --rm docker-demo通过-m参数指定限制的内存大小buffer/

本文主要研究一下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
}

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销毁掉

以上就是一文详解docker容器中的memory限制的详细内容,更多关于docker memory限制的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com