🚨 惊魂时刻:当你发现 systemd 进程占用了 7.4GB 内存时,第一反应是:“我是不是看错了?”
作为一名在 Linux 系统运维一线摸爬滚打多年的工程师(是的,打杂的娃头衔就是这么多变),我以为自己见过各种奇葩问题。直到那个平静的周三早晨,我在进行日常系统巡查时,发现测试环境服务器的内存使用异常——系统内存使用率居然达到了 65%!
当我登录服务器查看 top 命令时,看到的景象让我大吃一惊:systemd 进程竟然吃掉了 7.4GB 内存!要知道,这个家伙平时安安静静地只用几十兆内存,现在却成了内存大胃王。
虽然这只是测试环境,但这个问题给了我们一个重要警示:如果同样的问题发生在生产环境,后果可能不堪设想。这次的排查经历不仅解决了当前问题,更为生产环境的部署和运维策略提供了宝贵的参考价值。
目录
- 故事背景:周三早晨的例行巡查
- 第一反应:这不科学!
- 开始侦探工作:一步步缩小包围圈
- 3.1 嫌疑人一号:进程内存分析
- 3.2 嫌疑人二号:日志系统是不是搞鬼了
- 3.3 嫌疑人三号:会不会是版本太老了
- 真相大白:内存泄漏这个老朋友
- 救火队出动:解决方案登场
- 5.1 先救命:一招制敌
- 5.2 再治本:长期防护措施
- 未雨绸缪:如何预防类似问题
- 收工总结:这次踩坑学到了什么
1. 故事背景:周三早晨的例行巡查
先交代一下案发现场的基本情况,毕竟知己知彼才能百战不殆嘛。
我们的受害者是一台运行 AlmaLinux 10.0 的服务器,这可是个挺新的发行版,按理说不应该有什么古怪问题。
cat /etc/os-release
详细的身份信息:
NAME="AlmaLinux"
VERSION="10.0 (Purple Lion)"
ID="almalinux"
ID_LIKE="rhel centos fedora"
VERSION_ID="10.0"
PLATFORM_ID="platform:el10"
PRETTY_NAME="AlmaLinux 10.0 (Purple Lion)"
ANSI_COLOR="0;34"
LOGO="fedora-logo-icon"
CPE_NAME="cpe:/o:almalinux:almalinux:10::baseos"
HOME_URL="https://almalinux.org/"
DOCUMENTATION_URL="https://wiki.almalinux.org/"
VENDOR_NAME="AlmaLinux"
VENDOR_URL="https://almalinux.org/"
BUG_REPORT_URL="https://bugs.almalinux.org/"
ALMALINUX_MANTISBT_PROJECT="AlmaLinux-10"
ALMALINUX_MANTISBT_PROJECT_VERSION="10.0"
REDHAT_SUPPORT_PRODUCT="AlmaLinux"
REDHAT_SUPPORT_PRODUCT_VERSION="10.0"
SUPPORT_END=2035-06-01
💡 小插曲:AlmaLinux 是 CentOS 的后继者之一,有着到 10 年的长期支持,算是挺靠谱的企业级发行版。
2. 第一反应:这不科学!
周三上午 9:30,我照例开始进行服务器集群的日常巡查。这是我们团队的标准流程:每天早上检查一遍所有环境的健康状况,包括开发、测试、预发布等各个环境,确保各个团队都能正常工作。
当我打开监控面板时,一个异常的数字引起了我的注意:
🚨 测试服务器内存使用率: 65% (正常应该在 30% 左右)
我立即 SSH 连接到服务器,第一时间就是看 top 命令,然后我就愣了:
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 7.3g 7.4g ??? S 2.1 46.3 12:34.56 systemd
7.4GB???
我揉了揉眼睛,再看一遍。没错,systemd 这个家伙真的吃了 7.4GB 内存!
😱 心理活动:我的第一反应是"难道我看错了?“,然后是"难道服务器被黑了?”,接着是"难道 systemd 进化了?"
要知道,systemd 作为系统的 1 号进程,平时就是个安安静静的管家,顶多用个几十 MB 内存。现在突然变成了内存大胃王,这不是要命嘛!
3. 开始侦探工作:一步步缩小包围圈
冷静下来之后,我开始像个侦探一样分析这个异常现象。既然 systemd 占用了这么多内存,那肯定有原因,我们一个个排查。
3.1 嫌疑人一号:进程内存分析
首先,我要确认这个数字是不是真的。有时候 top 命令会有误报,所以我直接去 /proc 目录下看第一手资料:
cat /proc/1/status | grep -E 'VmRSS|VmSize'
结果更加残酷:
VmSize: 7433836 kB # 虚拟内存大小:~7.3 GB
VmRSS: 7424676 kB # 实际物理内存:~7.4 GB
😨 更惊恐的发现:虚拟内存和实际物理内存几乎一样大!这说明这 7.4GB 不是虚张声势,而是实实在在被占用了。
这下我心里更慌了。如果是虚拟内存大但实际内存小,还可能是内存映射的问题。现在连 RSS 都这么大,说明这些内存是真的被 systemd 给霸占了。
3.2 嫌疑人二号:日志系统是不是搞鬼了
我的第二个怀疑对象是 journald。这家伙负责收集系统日志,如果日志爆炸了,确实可能导致内存使用异常。
# 看看日志到底占了多少空间
journalctl --disk-usage
# 再仔细检查一下具体目录
du -sh /run/log/journal/*
结果让我松了一口气:
Archived and active journals take up 415.9M in the file system.
416M /run/log/journal/d97bb41f000843d19865fcf40fed142b
🤔 推理过程:416MB 的日志文件绝对解释不了 7.4GB 的内存占用。即使 journald 把日志全部加载到内存里,也不可能有这么大的差距。
看来日志系统是清白的,继续找下一个嫌疑人。
3.3 嫌疑人三号:会不会是版本太老了
第三个怀疑对象就是 systemd 本身的版本问题。有些老版本的软件确实会有内存泄漏的 bug。
systemctl --version
版本信息显示:
systemd 257 (257-9.el10_0.1.alma.1-g1905557)
💭 分析思路:systemd 257 是一个相当新的版本,发布时间不久,按理说不应该有这种低级的内存泄漏问题。而且如果是版本 bug,网上应该已经有一堆人在抱怨了。
到这里,三个主要嫌疑人都被排除了,看来这个案子没有那么简单…
4. 真相大白:内存泄漏这个老朋友
经过一番排查,所有常见的嫌疑人都被排除了。这时候,一个更加阴险的可能性浮现在我脑海里:内存泄漏。
🕵️ 福尔摩斯时刻:当你排除了所有不可能的情况,剩下的即使再不可思议,也必然是真相。
分析到这里,情况已经很明朗了:
被排除的无辜群众:
- ❌ 日志文件过大(人家只有 416 MB,很乖的)
- ❌ 版本太老(257 版本挺新的,不背这个锅)
- ❌ 配置有问题(检查了一圈都正常)
真正的罪魁祸首:
- ✅ systemd/journald 内存泄漏:内部某个地方在偷偷囤积内存
- ✅ 内存管理 bug:分配了内存却忘记释放
💡 技术分析:systemd 在处理系统事件、服务状态变化、日志记录等操作时,由于某个内部 bug,导致分配的内存没有正确释放。时间一长,这些"僵尸内存"就越积越多,最终变成了 7.4GB 的庞然大物。
说白了,就是 systemd 得了"健忘症",只记得要内存,忘记了还内存。(其实,我也只是怀疑,没有更确切的证据)
5. 救火队出动:解决方案登场
5.1 先救命:一招制敌
现在服务器内存快爆了,必须先解燃眉之急。对于这种内存泄漏问题,有一个简单粗暴但非常有效的方法:
sudo systemctl daemon-reexec
🎯 这招的精髓:让 systemd 重新执行自己,相当于给它做了个"内存重置",但又不会影响正在运行的服务。
为什么这招管用:
- 重置进程状态:清空所有累积的内存垃圾
- 保持服务连续性:不会杀死其他系统服务
- 立竿见影:几秒钟就能看到效果
我忐忑不安地敲下这个命令,然后迅速查看内存使用:
$ top
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1 root 20 0 47m 32m ??? S 0.1 0.4 0:02.15 systemd
奇迹发生了! 从 7.4GB 瞬间降到了 32MB,systemd 又变回了那个安静的小透明。
✅ 救火成功:内存使用率从 65% 降到了 18%,系统恢复正常。我长长地舒了一口气。
5.2 再治本:长期防护措施
虽然 daemon-reexec 解决了燃眉之急,但我们不能每次都靠这种"重启大法"。要想从根本上预防这种问题,还需要做一些长期的防护工作。
5.2.1 给 journald 戴上"紧箍咒"
虽然这次不是 journald 的锅,但既然 systemd 家族容易出问题,我们就给日志系统也设个限制,防患于未然:
sudo vi /etc/systemd/journald.conf
防护配置:
[Journal]
# 日志磁盘使用上限
SystemMaxUse=500M
# 内存使用上限
RuntimeMaxUse=200M
# 单个日志文件大小限制
SystemMaxFileSize=50M
# 日志保留时间(别让古董日志堆积)
MaxRetentionSec=1month
设置完后重启一下 journald:
sudo systemctl restart systemd-journald
💡 设计思路:即使以后 journald 也"健忘"了,最多也就占用 200MB 内存,翻不起大浪。
5.2.2 开启内存监控功能
为了以后能更好地监控各个服务的内存使用,我们开启 systemd 的内存统计功能:
sudo vi /etc/systemd/system.conf
监控配置:
[Manager]
# 开启内存统计(知己知彼)
DefaultMemoryAccounting=yes
# 顺便开启 CPU 统计
DefaultCPUAccounting=yes
让配置生效:
sudo systemctl daemon-reexec
这样以后就可以用 systemctl status 看到每个服务的详细资源使用情况了。
5.2.3 系统更新策略
既然这是测试环境,我们可以放心地进行系统更新来修复这个 bug:
# 专门更新 systemd
sudo dnf update systemd
# 全系统更新
sudo dnf update
🔄 测试环境 vs 生产环境:
- 测试环境:可以积极更新,验证补丁效果
- 生产环境:应该谨慎更新,优先通过配置和监控来规避风险
这次在测试环境发现问题,给了我们宝贵的时间来制定生产环境的应对策略。
6. 未雨绸缪:如何预防类似问题
经历了这次测试环境的意外发现,我深深意识到:预防比治疗更重要。更关键的是,我们需要为生产环境制定相应的防护策略,避免类似问题在生产环境中出现。
6.1 智能监控脚本:会自救的系统
我写了一个小脚本,让它定期检查 systemd 的内存使用,一旦发现异常就自动执行 daemon-reexec。相当于给系统配了个"自动医生"。
sudo vi /usr/local/bin/systemd-memory-monitor.sh
"自动医生"脚本:
#!/bin/bash
# systemd 内存监控与自动修复脚本
# 设计目标:测试环境自动化,为生产环境提供参考
# 配置参数
RSS_LIMIT_MB=512 # 内存报警阈值:512 MB
LOG_FILE="/var/log/systemd-memory-monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
# 获取 systemd 当前内存使用(单位:MB)
RSS_KB=$(awk '/VmRSS/{print $2}' /proc/1/status)
RSS_MB=$((RSS_KB / 1024))
# 记录日常体检结果
echo "$DATE - systemd 内存体检: ${RSS_MB}MB" >> "$LOG_FILE"
# 如果内存使用超标,立即处理
if [ "$RSS_MB" -gt "$RSS_LIMIT_MB" ]; then
echo "$DATE - [🚨 警报] systemd 内存异常: ${RSS_MB}MB (超过阈值 ${RSS_LIMIT_MB}MB)" >> "$LOG_FILE"
echo "$DATE - [💊 治疗] 执行 systemctl daemon-reexec 自动修复" >> "$LOG_FILE"
# 执行自动修复
systemctl daemon-reexec
# 等待几秒让系统稳定
sleep 5
# 检查治疗效果
NEW_RSS_KB=$(awk '/VmRSS/{print $2}' /proc/1/status)
NEW_RSS_MB=$((NEW_RSS_KB / 1024))
echo "$DATE - [✅ 结果] 修复后内存使用: ${NEW_RSS_MB}MB" >> "$LOG_FILE"
# 发送系统通知(这样就能在系统日志里看到)
logger "systemd-auto-doctor: 发现内存异常(${RSS_MB}MB),自动修复完成,当前使用: ${NEW_RSS_MB}MB"
fi
给脚本加上执行权限:
sudo chmod +x /usr/local/bin/systemd-memory-monitor.sh
6.2 定时体检:让系统自己照顾自己
然后我用 systemd 的 timer 功能,让这个"自动医生"每 5 分钟检查一次:
创建服务文件:
sudo vi /etc/systemd/system/systemd-memory-monitor.service
[Unit]
Description=Systemd Memory Auto Doctor
After=multi-user.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/systemd-memory-monitor.sh
User=root
StandardOutput=journal
StandardError=journal
创建定时器:
sudo vi /etc/systemd/system/systemd-memory-monitor.timer
[Unit]
Description=每5分钟进行systemd内存体检
Requires=systemd-memory-monitor.service
[Timer]
OnBootSec=5min # 开机5分钟后开始
OnUnitActiveSec=5min # 每5分钟执行一次
AccuracySec=1min # 时间精度1分钟
[Install]
WantedBy=timers.target
启动自动医生:
sudo systemctl daemon-reload
sudo systemctl enable systemd-memory-monitor.timer
sudo systemctl start systemd-memory-monitor.timer
这样,在测试环境中,即使 systemd 再次"健忘",系统也能自己发现并自动修复。
💡 分层防护策略:
- 测试环境:积极的自动化修复,快速验证解决方案
- 生产环境:保守的监控告警,人工干预确保安全
测试环境的自动化实践为生产环境的部署策略提供了宝贵的经验。
7. 收工总结:这次踩坑学到了什么
回顾这整个惊心动魄的故障处理过程,虽然过程有点刺激,但收获还是很大的。让我来总结一下这次踩坑的经验教训。
7.1 故障处理心得
🎯 核心经验:冷静分析 + 排除法 + 快速止损 + 长期预防
诊断三步曲:
- 先救命:
systemctl daemon-reexec让系统快速恢复 - 找真凶:通过
/proc/1/status精确定位内存使用 - 除后患:建立监控和自动化处理机制
我学到的排查套路:
- 数据说话:不要凭感觉,要用具体的数字和日志
- 排除法:一个个排除可能的原因,缩小包围圈
- 先止损再分析:生产环境故障时,先恢复服务再深入分析
7.2 运维哲学思考
这次事件让我重新思考了一些运维理念:
“测试环境的价值”:
- 测试环境不只是用来跑功能测试,也是发现系统问题的重要阵地
- 在测试环境发现问题比在生产环境发现问题要幸运得多
- 测试环境的问题处理经验直接指导生产环境的预防策略
“分层防护理念”:
- 测试环境可以激进一些,快速验证解决方案
- 生产环境必须保守一些,稳定性优于一切
- 好的运维策略是在稳定性和效率之间找到平衡
7.3 实用经验清单
给遇到类似问题的兄弟们一个可以直接抄作业的清单:
| 步骤 | 命令/操作 | 用途 |
|---|---|---|
| 确认问题 | cat /proc/1/status | grep VmRSS | 精确测量内存使用 |
| 排除日志 | journalctl --disk-usage | 检查日志文件大小 |
| 检查版本 | systemctl --version | 确认是否为已知bug版本 |
| 应急修复 | systemctl daemon-reexec | 无损重置systemd内存 |
| 长期预防 | 配置监控脚本 | 自动发现和修复 |
7.4 最后的思考
💭 感悟时刻:每一次故障都是系统在教我们如何变得更强。
这次 systemd 内存泄漏事件,让我意识到:
- 测试环境的重要性:它是发现问题的第一道防线,为生产环境提供预警
- 分层思维的价值:测试环境和生产环境的处理策略应该有所不同
- 问题的传递性:测试环境的问题往往预示着生产环境的潜在风险
- 预防的经济性:在测试环境解决问题比在生产环境解决问题成本低得多
🔥 最重要的领悟:真正的运维价值不在于避免所有问题,而是在于建立有效的问题发现和处理机制,让问题在最合适的环境中被解决。
希望我的这次测试环境问题排查经历能给大家一些启发。测试环境的每一个问题,都是生产环境稳定运行的保障!
💡 写在最后:如果你也遇到了类似的 systemd 内存问题,记得先用
daemon-reexec救急,然后建立监控机制。有问题欢迎交流讨论!
👋 作者信息:码农秋 | 发布日期:2025-08-27 | 一个热爱分享的全栈架构师
1198

被折叠的 条评论
为什么被折叠?



