由内存溢出的思考:如何监控内存?

本文总阅读量

前记

最近在做监控系统,发现Linux的内存监控比较困难,主要是指标太多,不知道那些指标才是真正需要的,比如一开始我们就监控free命令输出的free,然而由于页缓存的原因,有些数据是不对的.恰好今天刚刚遇到系统的一个内存看似不准的问题,在调查问题时同时了解了Linux的内存设计

1.一次”内存报警异常”

1.1.问题

最近监控一直在报某些机器内存异常,内存超过了90%的使用,登上去机器用free -h命令查看发现user部分确实很多,但通过ps -aux | sort -l 4发现所有进程加起来的内存占用都没有user的10分之一,究竟是谁在吃内存不得而知.

1.2.排查问题机器

1.2.1.top

从top命令可以看出,目前最占用内存的进程只有supervisord,只占用了0.9%的内存,即使所有进程占用内存加起来,也占用到10-20%左右

1
2
3
4
5
6
7
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                                                                                                          
1230 root 20 0 30040 18192 1764 S 0.0 0.9 73:32.80 supervisord
27835 nobody 20 0 21248 11552 1900 S 3.3 0.6 18:15.34 so1n-server
16045 nobody 20 0 18456 8240 1380 S 1.0 0.4 3:45.40 so1n-server
22880 root 20 0 16720 8108 6836 S 0.0 0.4 0:01.83 sshd
209 root 20 0 44292 8056 4212 S 0.0 0.4 5:36.02 systemd-journal
.....

1.2.2. free

通过free -h命令可以看到 共2g内存的小机器,used已经使用了1.6g了 ,占用了80%了

1
2
3
              total        used        free      shared  buff/cache   available
Mem: 2.0Gi 1.6Gi 297Mi 20Mi 87Mi 257Mi
Swap: 0B 0B 0B

为了了解used的主要组成,先通过man free查看free各个字段的说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DESCRIPTION
......
total Total installed memory (MemTotal and SwapTotal in /proc/meminfo)
used Used memory (calculated as total - free - buffers - cache)
free Unused memory (MemFree and SwapFree in /proc/meminfo)
shared Memory used (mostly) by tmpfs (Shmem in /proc/meminfo)
buffers
Memory used by kernel buffers (Buffers in /proc/meminfo)
cache Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
buff/cache
Sum of buffers and cache
available
Estimation of how much memory is available for starting new applications, without swapping. Unlike the data provided by the cache or free fields, this field takes
into account page cache and also that not all reclaimable memory slabs will be reclaimed due to items being in use (MemAvailable in /proc/meminfo, available on
kernels 3.14, emulated on kernels 2.6.27+, otherwise the same as free)

可以看出used是由total -free - buffes - cache计算得出,而cache又包括了Cached和SReclaimable,,所以used=total -free -buffers - Cached - SReclaimable.

1.2.3. cat /proc/meminfo

了解了free中used的计算后我们就可以自己根据/proc/meminfo查看有什么异常值,除去了计算used的free,buffers,Cached,SReclaimable后发现有一个叫SUnreclaim的值非常可疑,这个值表明的是存在于内核中的不可回收数据结构的大小, 存在与内核的数据结构有很多,比如创建一个TCP时,内核里面就会创建一个存TCP相关数据的数据结构,一般来说这个值的比较低的,而且是要低于SReclaimable(也是存在于内核中的数据结构的大小,不过是可回收的)的,所以需要进入Sunreclaim内部,查看是什么数据结构占用了比较多的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
cat /proc/meminfo 
MemTotal: 2045852 kB
MemFree: 296104 kB
MemAvailable: 255252 kB
Buffers: 9348 kB
Cached: 58672 kB
SwapCached: 0 kB
Active: 118576 kB
Inactive: 29460 kB
Active(anon): 80200 kB
Inactive(anon): 20792 kB
Active(file): 38376 kB
Inactive(file): 8668 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 36 kB
Writeback: 0 kB
AnonPages: 80080 kB
Mapped: 18632 kB
Shmem: 20956 kB
Slab: 717036 kB
SReclaimable: 22040 kB
SUnreclaim: 694996 kB
KernelStack: 1308 kB
PageTables: 1880 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 1022924 kB
Committed_AS: 210184 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 0 kB
VmallocChunk: 0 kB
Percpu: 452 kB
AnonHugePages: 18432 kB
ShmemHugePages: 0 kB
ShmemPmdMapped: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
Hugetlb: 0 kB
DirectMap4k: 1380324 kB
DirectMap2M: 716800 kB
DirectMap1G: 0 kB

1.2.4. slabtop

slabtop可以查看slab(存放于内核的数据结构)的使用情况,可以发现有一个叫TCP的数据结构占用了600多M的内存,根据往常的使用经验,一个正常使用的TCP链接大概占4kb左右,这样算下来是这台机器打开了161392个TCP链接,显然是不对的(即使64kb, 换算下来也要1w个请求, 当前的请求量根本没那么多).所以需要对该问题进行修复,不过由于该问题是更改TCP参数造成的,跟主题不一样,所以关于内存异常的问题的调查就到这.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
307968 307298 99% 0.02K 1203 256 4812K kmalloc-16
302610 301633 99% 2.12K 20174 15 645568K TCP
99519 99519 100% 0.19K 4739 21 18956K kmalloc-192
28056 21510 76% 0.19K 1336 21 5344K dentry
18944 16993 89% 0.03K 148 128 592K kmalloc-32
15390 15390 100% 0.13K 513 30 2052K kernfs_node_cache
13824 13038 94% 0.06K 216 64 864K anon_vma_chain
10816 8193 75% 0.06K 169 64 676K kmalloc-64
9477 9180 96% 0.59K 729 13 5832K inode_cache
7923 7915 99% 0.20K 417 19 1668K vm_area_struct
7605 6366 83% 0.10K 195 39 780K buffer_head
7452 7310 98% 0.09K 162 46 648K anon_vma
4528 3242 71% 0.25K 283 16 1132K filp
4116 4116 100% 0.19K 196 21 784K uid_cache

2.要监控哪些内存

通过上面的排查后,可以比较明确的是,内存监控不是单一指标,应该用一主多辅的指标进行监控(不然Linux内存那么多指标就是摆设了….)

主要指标当然是我们常说的内存使用占比了,根据free命令中的说明,我们一般都是用free的user/total来计算,也就是((MemTotal - (MemFree + Buffers + Cached + SReclaimable)) / MemTotal),但是这个计算会漏一些数据,比如上面所说的SUnreclaim以及/proc/meminfo里面的其他情况,同时Linux的页缓存也可能存在无法回收的情况.所以可以说这个指标也是不太准的.

那要怎么样才能把上面的情况考虑进去,尽量做一个所有服务都共有的内存监控呢,还记得free里有一个available字段,他的文档说明也说了,该字段是对一些可用内存进行统计,同时它会考虑 Page Cache 和无法回收的SUnreclaim的内存( 假设pagecache / 2 和 SUnreclaim / 2 是不可回收的),最后估算出一个当前可用内存,来达到尽可能的与实际数据贴合.
所以实际的内存统计应该为( MemTotal - MemAvailable) / MemTotal,这样子会比原来的统计更加准确.

这样一来主要指标已经定下了,但考虑到他只能尽量去适配所有业务,有些业务有对某项值消耗的非常厉害,但主要指标不一定能统计到的,所以我们还要添加一些辅助指标,以完善我们的内存监控系统

  • SUnreclaim ,就像我们上面提到的,这个指标一般机器是不会出现的,只有涉及到tcp等问题才会导致该常数的值异常,这个异常用我们的主要指标是很难分析到的.
  • buffer, cached,正常情况下的业务该值是不会有异常的,但是在涉及到一些io读写比较多的情景时,机器上面很多内存会被buffer,cached占用.

    3. 附录

    3.1. 正常机器数据

    该章节主要用于展示数据,对比与原来的数据差别

    3.2. free

    1
    2
    3
                  total        used        free      shared  buff/cache   available
    Mem: 2.0Gi 193Mi 921Mi 20Mi 882Mi 1.6Gi
    Swap: 0B 0B 0B

    3.3. cat /proc/meminfo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    MemTotal:        2045852 kB
    MemFree: 937856 kB
    MemAvailable: 1658140 kB
    Buffers: 56928 kB
    Cached: 779772 kB
    SwapCached: 0 kB
    Active: 356800 kB
    Inactive: 604076 kB
    Active(anon): 121720 kB
    Inactive(anon): 20808 kB
    Active(file): 235080 kB
    Inactive(file): 583268 kB
    Unevictable: 0 kB
    Mlocked: 0 kB
    SwapTotal: 0 kB
    SwapFree: 0 kB
    Dirty: 28 kB
    Writeback: 0 kB
    AnonPages: 121908 kB
    Mapped: 36768 kB
    Shmem: 20972 kB
    Slab: 104908 kB
    SReclaimable: 67096 kB
    SUnreclaim: 37812 kB
    KernelStack: 1612 kB
    PageTables: 2088 kB
    NFS_Unstable: 0 kB
    Bounce: 0 kB
    WritebackTmp: 0 kB
    CommitLimit: 1022924 kB
    Committed_AS: 398732 kB
    VmallocTotal: 34359738367 kB
    VmallocUsed: 0 kB
    VmallocChunk: 0 kB
    Percpu: 356 kB
    AnonHugePages: 51200 kB
    ShmemHugePages: 0 kB
    ShmemPmdMapped: 0 kB
    HugePages_Total: 0
    HugePages_Free: 0
    HugePages_Rsvd: 0
    HugePages_Surp: 0
    Hugepagesize: 2048 kB
    Hugetlb: 0 kB
    DirectMap4k: 163812 kB
    DirectMap2M: 1933312 kB
    DirectMap1G: 0 kB

    3.4. slabtop

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
    183456 179948 98% 0.10K 4704 39 18816K buffer_head
    57036 55070 96% 0.19K 2716 21 10864K dentry
    51639 51639 100% 0.19K 2459 21 9836K kmalloc-192
    24684 24684 100% 0.04K 242 102 968K ext4_extent_status
    17280 16530 95% 0.03K 135 128 540K kmalloc-32
    17055 16817 98% 1.05K 1137 15 18192K ext4_inode_cache
    15420 15420 100% 0.13K 514 30 2056K kernfs_node_cache
    11520 10774 93% 0.06K 180 64 720K anon_vma_chain
    11479 11416 99% 0.59K 883 13 7064K inode_cache
    11136 11136 100% 0.06K 174 64 696K tcp_bind_bucket
    9024 7905 87% 0.06K 141 64 564K kmalloc-64
    8960 8960 100% 0.02K 35 256 140K kmalloc-16
查看评论