一次普通的系统后台维护,可能成为压垮业务节点内存的最后一根稻草。
这次故障发生在一台 Rocky Linux 业务节点上。服务器没有立即宕机,SSH 也并非完全无法连接,但登录、执行命令和访问服务都变得异常缓慢。内核日志显示,Linux 最终通过 OOM Killer 终止了一个 dnf 进程。
表面看,是 dnf 导致了故障;进一步分析后,更准确的结论是:节点原本已经缺少足够的内存余量,周期运行的 dnf makecache 增加了临时内存压力,并可能触发系统进入持续回收和 OOM 处理阶段。
故障现场:服务器还在线,却几乎无法操作
节点出现了几个很典型的症状:
- SSH 建立连接和登录明显变慢;
- 登录后执行
top、ps等普通命令长时间没有响应; - 业务接口出现超时或访问异常;
- 服务器没有立刻重启,网络也没有完全中断。
这种“机器还活着,但什么都做不了”的状态,往往比直接宕机更容易误导排查方向。网络连接仍然存在,并不代表操作系统还有足够资源完成进程创建、内存分配和磁盘访问。
首先检查内核日志:
1 | journalctl -k | grep -i "out of memory" |
日志中出现了类似信息:
1 | Out of memory: Killed process 12345 (dnf) |
这说明系统触发了 OOM Killer,并选择终止当时的 dnf 进程,以释放内存并避免整个内核彻底失去运行能力。
需要注意:日志显示 dnf 被杀,只能证明它是 OOM Killer 选中的受害进程,不能仅凭这一行日志断定 dnf 是内存耗尽的唯一根因。完整复盘还应结合 OOM 日志中的内存统计、进程 RSS、Swap 使用情况、cgroup 限制和业务监控判断。
隐藏在后台的定时任务
业务人员并没有主动执行 dnf。继续检查系统定时器:
1 | systemctl list-timers --all | grep dnf |
可以看到 Rocky Linux 默认启用的定时任务:
1 | dnf-makecache.timer |
查看它的完整配置:
1 | systemctl cat dnf-makecache.timer |
典型配置如下:
1 | [Timer] |
这些配置共同决定了任务的执行节奏:
- 系统启动约 10 分钟后安排执行;
- 服务上一次结束约 1 小时后,再次进入调度周期;
- 在额外 0 至 60 分钟的随机延迟内启动,避免大量机器同时访问软件仓库。
因此,它通常表现为每隔约 1 至 2 小时运行一次。实际时间还会受到定时器状态和系统调度影响,不能简单理解为严格的整点任务。
定时器最终调用:
1 | dnf makecache --timer |
这条命令不会直接升级系统软件,而是检查软件仓库元数据是否需要刷新,并更新本地缓存。它本身属于正常的系统维护行为。
dnf makecache 为什么会占用内存
dnf 并不是一个零成本的后台命令。运行过程中,它需要加载运行环境、读取 RPM 数据库、访问软件仓库、下载或读取元数据,并解析软件包之间的关系。
这些工作都会产生临时的 CPU、内存、网络和磁盘 I/O 开销。正常情况下,这些开销不会影响业务;问题在于,业务节点的内存可能已经长期接近上限。
当节点同时运行应用服务、数据库、缓存服务或监控代理时,内存使用大致会变成:
1 | 业务进程常驻内存 |
此时 dnf 不一定是占用内存最多的进程,却可能成为让系统跨过临界点的新增负载。
还有一种必须排查的情况是 cgroup 或 systemd 服务内存限制。如果 OOM 日志属于某个受限 cgroup,那么主机整体可能仍有内存,真正耗尽的是该服务组的额度。判断故障时,需要区分全局 OOM 和 cgroup OOM。
为什么 dnf 被杀后,服务器仍然很卡
OOM Killer 不是内存不足时的第一步,而是系统在回收压力下仍无法满足内存申请时采取的最后保护措施之一。
故障过程更接近下面这条链路:
1 | 业务进程持续占用内存 |
真正让服务器失去响应的,通常是 OOM 发生前后的持续内存压力,而不是“杀进程”这个动作本身。
如果系统启用了 Swap,内核可能频繁在内存与磁盘之间换页。磁盘延迟远高于内存访问,严重时会出现 Swap 抖动:CPU 并不一定跑满,但大量任务都在等待页面换入或回收,整台机器看起来近乎停顿。
如果没有 Swap,或者 Swap 已经耗尽,新的内存申请可能更快失败。即使 OOM Killer 释放了部分内存,业务进程也可能已经积累了大量请求、超时和重试,系统不会在一瞬间恢复到正常状态。
为什么 SSH 能连接,却执行不了命令
SSH 登录不是一个单纯的网络动作。建立会话后,系统仍需要创建进程、加载动态库、分配内存、读取用户配置,并启动 Shell。
执行一条普通命令同样需要 fork 或创建新进程。内存严重不足时,这些操作可能等待资源回收,甚至直接失败。因此会出现一种很有迷惑性的现象:
1 | TCP 连接正常 |
这并不意味着 SSH 服务本身存在故障。它只是和业务进程一样,被困在系统级资源压力中。
处理方案:关闭不需要的自动缓存刷新
对于不依赖自动软件仓库缓存、软件变更受发布流程严格控制的业务节点,可以评估关闭 dnf-makecache.timer:
1 | systemctl disable --now dnf-makecache.timer |
该命令会立即停止定时器,并取消其开机自动启动。随后确认状态:
1 | systemctl is-enabled dnf-makecache.timer |
前两个命令应分别显示 disabled 和 inactive。最后一条命令没有输出时,说明当前没有匹配的 dnf 定时器处于调度列表中。
需要刷新仓库缓存时,可以在维护窗口手动执行:
1 | dnf makecache |
关闭定时器并不等于解决了全部内存问题。如果节点长期处于高水位,仅仅移除 dnf 后,下一次日志轮转、监控任务、备份或流量波动仍可能触发 OOM。
生产环境还应补齐哪些措施
保留足够的内存余量
业务节点不应长期运行在接近 100% 的内存使用率。容量评估应覆盖业务高峰、应用发布、日志处理、备份、监控和系统维护任务产生的额外开销。
检查 Swap 与内存压力
可以结合以下命令观察内存和换页情况:
1 | free -h |
vmstat 中持续升高的 si、so 表示频繁换页;/proc/pressure/memory 可以反映任务因内存压力而停顿的时间比例。单看 free 一列不足以判断 Linux 是否缺少可用内存,还应关注 available、Swap 和 PSI 指标。
限制非核心任务的资源
对于必须保留的维护任务,可以通过 systemd 资源控制降低其对业务的影响,例如设置内存上限、CPU 权重或 I/O 权重。但限制过低可能导致任务反复失败,因此参数必须经过实际测量和验证。
保留完整的 OOM 证据
不要只截取 Killed process 一行。排查时至少保存:
1 | journalctl -k --since "2026-07-25 00:00:00" |
同时对照监控平台中的可用内存、Swap、磁盘 I/O、进程 RSS、容器或 cgroup 内存、请求延迟和重试量。只有时间线能够互相印证,才能判断 dnf 是根因、触发因素,还是恰好被选中的受害进程。
复盘结论
dnf makecache 不是异常任务,OOM Killer 杀掉 dnf 也不意味着它“杀死了服务器”。这次故障暴露的核心问题,是业务节点没有为周期性系统任务保留足够的资源余量。
关闭不需要的 dnf-makecache.timer,可以减少一次可避免的资源竞争:
1 | systemctl disable --now dnf-makecache.timer |
但更重要的改进,是为生产节点建立系统任务清单、内存水位告警、Swap 与 PSI 监控,以及明确的容量缓冲。后台任务只是触发故障的最后一步,长期缺少资源余量才是需要真正解决的问题。