每个容器,pod在启动之后都会有着属于自己的cgroup路径,在该路径下有着这个容器cpu,memory限制参数,能够控制资源使用的上限,而在我们日常集群运营维护中,这些原理能够帮助我们快速解决问题,在此做一些总结
容器CPU
每个容器的创建都在 CPUCgroup 的子系统中建立一个控制组,然后把容器中进程写入到这个控制组里,这时候"Request CPU"就需要为容器设置最低可用CPU,"Limit CPU"就需要为容器设置可用 CPU 的上限,这里就得说到控制容器cpu的cgroup的三个参数,参数如下
limit cpu上限:容器 CPU 的上限由 cpu.cfs_quota_us 除以 cpu.cfs_period_us 得出的值来决定的。而且,在操作系统里,cpu.cfs_period_us 的值一般是个固定值(100ms),不会去修改它,所以我们就是只修改 cpu.cfs_quota_us
request cpu:当容器整体limit可能超过主机本身资源限制时,通过cpu.shares进行控制,保证最低资源使用
例
主机cpu共4核
容器A cpu.cfs_quota_us为200ms,200/100=2核,容器A最高使用资源上限为2 core,cpu.shares为1024
容器B cpu.cfs_quota_us为300ms,300/100=3核,容器B最高使用资源上限为3 core,cpu.shares为3072
当容器A,B都跑满时主机资源明显不足,这个时候主机就通过cpu.shares比例A:B=1024:3072,分配容器A 1核使用,分配容器B 3核使用
总结:limit CPU就是容器所在Cgroup控制组中的CPU上限值,request CPU的值就是控制组中的cpu.shares的值。
容器Memory
容器在运行一段时间之后,为什么会被kill,内存在监控上显示没有到上限,为什么会OOM了?
OOM的原因就是容器使用的内存超过cgroup限制,无法正常释放使用的内存,内存在容器层面是属于硬限制,如果超过使用,就会引发oom kill,Memory Cgroup也是Linux Cgroups子系统之一,它的作用是对一组进程的Memory使用做限制。Memory Cgroup的虚拟文件系统的挂载点一般在"/sys/fs/cgroup/memory"这个目录下,这个和CPU Cgroup类似。
同样memory cgroup强相关的三个参数
查看容器退出原因
裸docker运行
通过docker inspect查看,就会看到容器处于"exited"状态,并且"OOMKilled"是 true。
kubernetes pod
kubectl describe pod $podname可以在事件中查看到oom信息
再通过主机上/var/log/message日志可以查看到类似OOM推出的堆栈,可以查看到退出时容器的内存达到了limit上限。
RSS:RSS是指进程真正能申请到物理页面的内存大小。
Page Cache:每个进程除了各自独立分配到的RSS内存外,如果进程对磁盘上的文件做了读写操作,Linux还会分配内存,把磁盘上读写到的页面存放在内存中,这部分的内存就是Page Cache。
备注:而我们在容器中经常遇到的RSS内存使用量并不高但是却触发了limit的OOM,原因就在于进程读写文件产生的page_Cahce没有及时的释放给RSS申请,导致OOM
代码程序去读取 100MB 的文件,在读取文件前,系统中 Page Cache 的大小是 388MB,读取后 Page Cache 的大小是 506MB,增长了大约 100MB 左右,多出来的这 100MB,正是我们读取文件的大小。
在 Linux 系统里只要有空闲的内存,系统就会自动地把读写过的磁盘文件页面放入到 Page Cache 里。那么这些内存都被 Page Cache 占用了,一旦进程需要用到更多的物理内存,执行 malloc() 调用做申请时,就会发现剩余的物理内存不够了,那该怎么办呢?这就要提到 Linux 的内存管理机制了。
Linux 的内存管理有一种内存页面回收机制(page frame reclaim),会根据系统里空闲物理内存是否低于某个阈值(wartermark),来决定是否启动内存的回收。内存回收的算法会根据不同类型的内存以及内存的最近最少用原则,就是 LRU(Least Recently Used)算法决定哪些内存页面先被释放。因为 Page Cache 的内存页面只是起到 Cache 作用,自然是会被优先释放的。
所以,Page Cache 是一种为了提高磁盘文件读写性能而利用空闲物理内存的机制。同时,内存管理中的页面回收机制,又能保证 Cache 所占用的页面可以及时释放,这样一来就不会影响程序对内存的真正需求了。
了解到了memory内存统计的方法,当看到容器长期处于limit的临界值,但依然能够不断申请到内存的原因,其实就是page_cache一直存在可以申请到的内存,可以通过memory.stats查看到当前的内存开销
启动容器后可以看到memory.stats参数中的rss跟cache,而cache代表的就是page_Cache,而这部分的内存是可以回收的,如果回收不掉造成容器的oom就需要进一步排查一下是否是因为脏页太多导致的无法回收