systemd-logind高负载和内存泄漏问题排查

处理用户的一个线上虚拟机systemd-logind异常消耗CPU资源的异常,记录相关的排查方法以及进一步的学习。

异常现象

  • top显示systemd-loginddbus-daemon两个进程占用了接近100%的CPU资源

top - 18:09:08 up 39 days,  7:25,  2 users,  load average: 3.37, 3.57, 3.59
Tasks: 127 total,   3 running, 124 sleeping,   0 stopped,   0 zombie
%Cpu(s): 49.9 us, 26.5 sy,  0.1 ni, 21.5 id,  0.0 wa,  0.0 hi,  2.0 si,  0.0 st
KiB Mem :  8010880 total,   461688 free,  4174468 used,  3374724 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  2324852 avail Mem 
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 1334 root      20   0  809416 608856   1396 R  99.7  7.6   0:17.46 systemd-logind
  420 dbus      20   0   30252   5144   1120 R  89.3  0.1   5198:35 dbus-daemon
 5953 admin     20   0 4320808 2.126g  11648 S  79.7 27.8 291:06.39 java
 6253 admin     20   0   84312  25208   1320 S   7.0  0.3  18:10.09 tengine
 6252 admin     20   0   86360  24716   1320 S   6.0  0.3  18:19.18 tengine
   17 root      20   0       0      0      0 S   5.7  0.0 194:55.55 rcuos/3
  • ssh登陆非常缓慢,并且登陆后sudo切换账号也非常缓慢(虽然vm的cpu分配了8个,从负载看还是有比较充裕的计算资源)

排查

检查服务

  • 观察到上述systemd-loginddbus-daemon负载过高,观察这两个进程的堆栈

  • 检查dbus服务

可以安康到服务启动运行状态

  • 检查systemd-logind服务

也是启动运行状态

检查journel日志

systemd提供了非常好的日志检查功能命令journalctl

  • journalctl

检查journel日志,发现有大量的 systemd-logind.service watchdog timeout 以及 pam_systemd 创建会话时没有从消息总线接收到响应

  • 通过journalctl检查系统日志可以看到 systemd-logind.service主进程被杀死重启

注意:这里可以看到systemd-logind.service主进程被杀死,并且不断被systemd再次启动。那么我们需要佩查为何操作系统要杀死systemd-logind

  • 持续观察针对某个服务的日志

journalctl提供了指定服务的日志观察方法:

这样可以只观察systmed-logind服务日志。

  • 由于观察到systemd-logind不断重启,我们来观察重启的规律:

可以看到每分钟都出现一次systemd-logind服务被杀死重启:

  • 开启journalctl -f动态查看日志,发现这个系统内存不足,导致不断有javatenginesystemd-logind进程被oom杀死:

仔细检查发现systemd-logind内存泄漏非常快,从top上查看,很快就达到了2G,导致被oom杀掉

使用valgrind排查内存泄漏

systemd-logind位于/usr/lib/systemd目录下,采用以下脚本替换

  • check_systemd-logind:

  • 安装

  • 回退

但是日志显示:

暂时没有找到排查方法

systemd的一些排查方法

systemd提供了一个非常好的工具可以观察cgroup的资源使用

以下观察到内存使用从6.8G突然降低到4.0G是因为systemd-logind被oom-kill导致:

  • systemd-analyze dump提供了dump信息

检查了CentOS的版本,发现原来alios 7.2已经把最新的CentOS 7.4所用的systemd版本backport回来的,最新的systemd版本就是 systemd-219-42.el7_4.4.x86_64.rpm

通过strace排查

  • strace 采用如下方法检查systemd_logind进程访问系统函数情况

检查发现有大量的

然后出现不断重复的

  • 通过strace统计

可以大量的时间消耗在 recvmsg并且有接近1/6的调用错误,而且mkdir系统调用100%错误。

systemd的sessions

为何系统会有重复的

观察了 /run/systemd/sessions 目录下有大量的会话文件

注意,在/run/systemd/sessions目录下,有大量数字字符串命名的文件,以及以c开头的数字字符串命名的文件,其中部分文件还有一个配对的.ref后缀的文件,类似如下:

参考 help understand /var/run/systemd/sessions/2 file / or hacked?

我检查可以看到有2000+个.ref文件

但是,使用loginctl指令无法显示会话,显示消息总线响应超时(Failed to get session: Message did not receive a reply (timeout by message bus))

正常情况下loginctl是可以显示服务器会话的详细信息的,例如:

显然,上述异常服务器中会话管理出现了问题,特别明显的是 message bus 没有响应。

另外,我明白了,为何在前面 strace -c -p $systemd_logind_pid 会出现以下内容

原来这个"# This is private data. Do not p"实际上就是session文件开头的第一行:

也就是说,有大量的会话连接存在。

搜索到CentOS 7的一个bug报告 0014278: systemd update causes /run tmpfs to fill up with sessions,报告称升级了systemd之后/run/systemd/sessions堆积了大量会话文件,导致分区被占满。

类似检查了一下服务器

同样表明/run/systemd/sessions目录下会话文件没有清理。解决的方法是升级了systemd之后重启一次操作系统。

这个问题已经在systemd上游项目中发现,上游systemd版本已经修复。导致的原因是因为systemd丢失了dbus连接。

存在异常的centos 7 服务器,所有 ls -ld /run/systemd/system/session-*.scope* |wc -l 输出结果都是3万以上。

显示大量的用户会话都是root账号。

Bug 1439989 - why so many sessions scope file didn't been deleted? 说明在docker容器中,存在于/run/systemd/systme/目录下会有大量的session-xxx.scope.d文件和目录。而且这个bug报告的系统systemddbus版本恰恰和当前运行服务器版本相同:

这个bug说明中显示,systemd-219-19.el7_2.13容易有泄漏,并且参考 Leak of scope units slowing down "systemctl list-unit-files" and delaying logins #1961 可以看到完全相同的案例:

  • /run/systemd/system目录下有大量泄漏的scope unit文件

  • PID 1使用了100% CPU资源并导致登陆延迟

使用journalctl -b -u systemd-logind观察。

参考 Raman Gupta 2017-04-28 13:24:58 EDT 在 Bug 1271394 - All logins, like ssh, sudo, su take many seconds 提供的解决方法

如果第一个命令systemctl daemon-reload失败,可以尝试一下命令重启:

删除session文件

删除session目录

然后删除abandoned sessions

最后再次尝试上述命令。

不过,实际验证发现systemctl daemon-reload始终超时,无法正确完成。并且systemd-logind的负载不能下降。google发现CentOS的buglist中报告,当前版本的systemd升级后必须重启操作系统。只有上游的最新版本的systemd可以支持升级后无需重启操作系统。

升级systemd解决

果然,升级以后重启操作系统,观察到恢复正常

并且 /run/systemd/sessions 目录下只有少量session

参考

Last updated

Was this helpful?