容器内存相关知识

这篇文章是我研究容器内存整理出的相关内容. 前后内容并没有上下文关系, 每个知识点都可以单独查看.

内存控制

使用这样的命令启动一个容器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.

容器RSSbuff/cache的和, 就是容器实际使用的物理内存总值. 应该与cgroup路径下的memory.usage_in_bytes值相同. (容器的内存分布也可以查看文件memory.stat)

这么一说, 这个奇怪的现象是不是就可以解释了? 当内存不足的时候, 系统会回收文件缓存以供进程使用.

交换内存

如果容器开启了交换内存, 你就会惊奇的发现, memory cgroup限制失效了. 容器申请了超过限制的内存仍然可以继续执行, 只不过部分内存被交换到磁盘上了.

如果同时又Page Cache和交换内存, 操作系统优先选择哪一个呢? 可以通过修改memory.swappiness的值来修改优先级, 其值为0-100, 值越大, 使用交换内存的概率越大. 当值为100时, 则Page Cache与叫换内存的概率相同. 官网介绍

不过一般启动容器的时候, 都是将swap关闭的, 应该没什么需要开启的场景吧.


还有一些内存相关的其他知识点, 大部分都可查看memory group官方文档

订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请发表评论。x