背景在使用kubernetes的过程中,可以通过kubectltoppod命令查看各个Pod的内存使用情况,发现与Pod进程实际使用的内存不一致。具体情况如下:Java应用部署在kubernetes中,配置的JVM参数为-Xmx2048m-Xms2048mPod内存请求设置为3G,内存限制为4G查看Pod当前使用的内存#kubectltoppodsgateway-5bf49bcb7-7mj9998m3046Mi然后我们使用top命令查看内存使用情况top-15:29:03up140days,13:13,0users,loadaverage:0.49,0.46,0.51Tasks:5total,1running,4个睡眠,0个停止,0个僵尸%Cpu(s):3.8us,1.8sy,0.0ni,93.8id,0.5wa,0.0hi,0.1si,0.0stMiB内存:总计32011.6,免费239.0,已使用13934.4,17838.2buff/cacheMiBSwap:0.0total,0.0free,0.0ailIDUS79PRNIVIRTRESSHRS%CPU%MEMTIME+COMMAND8root2007761.2m2.0g15.2mS10.36.377:27.35java1root2002.3m0.7m0.5mS0.00.00:00.01sh7根2005.5m1.7m1.3mS0.00.00:00.00run.sh1349root2005.8m2.2m1.6mS0.00.00:00.02bash1355root2009.5m1.8m1.3mR0.00.010:0top从上面的操作可以看出,kubectltoppods使用的内存是3046M,top命令使用的内存是2.0G。由此可见,Pod内存高于实际使用的内存。Prometheusmetrics组件用于获取toppod的监控指标数据。接下来介绍比较常用的监控指标;使用内存Linux缓存机制介绍缓存机制在Linux系统中,为了提高文件系统性能,内核使用一部分物理内存分配一块缓冲区来缓存系统操作和数据文件。当内核收到读或写请求时,内核首先去缓存中查找是否有请求的数据,有则直接返回,没有则直接通过驱动程序对磁盘进行操作。缓存机制的优点:减少系统调用次数、CPU上下文切换和磁盘访问频率CPU上下文切换:CPU给每个进程一定的服务时间。当时间片用完后,内核将处理器从正在运行的进程中收回,同时保存进程当前的运行状态,然后加载下一个任务。这个过程称为上下文切换。本质上就是终止的运行进程和等待进程之间的进程切换。查看缓存区和内存使用情况#free-mtotalusedfreesharedbuff/cacheavailableMem:37901914126317501579Swap:00totaltotalmemoryusedmemoryusedfreememorysharedtotalmemorybuff/cachesharedbymultipleprocesses占用的内存通过cacheisavailable和availablememorysize可以看出上面的命令结果显示:总内存4G,已经使用了1914M,剩下的126M是很多人认为的。但实际上这并不能作为实际使用率,因为有缓存机制,具体算法如下:freememory=free(126)+buffers(0)+cached(1750)usedmemory=total(3790)-freememorybuffersandcached缓存(cached)是用来保存读取的数据,如果在重新读取时命中(找到需要的数据),则不读取硬盘,如果没有命中,则读取硬盘。里面的数据会按照阅读频率进行整理,阅读频率最高的内容会放在最容易找到的位置,不再阅读的内容会放在后排,直到删除。Buffer(缓冲区)是根据磁盘的读写而设计的,将分散的写操作集中起来,减少磁盘碎片和硬盘的重复寻道,从而提高系统性能。Linux有守护进程定期清除缓存内存(即写入磁盘),也可以通过sync命令手动清除缓冲区。举个例子:我这里有一个ext2的U盘,我cp了一个3M的MP3进去,但是U盘上的灯不闪。卸载设备时会清除缓存,因此有时您必须等待几秒钟才能卸载设备。修改/etc/sysctl.conf中vm.swappiness右边的数字,调整下次启动时的swap使用策略。数字范围从0到100,数字越大,使用swap的可能性越大。默认是60,你可以试试改一下。–两者都是RAM中的数据。两者都是RAM中的数据。简单的说,buffer即将写入磁盘,而cache则是从磁盘读取。Buffer由各种进程分配,用于输入队列等方面。一个简单的例子就是一个进程需要要读入的多个字段。在读入所有字段之前,该过程将先前读取的字段保存在缓冲区中。缓存通常用于磁盘I/O请求。如果多个进程需要访问某个文件,则将该文件缓存起来,方便下次访问,可以提高系统性能。Cache:Buffer,高速缓存,是位于CPU和主存之间的容量小但速度高的内存。由于CPU的速度远高于主存,所以CPU直接从内存中存取数据需要一定的时间。CPU刚刚使用或回收的一部分数据存储在Cache中。当CPU再次使用这部分数据时,可以从Cache中取出。直接调用,减少了CPU的等待时间,提高了系统的效率。Cache又分为一级Cache(L1Cache)和二级Cache(L2Cache)。L1Cache集成在CPU内部。L2Cache早期一般是焊接在主板上,现在也集成在CPU内部。常用容量为256KB。或512KBL2缓存。它是根据程序局部性原则设计的,即CPU执行的指令和访问的数据往往都集中在某个块中,所以将这些内容放入缓存后,CPU就不需要访问内存了,提高了访问速度。当然,如果缓存中没有CPU需要的内容,还是需要访问内存的。从内存读取和磁盘读取的角度来看,缓存可以理解为操作系统使用更多的内存来缓存可能再次访问的数据,以提高读取效率。缓存不是用来缓存文件的,而是用来缓存块的(块是I/O读写的最小单位);缓存通常用于I/O请求。如果多个进程要访问一个文件,可以将这个文件读入Cache,这样下一个进程获得CPU控制权,访问这个文件直接从Cache中读取,提高系统性能。Buffer:缓冲区,用于存储速度不同步的设备或不同优先级的设备之间传输的数据的区域。缓冲区可以减少进程间通信的等待时间。当存储速度快的设备与存储速度慢的设备通信时,慢速数据先存储在缓冲区中,存储速度快的设备达到一定程度后才读取缓冲区。在此期间,快速设备CPU可以做其他事情。缓冲区:一般用于写入磁盘。例如,一个进程需要读取多个字段。当所有需要的字段都读完后,之前读过的字段会先放入缓冲区。Buffer是根据磁盘的读写而设计的,将分散的写操作集中起来,减少磁盘碎片和硬盘的重复寻道,从而提高系统性能。Linux有守护进程定期清除缓冲区内容(即写入磁盘),也可以通过sync命令手动清除缓冲区。Cache是??一种高速缓存,用于CPU和内存之间的缓冲;buffer是I/O缓存,用于缓冲内存和硬盘;cache本来是用来做cpu缓存的,主要是cpu和内存,因为cpu快,内存和内存没有,而且有些值会被多次使用,所以放在缓存中,主要目的是为了复用,以及一级\二级物理缓存速度快,缓冲区主要用于磁盘和内存,主要是为了保护硬盘或者减少网络传输的次数(内存数据性能dataSet)。当然也可以提高速度(数据不会立即写入硬盘或直接从硬盘读取),重复使用。最开始的主要目的是为了保护Free中的disk、buffer和cache:(都占用内存):buffer:用作buffercache的内存,是块设备的读/写缓冲区。缓存:用作页面缓存、文件系统缓存的内存。如果能缓存经常访问的文件,磁盘读IObi就会很小。这里有一些简单而通俗的例子来说明Cache和Buffer的区别:1)Cache假设某个地方发生了自然灾害(比如地震),居民缺钱食物和衣服,所以救援人员被派出火车前往几个定居点运送水。消防车到达第一居民区后,打开闸门放水,群众端着锅碗瓢盆前来接水。假设救火车在一个居民区停100分钟放水,然后需要半个小时重新蓄水,然后开到下一个居民区。白天这么一个来回,就是4-5个定居点。但是让我们想想消防车是如何存在的。如果水龙头全开,强大的水压可以轻松冲上10多层楼,10分钟就能把水全部排干。但由于居民用锅碗瓢盆接水,开100%的水龙头就相当于给人洗澡,所以只能开一小部分(比如10%的流量)。但这降低了放水效率(只有原来的10%),10分钟变成了100分钟。那么,能否改进放水流程,让救火车以最高效率放水,尽快赶往下一个聚居地呢?方法是:在居民区建水库。消防车将水放入蓄水池,因为放水效率100%,10分钟后离开。居民从水库中一点一点取水。我们来分析一下这个例子就知道Cache的含义了。救火车要给居民送水,居民也要从救火车取水,这就意味着居民和救火车之间存在着互动和联系。但救火车是“高速设备”,居民是“低速设备”。低速居民赶不上高速消防车,消防车被迫降低放水速度以适应居民。为了避免这种情况,消防车与居民之间多了一层“蓄水池(即Cache)”。一方面,它处理效率为100%的消防车,另一方面,它处理效率为10%的消防车。对付居民可以让救火车腾出最大的效率运行,而不会被低速居民拖累,所以救火车只需要在聚居地停留10分钟。所以,水库是一个“活雷锋”,效率高的留给别人,效率低的留给自己。给消防车10分钟,给自己100分钟。从上面的例子可以看出,所谓Cache就是为了“补偿高速设备和低速设备之间的矛盾”而设置的一个中间层。因为在现实中,高速设备往往要和低速设备打交道,结果是被低速设备给拖了后腿。Cache存在是为了解决什么问题?速度太慢了,加快速度!以个人电脑为例。CPU速度很快,但是CPU执行的指令是从内存中取的,计算结果必须写回内存,但是内存的响应速度跟不上CPU。CPU对内存说:你把XX地址的指令发给我。记忆听到了,但因为速度慢,指令久久没有返回。这段时间,CPU只能闲着等着。这样,CPU再快也无法高效。怎么做?在CPU和内存之间加一个“蓄水池”,即Cache(片上缓存)。这个Cache比内存更快,不需要等待从Cache中取指令。当CPU要从内存中读取指令时,先读取Cache,然后再读取内存,但一开始Cache是??空的,只能从内存中访问。这个时候确实很慢,需要CPU等待。但不仅CPU需要的指令会从内存中取出,还有其他当前不需要的指令,然后将这些指令存入Cache中备用。CPU再次取指令时,还是先读取Cache,看看里面有没有需要的指令。如果恰好有,则直接从Cache中取出并返回(hit),无需等待,从而释放CPU,提高效率。(当然不会100%命中,因为Cache的容量比内存小)2)BufferCache摘下来,然后装上卡车,不过需要一个中间过程“篮子”:采摘葡萄→放入篮子→将篮子里的葡萄倒入卡车。也就是说,虽然最终目的是“把葡萄倒进卡车”,但中间肯定有一个“篮子”的转移,而这里的篮子就是Buffer。是“暂时存放物品的空间”。注意两个关键词:临时,空间,换句话说,为了完成最终目标:把葡萄放在卡车的空间里,需要把葡萄临时放在篮子的空间里。以BT为例,BT下载需要长时间挂机,电脑可能会连续运行24小时,但是BT下载的数据是碎片化的,体现在硬盘写入上,因为硬盘是一个机械寻址装置。这种碎片化写入会造成硬盘长期高负荷的机械运动,导致硬盘过早老化和损坏。那些年,有大量的硬盘因为BT下载而损坏。因此,新的BT软件在内存中开辟了一个Buffer,将数据暂时写入到Buffer中,然后保存到一定大小(比如512M)后一次性写入硬盘。硬盘负载。这就是:为了完成最终目标:将数据写入硬盘空间,需要临时写入Buffer空间。3)两者的区别总结了Cache和Buffer的相同点:都是两个层次之间的中间层,都是内存。Cache和Buffer的区别:Cache解决的是时间问题,Buffer解决的是空间问题。为了提高速度,引入了Cache这个中间层。为了给信息找一个暂存空间,引入了中间层Buffer。为了解决两个不同维度(时间和空间)的问题,刚好采用了同样的方案:加一个中间层,先把数据写到中间层,再写目标。这个中间层是内存“RAM”。既然是内存,那么就有两个参数:能写多少块(速度),能装多少东西(容量)。Cache是??利用RAM提供的高速读写,而Buffer是利用RAM提供的存储容量(空间)。简而言之:当系统两端的处理速度平衡时(在长期尺度上)使用缓冲区(buffer)。它的引入是为了减少短期突发I/O的影响,起到流量整形的作用。比如生产者-消费者问题,他们产生和消耗资源的速度大致接近,加个buffer可以抵消刚产生/消耗资源时的突然变化。缓存(caching)是当系统两端的处理速度不匹配时的一种折衷策略。由于CPU和内存之间的速度差异越来越大,人们充分利用数据的局部性特征,通过内存层次化的策略来降低这种差异的影响。假设未来内存访问变得和CPU计算一样快,缓存可以消失,但缓冲区仍然存在。比如从网上下载东西,瞬时速率可能相差很大,但长期来看是稳定的。这样就可以引入缓冲区,使OS接收数据的速率更加稳定,进一步减少对磁盘的损坏。Pod进程内存缓存讲解既然我们对Linux缓存有了一个大概的了解,那我们就来分析一下Pod内存吧!首先,我们通过kubectltoppods查看的内存指标对应Prometheusmetrics组件的container_memory_working_set_bytes指标(进程缓存内存占用+进程内存占用),也作为PodHPA弹性伸缩和limit限制的判断依据(oom)在kubernetes集群中,当Pod内存即将达到内存限制时,会回收内存。#kubectltoppodsgateway-5bf49bcb7-7mj9998m3046Mi可以想象,如果根据进程实际使用的内存来判断limit限制,那么Pod进程实际使用的内存一定大于限制的内存。会发生什么?Answer:以上情况,Pod内存不会被限制,cache会使用无限内存,无法正常回收。所以Podlimit限制使用container_memory_working_set_bytes指标(进程缓存内存占用+进程内存占用)作为判断依据。点击“阅读原文”,获得更好的阅读体验!
