前言 线上发现一些机器的内存一直在持续的增长, 刚开始怀疑是内存泄漏.
不过我们是通过Dcoker启动的进程,并且容器使用完毕以后就会被销毁, 按理说是不会出现这种情况的。
排查 计算当前所有进程使用的总内存
1 2 3 4 5 6 ~ 11 .7488 ~ total used free shared buff/cache available Mem: 62 Gi 34 Gi 2 .5 Gi 4 .0 Mi 25 Gi 28 GiSwap: 0 B 0 B 0 B 0 0 0
可以看到, 当前进程加起来一共使用了11G,但是free命令过滤出来 used 使用了34G.
这岂不是也太奇怪了.
首先, 我们先复习一下free是如何计算的。
used Used memory (calculated as total - free - buffers - cache)
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
很清晰的逻辑, 使用的内存 = 总内存 - free - buffers - cache
所以上面就是: 62 - 2.5 - 25 = 34.5
这样的算法,直接求出了除了系统可支配的内存, 节点使用的内存。
slab 内存分析 但是有一个问题就是, 34G的内存,究竟是被那些占用了????
在Google的时候, 我看很多都是教你清除cache。
1 echo 2 > /proc/ sys/vm/ drop_caches
但是系统默认有一个 vfs_cache_pressure的内核参数 参考连接在下方编号: 5
默认是100, 调小的话系统将会尽可能保留cache,如果调0则一直保留。同理,如果调大则会加快回收速度。
如果走默认值,则会让系统自动的处理。原话是这样的。
1 2 3 At the default value of vfs_cache_pressure =100 the kernel will attempt to reclaim dentries and inodes at a "fair" rate with respect to pagecache and swapcache reclaim.
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 49 MemTotal : 65967212 kBMemFree : 2035892 kBMemAvailable : 28986076 kBBuffers : 1107588 kBCached : 9034516 kBSwapCached : 0 kBActive : 13007892 kBInactive : 5995480 kBActive(anon) : 8863044 kBInactive(anon) : 3172 kBActive(file) : 4144848 kBInactive(file) : 5992308 kBUnevictable : 0 kBMlocked : 0 kBSwapTotal : 0 kBSwapFree : 0 kBDirty : 568 kBWriteback : 0 kBAnonPages : 8657824 kBMapped : 281220 kBShmem : 4952 kBSlab : 27912344 kBSReclaimable : 17133380 kBSUnreclaim : 10778964 kBKernelStack : 42112 kBPageTables : 61316 kBNFS_Unstable : 0 kBBounce : 0 kBWritebackTmp : 0 kBCommitLimit : 32983604 kBCommitted_AS : 27884200 kBVmallocTotal : 34359738367 kBVmallocUsed : 0 kBVmallocChunk : 0 kBPercpu : 16441728 kBHardwareCorrupted : 0 kBAnonHugePages : 612352 kBShmemHugePages : 0 kBShmemPmdMapped : 0 kBHugePages_Total : 0HugePages_Free : 0HugePages_Rsvd : 0HugePages_Surp : 0Hugepagesize : 2048 kBHugetlb : 0 kBDirectMap4k : 41185080 kBDirectMap2M : 25923584 kBDirectMap1G : 2097152 kB
关注了下面的部分
1 2 3 Slab: 27912344 kBSReclaimable: 17133380 kBSUnreclaim: 10778964 kB
不可回收的slab内存差不多10个G。
查看slab占用 slabtop 命令
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 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 ~ Active / Total Objects (% used) : 55581636 / 118927019 (46.7%) Active / Total Slabs (% used) : 2792439 / 2792439 (100.0%) Active / Total Caches (% used) : 97 / 123 (78.9%) Active / Total Size (% used) : 12165536.08K / 27438867.98K (44.3%) Minimum / Average / Maximum Object : 0.01K / 0.23K / 8.00K OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 40731663 9143202 22% 0.19K 969802 42 7758416K dentry 22982106 10634217 46% 0.09K 547193 42 2188772K kmalloc-96 11154560 1317716 11% 0.06K 174290 64 697160K kmalloc-64 7999936 7998096 99% 0.06K 124999 64 499996K kmem_cache_node 7999572 7997791 99% 0.38K 190466 42 3047456K kmem_cache 7356126 707560 9% 1.06K 245206 30 7846592K ext4_inode_cache 3875430 2434749 62% 0.10K 99370 39 397480K buffer_head 3431648 3426132 99% 0.12K 107239 32 428956K kmalloc-128 3400782 3398492 99% 0.19K 80971 42 647768K kmalloc-192 1523200 1382921 90% 0.03K 11900 128 47600K kmalloc-32 1224612 503648 41% 0.04K 12006 102 48024K ext4_extent_status 1122800 562131 50% 0.57K 40100 28 641600K radix_tree_node 1046784 1046407 99% 0.25K 32712 32 261696K kmalloc-256 675008 661284 97% 0.06K 10547 64 42188K dmaengine-unmap-2 488224 487097 99% 0.50K 15257 32 244112K kmalloc-512 450256 449928 99% 2.00K 28141 16 900512K kmalloc-2048 305024 304960 99% 0.06K 4766 64 19064K anon_vma_chain 296580 296210 99% 1.00K 9269 32 296608K kmalloc-1024 275574 275535 99% 0.20K 7066 39 56528K vm_area_struct 268200 266852 99% 0.13K 8940 30 35760K kernfs_node_cache 264996 264996 100% 0.04K 2598 102 10392K pde_opener 258128 239919 92% 0.05K 3536 73 14144K Acpi-Parse 244136 243898 99% 4.00K 30517 8 976544K kmalloc-4096 203964 203459 99% 0.09K 4434 46 17736K anon_vma 190096 188716 99% 0.25K 5941 32 47528K filp 162127 160216 98% 0.59K 3059 53 97888K inode_cache 151040 151040 100% 0.02K 590 256 2360K kmalloc-16 124544 123705 99% 0.12K 3892 32 15568K pid 113376 106310 93% 0.66K 2362 48 75584K proc_inode_cache 105984 105984 100% 0.01K 207 512 828K kmalloc-8 71008 70844 99% 0.07K 1268 56 5072K eventpoll_pwq 54280 54188 99% 0.69K 1180 46 37760K sock_inode_cache 47460 47460 100% 0.19K 1130 42 9040K cred_jar 44180 44180 100% 0.67K 940 47 30080K ovl_inode 39200 39200 100% 0.25K 1225 32 9800K skbuff_head_cache 25024 25024 100% 1.00K 782 32 25024K UNIX 23736 22736 95% 0.70K 516 46 16512K shmem_inode_cache 23322 23322 100% 0.69K 507 46 16224K files_cache 20550 20550 100% 1.06K 685 30 21920K signal_cache 18120 18120 100% 1.06K 604 30 19328K mm_struct 13880 13723 98% 5.44K 2776 5 88832K task_struct 9408 9251 98% 0.38K 224 42 3584K mnt_cache 8325 8250 99% 2.06K 555 15 17760K sighand_cache 8007 8007 100% 0.08K 157 51 628K inotify_inode_mark 7854 7652 97% 0.31K 154 51 2464K nf_conntrack 6936 6936 100% 0.12K 204 34 816K jbd2_journal_head 5440 5440 100% 0.02K 32 170 128K numa_policy 5304 5304 100% 0.23K 156 34 1248K tw_sock_TCP 5256 5256 100% 0.05K 72 73 288K mbcache 4736 4736 100% 0.03K 37 128 148K fscrypt_info 4416 4416 100% 0.06K 69 64 276K ext4_io_end 4080 4080 100% 0.05K 48 85 192K ftrace_event_field 3978 3978 100% 0.04K 39 102 156K Acpi-Namespace 3210 3210 100% 2.12K 214 15 6848K TCP 3024 3024 100% 0.07K 54 56 216K Acpi-Operand 2760 2760 100% 0.09K 60 46 240K trace_event_file 2720 2720 100% 0.05K 32 85 128K fscrypt_ctx 2592 2592 100% 0.25K 81 32 648K pool_workqueue 2560 2560 100% 0.20K 64 40 512K file_lock_cache
并且看监控, 一直在增加
柳暗花明 上面看slab也确实找不到真正的问题.
在几台dev的机器上去使用
1 echo 2 > /proc/ sys/vm/ drop_caches
实际上并不会去减少 SUnreclaim 的使用, 而且虽然SUnreclaim使用也很高, 但是加起来也对不上数。其他十几个G的内存都去哪里了?
实在找不到问题, 没办法只能去线上去尝试一下,死马当活马医。
但是, 没想到使用了以后内存真的降下来了。 那就是说明确实有其他的东西在占用内存。 没办法, 只能去对比 /proc/meminfo,前后数据。
发现一个字段 Percpu:
这个字段数值前后对比差异非常明显, 所以初步怀疑是这个字段造成的内存使用。
沿着这个线索在Google一下,发现在Redhat Podman有人反馈过这个问题。参考文档[6]
发现触发这个问题的逻辑差不多是一样的, 我们的进程也是会一直启动销毁容器。
而容器被创建的cgoups并没有及时的释放。
顺着思路, 也查看了一下目前内存依旧很高节点的cgoups
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ~# cat /proc/cgroups | column -t #subsys_name hierarchy num_cgroups enabled cpuset 10 31 1 cpu 7 102 1 cpuacct 7 102 1 blkio 5 100 1 memory 9 243964 1 devices 4 100 1 freezer 8 31 1 net_cls 3 31 1 perf_event 2 31 1 net_prio 3 31 1 pids 11 107 1 rdma 6 1 1 ~# docker ps -a | wc -l 104
cgoups 的 num_cgroups 竟然有243964个, 而目前容器数量才100左右。
继续搜索下去, 在git上也发现了相关文档参考文档[7]
1 I believe 4.4 .19 stable kernel has the fix so this is no longer an issue (finally ).
但是我们的内核版本: 4.19.0-18-amd64 感觉也不应该啊。
还原
执行命令
1 while true ;do id =$(date +%s%N);docker run --name =test${id} -v /data:/data centos /bin/echo "${id} " ;done
等待30分钟
查看cgoups
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ~# cat /proc/cgroups | column -t #subsys_name hierarchy num_cgroups enabled cpuset 11 3 1 cpu 5 34 1 cpuacct 5 34 1 blkio 4 32 1 memory 2 3447 1 devices 3 32 1 freezer 10 3 1 net_cls 9 3 1 perf_event 7 3 1 net_prio 9 3 1 pids 8 40 1 rdma 6 1 1
清理已退出的容器
1 docker ps -aq --filter status=exited | xargs -r -n 10 -P 4 docker rm
再次查看cgoups
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ~# cat /proc/cgroups | column -t #subsys_name hierarchy num_cgroups enabled cpuset 11 3 1 cpu 5 34 1 cpuacct 5 34 1 blkio 4 32 1 memory 2 3035 1 devices 3 32 1 freezer 10 3 1 net_cls 9 3 1 perf_event 7 3 1 net_prio 9 3 1 pids 8 40 1 rdma 6 1 1
确认当前容器数量
可以看到, 一共8个仅存容器, 但是cgoups memory num_cgroups 缺有3000+
1 2 3 4 5 6 7 8 9 10 11 free -m total used free shared buff/cache available Mem: 3803 590 1839 0 1373 2992 Swap: 0 0 0 grep Per /proc/meminfo Percpu: 16848 kB grep SUnreclaim /proc/meminfo SUnreclaim: 217148 kB
Percpu 一直在增加, 并且 SUnreclaim 也占用了200M
我们使用命令清理一下。
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 echo 1 > /proc/sys/vm/drop_caches ~ ~ cpuset 11 3 1 cpu 5 34 1 cpuacct 5 34 1 blkio 4 32 1 memory 2 66 1 devices 3 32 1 freezer 10 3 1 net_cls 9 3 1 perf_event 7 3 1 net_prio 9 3 1 pids 8 40 1 rdma 6 1 1 ~ Percpu: 16848 kB ~ SUnreclaim: 43664 kB ~ total used free shared buff/cache available Mem: 3803 307 3148 0 347 3268 Swap: 0 0 0
发现内存和 cgoups 的 num_cgroups 也恢复了正常。
之后又进行了一轮测试, 如果run的时候加一个--rm 内存和cgoups使用则是正常状态.
1 while true ;do id =$(date +%s%N);docker run --rm --name =test${id} -v /data:/data centos /bin/echo "${id} " ;done
随后又测试了一下内核版本5.10.0-11-amd64发现也同样出现这种问题.
结论 目前看下来:
docker 手动rm不会造成 cgoups 的回收. 但具体是内核的bug,还是docker的,暂时没办法查明,欢迎大佬们留言。
当在Linux下频繁存取 or 读取文件后,物理内存会很快被用光,当程序结束后,内存不会被正常释放,而是一直作为caching。
正因为我们的应用一直频繁读取文件,并且容器启停频率很高,所以导致了内存占用的问题。
参考文档