服务器内存飙升,可能的排查步骤
服务器内存突然飙升可能是由许多因素引起的,以下是一些可能的排查步骤:
查看系统日志:查看系统日志可以帮助我们确定是否有任何错误或异常情况发生,这些情况可能导致内存使用率增加。
查看进程列表和资源占用情况:使用命令行工具(如top、htop或ps等)查看当前运行的进程列表和它们使用的资源情况,特别是内存占用情况。
使用性能监控工具:使用性能监控工具(如sar、vmstat、iostat等)来查看系统的整体性能,包括CPU、内存、磁盘和网络使用情况,以帮助我们确定系统中的瓶颈。
检查程序和服务日志:如果有正在运行特定的程序或服务,查看它们的日志可以帮助您确定是否有任何错误或异常情况发生,这些情况可能导致内存使用率增加。
检查数据库和缓存:如果有正在运行数据库或缓存服务,查看它们的日志和统计数据可以帮助您确定是否有任何查询或缓存问题导致内存使用率增加。
检查网络流量:如果服务器处理大量网络流量,考虑使用网络监控工具(如tcpdump或Wireshark等)来查看网络流量,以帮助我们确定是否有任何异常情况导致内存使用率增加。
总之,排查服务器内存突然飙升的原因需要综合考虑多个方面,需要根据实际情况采取不同的措施。
已经过去的故障如何排查
对于历史时刻出现的服务器内存飙升的问题,我们可以通过分析系统日志,查看系统日志以确定飙升发生的时间点,并查找与内存相关的任何错误或异常情况。
例如,我们可以查看系统日志中的oom-killer日志,该日志记录了Linux内核如何通过杀死进程来解决内存不足的问题。
在CentOS中,系统日志通常存储在/var/log目录下。以下是/var/log目录中一些常见的系统日志文件:
/var/log/messages:这是一个常见的系统日志文件,包括内核、系统服务和其他应用程序的日志信息。
/var/log/secure:这是一个安全日志文件,包括用户登录、sudo命令、ssh连接和其他安全相关的事件。
/var/log/boot.log:这是一个引导日志文件,包括系统引导过程中的信息。
/var/log/cron:这是一个cron任务日志文件,包括cron任务的执行记录。
/var/log/maillog:这是一个邮件服务器日志文件,包括邮件服务器的发送和接收记录。
oom-killer
oom-killer是Linux系统中的一个内核机制,用于在系统内存不足的情况下,杀死一些进程以释放内存。
oom-killer的全称是Out-Of-Memory Killer,即内存不足时的杀手。当系统内存不足时,Linux内核会尝试通过回收一些不必要的内存来释放资源,但如果无法释放足够的内存,就会触发oom-killer机制,强制杀死一些进程以释放内存。
打开系统日志文件,查看oom-killer相关信息。以下是笔者最近处理过的一个例子,以供参考。
1.首先打开/var/log/messages文件,查找oom-killer关键字,开始的信息是这样的。
Jul 19 07:10:22 GG-ops-yearning kernel: systemd invoked oom-killer: gfp_mask=0x3000d0, order=2, oom_score_adj=0
Jul 19 07:10:23 GG-ops-yearning kernel: systemd cpuset=/ mems_allowed=0
Jul 19 07:10:23 GG-ops-yearning kernel: CPU: 0 PID: 1 Comm: systemd Tainted: G OE ------------ T 3.10.0-1160.11.1.el7.x86_64 #1
Jul 19 07:10:23 GG-ops-yearning kernel: Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS 449e491 04/01/2014
Jul 19 07:10:23 GG-ops-yearning kernel: Call Trace:
Jul 19 07:10:23 GG-ops-yearning kernel: [<ffffffff8b380faa>] dump_stack+0x19/0x1b
......
上面的信息,记录了Linux系统中一个进程被oom-killer机制杀死的过程。
以下是日志的详细解释:
第一行日志记录了系统服务管理器systemd在某个时间点(2023年7月19日上午7点10分22秒)调用oom-killer机制杀死了一个进程。具体的oom-killer参数包括gfp_mask、order和oom_score_adj。
第二行日志记录了被杀死进程的启动参数,包括cpuset和mems_allowed。
接下来的几行日志记录了一些与进程相关的信息,如CPU ID、进程ID、进程名称和内核版本等。
最后几行日志记录了一些内核调用栈信息,这些信息通常用于分析和解决系统问题。
oom_score_adj
上面我们提到当服务器内存不足时,oom会杀死一些进程来释放内存,但是哪些进程会被选择呢?答案就跟oom_score_adj有关。
其实 Linux 的每个进程都有一个 oom_score (位于/proc/
oom_score 是每个进程的一个只读整数值,表示该进程在内存不足时被 OOM killer 杀死的优先级。oom_score 是根据进程的 oom_score_adj 值计算出来的,该值可以通过 proc/[pid]/oom_score_adj 文件进行设置。oom_score 越高,进程越容易被杀死。
我们可以调整oom_score_adj的值来防止服务器上比较重要的应用被kill,比如例如 核心的应用进程,我们想尽量避免它被 OOM Killer 杀死,这时候就可以调低它的 oom_score_adj 的值。
cpuset && mems_allowed
"systemd cpuset=/"和"mems_allowed=0"是由systemd进程设置的cgroup参数,用于限制该进程的资源使用。
"systemd cpuset=/"表示该进程被分配到了默认的cgroup中,并且没有任何CPU资源限制。也就是说,该进程可以使用系统中的所有CPU核心。
"mems_allowed=0"指定该进程只能使用NUMA节点0的内存资源,而不能使用其他节点的内存资源。
NUMA(Non-Uniform Memory Access)是一种计算机系统的体系结构,其中CPU和内存资源被分成多个节点(node)或区域(zone),每个节点的访问延迟和带宽不同。
接着往下看后面的日志信息。
Jul 19 07:10:23 GG-ops-yearning kernel: Mem-Info:
Jul 19 07:10:23 GG-ops-yearning kernel: active_anon:1701746 inactive_anon:730 isolated_anon:0#012 active_file:1379 inactive_file:2674 isolated_file:73#012 unevictable:0 dirty:3 writeback:0 unstable:0#012 slab_reclaimable:22823 slab_unreclaimable:22389#012 mapped:650 shmem:2732 pagetables:42746 bounce:0#012 free:45069 free_pcp:0 free_cma:0
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 DMA free:15908kB min:136kB low:168kB high:204kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15992kB managed:15908kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Jul 19 07:10:23 GG-ops-yearning kernel: lowmem_reserve[]: 0 2813 7533 7533
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 DMA32 free:111776kB min:25188kB low:31484kB high:37780kB active_anon:2573912kB inactive_anon:456kB active_file:4076kB inactive_file:5496kB unevictable:0kB isolated(anon):0kB isolated(file):252kB present:3111608kB managed:2881176kB mlocked:0kB dirty:12kB writeback:0kB mapped:2328kB shmem:1448kB slab_reclaimable:31344kB slab_unreclaimable:24556kB kernel_stack:6864kB pagetables:45460kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:14418 all_unreclaimable? yes
Jul 19 07:10:23 GG-ops-yearning kernel: lowmem_reserve[]: 0 0 4719 4719
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 Normal free:52592kB min:42252kB low:52812kB high:63376kB active_anon:4233072kB inactive_anon:2464kB active_file:1440kB inactive_file:5200kB unevictable:0kB isolated(anon):0kB isolated(file):40kB present:4980736kB managed:4835924kB mlocked:0kB dirty:0kB writeback:0kB mapped:272kB shmem:9480kB slab_reclaimable:59948kB slab_unreclaimable:65000kB kernel_stack:10240kB pagetables:125524kB unstable:0kB bounce:0kB free_pcp:0kB local_pcp:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:10151 all_unreclaimable? yes
Jul 19 07:10:23 GG-ops-yearning kernel: lowmem_reserve[]: 0 0 0 0
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 DMA: 14kB (U) 08kB 016kB 132kB (U) 264kB (U) 1128kB (U) 1256kB (U) 0512kB 11024kB (U) 12048kB (M) 34096kB (M) = 15908kB
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 DMA32: 217324kB (UEM) 26728kB (UEM) 22316kB (UEM) 032kB 064kB 0128kB 0256kB 0512kB 01024kB 02048kB 04096kB = 111872kB
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 Normal: 124724kB (UEM) 3868kB (UM) 316kB (M) 032kB 064kB 0128kB 0256kB 0512kB 01024kB 02048kB 0*4096kB = 53024kB
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=1048576kB
Jul 19 07:10:23 GG-ops-yearning kernel: Node 0 hugepages_total=0 hugepages_free=0 hugepages_surp=0 hugepages_size=2048kB
Jul 19 07:10:23 GG-ops-yearning kernel: 6892 total pagecache pages
Jul 19 07:10:23 GG-ops-yearning kernel: 0 pages in swap cache
Jul 19 07:10:23 GG-ops-yearning kernel: Swap cache stats: add 0, delete 0, find 0/0
Jul 19 07:10:23 GG-ops-yearning kernel: Free swap = 0kB
Jul 19 07:10:23 GG-ops-yearning kernel: Total swap = 0kB
Jul 19 07:10:23 GG-ops-yearning kernel: 2027084 pages RAM
Jul 19 07:10:23 GG-ops-yearning kernel: 0 pages HighMem/MovableOnly
Jul 19 07:10:23 GG-ops-yearning kernel: 93832 pages reserved
这条日志信息包含了系统的内存信息(Mem-Info)。它提供了关于系统内存使用情况的详细信息,包括当前可用内存、活动的内存、不活动的内存、缓存、页表等等。
如上面日志中的"active_anon"、"inactive_anon"、"active_file"、"inactive_file"等字段表示不同类型的内存使用情况。
"active_anon":表示正在使用的匿名内存(未命名的内存块,如进程栈和堆等)大小,单位为页(page),在这个例子中是1701746页。 "inactive_anon":表示不活跃的匿名内存大小,单位为页,在这个例子中是730页。 "active_file":表示正在使用的文件内存大小,单位为页,在这个例子中是1379页。 "inactive_file":表示不活跃的文件内存大小,单位为页,在这个例子中是2674页。 "isolated_anon"和"isolated_file":表示被隔离的匿名内存和文件内存大小。在这个例子中,都是0页。 "unevictable":表示不能被释放的内存大小,单位为页,在这个例子中是0页。 "dirty":表示脏内存大小,即需要写回到磁盘的内存大小,单位为页,在这个例子中是3页。 "writeback":表示正在写回到磁盘的内存大小,单位为页,在这个例子中是0页。 "unstable":表示不稳定的内存大小,单位为页,在这个例子中是0页。 "slab_reclaimable":表示可回收的内核对象内存大小,单位为字节,在这个例子中是22823字节。 "slab_unreclaimable":表示不可回收的内核对象内存大小,单位为字节,在这个例子中是22389字节。 "mapped":表示映射内存大小,单位为页,在这个例子中是650页。 "shmem":表示共享内存大小,单位为页,在这个例子中是2732页。 "pagetables":表示页表内存大小,单位为页,在这个例子中是42746页。 "bounce":表示反弹缓冲区内存大小,单位为页,在这个例子中是0页。 "free":表示可用内存大小,单位为页,在这个例子中是45069页。 "free_pcp"和"free_cma":表示保留给per-CPU变量和DMA内存的可用内存大小,单位为页。在这个例子中,它们都是0页。
可与看到可用内存大小free,只有45069页*4KB(页面大小)= 176MB
再往下走,可以看到系统的内存分为三个区域:DMA、DMA32和Normal。每个区域的可用内存、已使用内存、缓存、页表等等的详细信息都有列出。
例如,"Node 0 DMA free:15908kB"表示DMA区域当前可用的内存为15908kB。"Node 0 DMA32 free:111776kB"表示DMA32区域当前可用的内存为111776kB。"Node 0 Normal free:52592kB"表示Normal区域当前可用的内存为52592kB。
此外,日志信息还提供了有关Huge pages、Swap Cache和Page Cache的信息,以及系统中的总内存页数和可用的Swap空间。
这些信息对于了解系统的内存使用情况以及可能的内存问题非常有用。管理员可以使用这些信息来诊断内存问题,并采取必要的措施来优化系统的内存使用情况。
DMA、DMA32和Normal
DMA、DMA32和Normal是Linux内核中用于描述内存区域的术语。DMA代表Direct Memory Access,表示直接内存访问。DMA区域是系统中的低端内存区域,通常用于设备驱动程序和其他需要访问物理内存的程序。它包括0到16MB的物理内存地址范围。DMA32区域是介于DMA和Normal之间的一个区域,它包括16MB到4GB的物理内存地址范围。DMA32区域用于支持更大的内存访问,如大容量磁盘和网络存储设备。它通常用于服务器和工作站等高端系统。Normal区域是系统中的高端内存区域,它包括4GB到机器上限的物理内存地址范围。Normal区域通常用于操作系统和应用程序。
接着往下看后面的日志信息。
Jul 19 07:10:23 GG-ops-yearning kernel: [ pid ] uid tgid total_vm rss nr_ptes swapents oom_score_adj name
Jul 19 07:10:23 GG-ops-yearning kernel: [ 381] 0 381 35462 99 74 0 0 systemd-journal
Jul 19 07:10:23 GG-ops-yearning kernel: [ 419] 0 419 11410 196 22 0 -1000 systemd-udevd
Jul 19 07:10:23 GG-ops-yearning kernel: [ 543] 0 543 13883 113 28 0 -1000 auditd
Jul 19 07:10:23 GG-ops-yearning kernel: [ 568] 999 568 153059 1653 63 0 0 polkitd
Jul 19 07:10:23 GG-ops-yearning kernel: [ 573] 0 573 6703 198 18 0 0 systemd-logind
Jul 19 07:10:23 GG-ops-yearning kernel: [ 574] 81 574 14594 213 32 0 -900 dbus-daemon
Jul 19 07:10:23 GG-ops-yearning kernel: [ 592] 998 592 29452 128 30 0 0 chronyd
Jul 19 07:10:23 GG-ops-yearning kernel: [ 837] 0 837 25735 513 47 0 0 dhclient
Jul 19 07:10:23 GG-ops-yearning kernel: [ 898] 0 898 3793730 864300 18 0 0 node_exporter
Jul 19 07:10:23 GG-ops-yearning kernel: [ 900] 0 900 143571 3340 98 0 0 tuned
Jul 19 07:10:23 GG-ops-yearning kernel: [ 911] 0 911 171069 7440 66 0 -999 containerd
Jul 19 07:10:23 GG-ops-yearning kernel: [ 955] 0 955 10890 393 21 0 0 AliYunDunUpdate
Jul 19 07:10:23 GG-ops-yearning kernel: [ 1029] 0 1029 5732 90 16 0 0 argusagent
Jul 19 07:10:23 GG-ops-yearning kernel: [ 1039] 0 1039 359960 3763 68 0 0 /usr/local/clou
Jul 19 07:10:23 GG-ops-yearning kernel: [ 1210] 0 1210 22536 278 41 0 0 master
上面的日志信息提供了系统当时运行的进程快照,包括进程ID(PID)、用户ID(UID)、总虚拟内存(total_vm)、驻留内存(rss)、页表项数(nr_ptes)、交换项数(swapents)等。每行日志信息对应一个进程。
"[ pid ]":表示进程ID(PID),用方括号括起来。 "uid":表示用户ID(UID),即运行该进程的用户ID。 "tgid":表示进程组ID(TGID),即进程的父进程ID。 "total_vm":表示进程使用的总虚拟内存大小,单位为页。 "rss":表示进程使用的驻留内存大小,即实际占用的物理内存大小,单位为页。 "nr_ptes":表示进程使用的页表项数。 "swapents":表示进程使用的交换项数。 "oom_score_adj":表示进程的OOM分数调整值,用于调整进程在内存不足时被杀死的优先级。较低的值表示进程更容易被杀死。 "name":表示进程的名称。
这时我们就可以利用这些信息来了解当时系统中运行的进程的情况,包括使用的内存、资源占用情况等,找到是哪个进程占用内存过大,导致资源不足。
在这个例子中,我们可以看到pid是898的进程,占用资源非常大,rss为864300页,总大小864300*4K = 3.3G
好了,通过上面的分析,我们已经找到了导致问题的进程了,接下来就是分析为什么该进程占用内存大的问题了。详细的就不展开了。
回顾我们上述的过程,可以看到通过分析oom-killer记录的日志信息,我们可以得到很多有用的信息,这些信息对于了解系统的内存使用情况和性能瓶颈非常有用。管理员可以使用这些信息来优化系统的内存管理和配置,以实现更好的性能和可靠性。
总结
在分析历史时刻出现的服务器内存飙升的问题时,我们可以利用linux的oom-killer机制,查看系统日志中的oom-killer日志信息,该日志记录了Linux内核如何通过杀死进程来解决内存不足的问题。通过这些信息,以确定飙升发生的时间点,并查找与内存相关的任何错误或异常情况。
oom-killer是Linux系统中的一个内核机制,用于在系统内存不足的情况下,杀死一些进程以释放内存。
Linux 的每个进程都有一个 oom_score (位于/proc/
我们可以调整oom_score_adj的值来防止服务器上比较重要的应用被kill,比如例如 核心的应用进程,我们想尽量避免它被 OOM Killer 杀死,这时候就可以调低它的 oom_score_adj 的值。
参考:
http://senlinzhan.github.io/2017/07/03/oom-killer/ https://www.kernel.org/doc/gorman/html/understand/understand016.html https://www.percona.com/blog/out-of-memory-killer-or-savior/

点个“赞 or 在看” 你最好看!

👇👇👇 下面的犷告咔片谢谢各位老板啦!!!




