美文网首页
cgroup 内存泄露问题排查记录

cgroup 内存泄露问题排查记录

作者: 一个大大大坑 | 来源:发表于2020-06-02 11:50 被阅读0次

    cgroup 内存泄露造成高内存使用率

    配置

    出现内存泄漏的主机为集群机器,运行时间约5天,内存使用超90%,其上运行 集群管理软件 和 docker并执行测试脚本反复启停容器。

    现象

    长时间运行后,集群主机内存占用逐渐增加,出现应用 OOM 现象。
    而实际查看时发现主机内存总占用高,但应用实际占用内存低或未见显著异常。

    # top
    top - 09:52:50 up 19 days, 23 min,  1 user,  load average: 0.13, 0.06, 0.06
    Tasks: 114 total,   2 running, 112 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  0.4 us,  0.7 sy,  0.0 ni, 98.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
    KiB Mem : 83.6/3881072  [|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||               ]
    KiB Swap:  0.0/0        [                                                                                          ]
    
      PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                         
     1671 root      20   0  150240  21556   5740 S   2.9  0.6 446:05.66 calico-node                                     
    24099 root      20   0  161968   2232   1560 R   1.5  0.1   0:00.02 top                                             
        1 root      20   0  193628   5344   2808 S   0.0  0.1  12:18.55 systemd                                         
        2 root      20   0       0      0      0 S   0.0  0.0   0:00.37 kthreadd                                        
        3 root      20   0       0      0      0 S   0.0  0.0   0:14.85 ksoftirqd/0                                     
        5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H                                    
        7 root      rt   0       0      0      0 S   0.0  0.0   0:30.62 migration/0                                     
        8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh                                          
        9 root      20   0       0      0      0 S   0.0  0.0   4:33.15 rcu_sched                                       
       10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 lru-add-drain                                   
       11 root      rt   0       0      0      0 S   0.0  0.0   0:08.13 watchdog/0                                      
       12 root      rt   0       0      0      0 S   0.0  0.0   0:05.43 watchdog/1                                      
       13 root      rt   0       0      0      0 S   0.0  0.0   0:26.43 migration/1                                     
       14 root      20   0       0      0      0 S   0.0  0.0   0:11.06 ksoftirqd/1                                     
       16 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/1:0H                                    
       17 root      rt   0       0      0      0 S   0.0  0.0   0:05.51 watchdog/2                                      
       18 root      rt   0       0      0      0 S   0.0  0.0   0:26.38 migration/2                                     
       19 root      20   0       0      0      0 S   0.0  0.0   0:15.31 ksoftirqd/2                                     
       21 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/2:0H                                    
       22 root      rt   0       0      0      0 S   0.0  0.0   0:05.11 watchdog/3                                      
       23 root      rt   0       0      0      0 S   0.0  0.0   0:30.14 migration/3                                     
       24 root      20   0       0      0      0 S   0.0  0.0   0:10.12 ksoftirqd/3
       26 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/3:0H
       28 root      20   0       0      0      0 S   0.0  0.0   0:00.00 kdevtmpfs
       29 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 netns                                           
       30 root      20   0       0      0      0 S   0.0  0.0   0:00.89 khungtaskd                                      
       31 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 writeback                                       
       32 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kintegrityd                                     
       33 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 bioset      
    # free -h
                  total        used        free      shared  buff/cache   available
    Mem:           3.7G        2.6G        103M        190M        991M        622M
    Swap:            0B          0B          0B
    

    可以看到内存占用 83.6% ,而实际top显示的内存占用最高也才 0.6% 没有占用内存过高的应用。

    内存占用除了用户应用占用还有内核占用,遂查看内核内存占用。

    使用linux文件系统接口查看

    # cat /proc/meminfo
    MemTotal:        3881072 kB
    MemFree:          119732 kB
    MemAvailable:     629952 kB
    Buffers:               0 kB
    Cached:           414472 kB
    SwapCached:            0 kB
    Active:           465720 kB
    Inactive:         168456 kB
    Active(anon):     362720 kB
    Inactive(anon):    60056 kB
    Active(file):     103000 kB
    Inactive(file):   108400 kB
    Unevictable:           0 kB
    Mlocked:               0 kB
    SwapTotal:             0 kB
    SwapFree:              0 kB
    Dirty:                36 kB
    Writeback:             0 kB
    AnonPages:        219780 kB
    Mapped:            28452 kB
    Shmem:            203072 kB
    Slab:            3035060 kB
    SReclaimable:     587952 kB
    SUnreclaim:      2447108 kB
    KernelStack:        3424 kB
    PageTables:         4292 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:     1940536 kB
    Committed_AS:    1370808 kB
    VmallocTotal:   34359738367 kB
    VmallocUsed:      164620 kB
    VmallocChunk:   34359326716 kB
    HardwareCorrupted:     0 kB
    AnonHugePages:     49152 kB
    CmaTotal:              0 kB
    CmaFree:               0 kB
    HugePages_Total:       0
    HugePages_Free:        0
    HugePages_Rsvd:        0
    HugePages_Surp:        0
    Hugepagesize:       2048 kB
    DirectMap4k:       98240 kB
    DirectMap2M:     4096000 kB
    

    可以看到占用超高的项目为 slab 内核占用:

    Slab:            3035060 kB //slab 内存大小
    SReclaimable:     587952 kB //slab 可回收内存大小
    SUnreclaim:      2447108 kB //slab 不可回收内存大小
    

    继续查看内核详细占用,按照缓存大小进行排序

    # slabtop -s c -o
    
     Active / Total Objects (% used)    : 5564843 / 19168412 (29.0%)
     Active / Total Slabs (% used)      : 416410 / 416410 (100.0%)
     Active / Total Caches (% used)     : 71 / 97 (73.2%)
     Active / Total Size (% used)       : 841857.29K / 3022388.82K (27.9%)
     Minimum / Average / Maximum Object : 0.01K / 0.16K / 8.00K
    
      OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME                   
    211776   4758   2%    2.00K  13236       16    423552K kmalloc-2048           
    101960  10026   9%    4.00K  12745        8    407840K kmalloc-4096           
    3289262 3287614  99%    0.12K  96743       34    386972K kernfs_node_cache      
    487704 133512  27%    0.66K  20321       24    325136K ovl_inode              
    285712  39486  13%    1.00K  17857       16    285712K kmalloc-1024           
    762048 485348  63%    0.19K  36288       21    145152K kmalloc-192            
    270064  24116   8%    0.50K  16879       16    135032K kmalloc-512            
    701778  74628  10%    0.19K  33418       21    133672K dentry                 
    1676736  55595   3%    0.06K  26199       64    104796K kmalloc-64             
    1098678 412338  37%    0.09K  26159       42    104636K kmalloc-96             
    157650    750   0%    0.62K   6306       25    100896K sock_inode_cache       
    2868480 132994   4%    0.03K  22410      128     89640K kmalloc-32             
    325280  88966  27%    0.25K  20330       16     81320K kmalloc-256            
    1946466  28141   1%    0.04K  19083      102     76332K selinux_inode_security 
    520096 187567  36%    0.12K  16253       32     65012K kmalloc-128            
     79338    965   1%    0.38K   3778       21     30224K mnt_cache              
    1699584  68608   4%    0.02K   6639      256     26556K kmalloc-16             
    101904 101904 100%    0.25K   6369       16     25476K kmem_cache             
    510255   2805   0%    0.05K   6003       85     24012K shared_policy_node     
    1725952  97281   5%    0.01K   3371      512     13484K kmalloc-8              
     17766  17275  97%    0.58K    658       27     10528K inode_cache            
     12992   6106  46%    0.57K    464       28      7424K radix_tree_node        
    102016 102016 100%    0.06K   1594       64      6376K kmem_cache_node        
     45942  35707  77%    0.10K   1178       39      4712K buffer_head            
      4386   4284  97%    0.94K    258       17      4128K xfs_inode              
    137190 137190 100%    0.02K    807      170      3228K fsnotify_mark_connector
      1125    395  35%    2.06K     75       15      2400K idr_layer_cache        
       184     60  32%    8.00K     46        4      1472K kmalloc-8192           
      2040   1824  89%    0.64K     85       24      1360K proc_inode_cache       
       288    252  87%    3.95K     36        8      1152K task_struct            
      1512   1367  90%    0.66K     63       24      1008K shmem_inode_cache      
      3366   3334  99%    0.21K    187       18       748K vm_area_struct         
       270    244  90%    2.06K     18       15       576K sighand_cache          
       420    392  93%    1.12K     15       28       480K signal_cache           
      5768   5768 100%    0.07K    103       56       412K Acpi-ParseExt          
      2376   1280  53%    0.16K     99       24       396K xfs_ili                
       160    160 100%    1.56K      8       20       256K mm_struct              
      3060   3060 100%    0.08K     60       51       240K anon_vma               
       532    532 100%    0.41K     28       19       224K xfs_efd_item           
       180    180 100%    1.06K      6       30       192K UDP                    
       156    156 100%    1.19K      6       26       192K UDPv6                  
       378    349  92%    0.44K     21       18       168K ip6_dst_cache          
       450    450 100%    0.31K     18       25       144K bio-2                  
        24     24 100%    5.12K      4        6       128K net_namespace          
        52     52 100%    2.37K      4       13       128K blkdev_queue           
        64     64 100%    1.94K      4       16       128K TCP                    
        56     56 100%    2.15K      4       14       128K pid_namespace          
        60     60 100%    2.12K      4       15       128K TCPv6     
    

    可以看到此处:

    kmalloc-2048,kmalloc-4096,kernfs_node_cache,kmalloc-1024,kmalloc-192,kmalloc-512 均占用较高,对比了正常主机,已经严重超过正常值。

    如果是内核缓存过高则可以尝试进行内核缓存释放:

    # echo 3 > /proc/sys/vm/drop_caches
    1
    

    但执行上述操作后,内存占用依旧无显著下降,也符合上面看到的 SUnreclaim: 2447108 kB //slab 不可回收内存大小。这部分内存不能被释放。

    kmalloc 为内核进行分配的内存,参考价值较大的为 kernfs_node_cache 占用高,遂搜索该项是作何作用。
    很明显,该现象为内核占用严重超标,于是在搜索时加入了 memory leak 关键字,很快发现了该 Issues docker-run --memory slab cache leak on centos7

    该 issue 表示 centos7 在反复运行 docker run --rm --memory 1g hello-world 时存在明显的内核内存占用升高,且无法被释放。且现象和当前现象一致。
    最终指向内kernel c group内存泄露问题 slab leak causing a crash when using kmem control group

    大致原因是在 3.10 内核上如果使用了 kmem limit 参数,会导致cgroup回收时无法释放部分已分配内存。至于更深入的了解,还需要其他时间,先解决目前的问题。

    复现

    原因大概确定,为了再次确定这个问题,如果能够通过上述手段复则可以确定是该问题。

    while true;do docker run --rm --memory 1g hello-world; done
    

    在一台仅运行docker的机器上执行上述语句,查看 slab 内存占用,可以看见内存占用明显上升。且最终表现和已有环境上的问题一致,总内存占用高,用户态内存占用低,内核内存占用高且无法被释放。

    解决

    既然是内核问题,且知道了明确复现路径,则可以通过两种方式进行解决:

    1. 对现有3.10内核加入补丁,重新编译。
    2. 更换没有该问题的内核版本。

    最终,进过测试后,选择了更换内核版本,将使用 Ubuntu 18.04 作为新的操作系统。

    参考

    Linux内核使用层次化内存管理的方法,每一层解决不同的问题,从下至上的关键部分如下:

    1. 物理内存管理,主要用于描述内存的布局和属性,主要有Node、Zone和Page三个结构,使内存按照Page为单位来进行管理;
    2. Buddy内存管理,主要解决外部碎片问题,使用get_free_pages等函数以Page的N次方为单位进行申请释放;
    3. Slab内存管理,主要解决内部碎片问题,可以按照使用者指定的大小批量申请内存(需要先创建对象缓存池);
    4. 内核缓存对象,使用Slab预先分配一些固定大小的缓存,使用kmalloc、vmalloc等函数以字节为单位进行内存申请释放。

    slab是Linux操作系统的一种内存分配机制。其工作是针对一些经常分配并释放的对象,如进程描述符等,这些对象的大小一般比较小,如果直接采用伙伴系统来进行分配和释放,不仅会造成大量的内碎片,而且处理速度也太慢。而slab分配器是基于对象进行管理的,相同类型的对象归为一类(如进程描述符就是一类),每当要申请这样一个对象,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免这些内碎片。slab分配器并不丢弃已分配的对象,而是释放并把它们保存在内存中。当以后又要请求新的对象时,就可以从内存直接获取而不用重复初始化。

    Slab导致的占用内存过高,Slab可以对可回收缓存手动释放,操作如下:

    # echo 3 > /proc/sys/vm/drop_caches
    1
    

    其中drop_caches的4个值有如下含义:

    • 0:不做任何处理,由系统自己管理
    • 1:清空pagecache
    • 2:清空dentries和inodes
    • 3:清空pagecache、dentries和inodes

    相关文章

      网友评论

          本文标题:cgroup 内存泄露问题排查记录

          本文链接:https://www.haomeiwen.com/subject/ququzhtx.html