返回首页

系统负载高排查实战 — rsyslogd + audit 日志刷屏案例

📅 创建于 2026-07-31 🔄 更新于 2026-07-31 📝 1417 字

来源:一起登山 | 发布日期:2026-07-02

系统负载高排查实战 — rsyslogd + audit 日志刷屏案例

场景:4 核服务器,load average 7.53,CPU idle 仍有 44.8%。最终根因是 rsyslogd 被 audit 审计消息刷屏(每小时 28 万条),加上单队列网卡软中断热点。处理后负载降至 1.02。

排查漏斗:从整体负载到具体进程

整体负载高 (load 7.53)
  ↓ 拆 %Cpu(s)
CPU 拆解 → si 17.9% + sy 20.9% + rsyslogd 68.8%
  ↓ 分两条线
线 A: rsyslogd CPU 高 → journal 刷屏 → audit 消息
  ↓ 过滤 audit 后 rsyslogd CPU 从 88% → 25%
线 B: si 17.9% → NET_RX 集中 → 单 RX 队列 → RPS 软件补救
  ↓ 负载 7.53 → 1.02

CPU 消耗拆解参考表

top 输出的 %Cpu(s) 行是排查负载问题的第一道门:

字段 全称 正常参考值 排查方向
us user < 50% 用户态程序(Java、nginx、rsyslogd)
sy system < 10% 内核态消耗,偏高指向网络/系统调用
ni nice 0% 调整过优先级的进程
id idle > 30% 空闲占比
wa iowait < 5% 磁盘 I/O 等待,高则 I/O 瓶颈
hi hardirq < 1% 硬中断,略高说明硬件中断频繁
si softirq < 1% 软中断,高则指向网络处理
st steal < 5% 被宿主机偷走的时间,虚拟化环境

关键判断:si 高 + sy 高 + hi 偏高 → 指向网络问题。

线 A:rsyslogd 为什么 CPU 飙到 88%

rsyslogd 是什么

rsyslogd 是 Linux 的系统日志守护进程,从三个来源收日志:

  1. imuxsock — 从 /dev/log 收本地进程日志
  2. imjournal — 从 systemd journal 拉取日志
  3. 网络 — 收远程机器发来的 syslog

收到后按规则写入文件(如 /var/log/messages)或转发到远程。

排查步骤:谁在刷日志

步骤 1:确认 rsyslog 配置

grep -v "^#" /etc/rsyslog.conf | grep -v "^$"

注意 *.debug — 所有 facility 的 debug 及以上级别全部写入 messages,日志等级非常宽松。

步骤 2:看 journal 磁盘占用

journalctl --disk-usage

1.1G 的 journal 文件是危险信号。

步骤 3:找刷屏来源(最关键的步骤)

# 看最近 1 小时谁发消息最多
journalctl --since "1 hour ago" | awk '{print $5}' | sort | uniq -c | sort -rn | head -10

输出:

285194 audit:         ← 罪魁祸首!每小时 28 万条 audit 消息
 14335 audit[897]:    ← auditd 进程本身
  6578 kernel:        ← 内核消息
  5134 audit[2132]:   ← 另一条 audit 规则

注意awk '{print $5} 取第 5 列作为进程名,依赖列宽固定。更稳健的做法是用 journalctl -o json 配合 jq 解析。

步骤 4:看实时消息频率

timeout 5 journalctl -f | wc -l

步骤 5:看最近消息内容

journalctl -n 20 --no-pager

解决方案:过滤 audit 消息

# 新建过滤规则,让 rsyslogd 忽略 audit 消息
echo ':msg, contains, "audit" stop' > /etc/rsyslog.d/ignore-audit.conf
systemctl restart rsyslog

过滤规则说明

  • stop — 匹配的消息就此丢弃,不再处理。旧版 rsyslog 用 ~,新版推荐 stop
  • contains "audit" — 匹配消息体中任意位置含 "audit" 的内容。如需更精确,可按 facility 过滤:if $syslogfacility-text == "authpriv" then stop
  • 不确定是否误伤时,先临时测试:重启后观察几分钟,确认业务日志正常再固化

效果

  • load average 7.53 → 1.02
  • rsyslogd CPU 88.2% → 25.0%
  • si 从 17.9% → 10.6%(软中断问题仍存在,但不再是主要矛盾)

临时操作参考

# 暂停(不杀进程,冻结)
kill -STOP <pid>
# 恢复
kill -CONT <pid>
# 重启
systemctl restart rsyslog

停掉 rsyslogd 不会丢失日志,systemd journal 仍在收日志,只是 journal 中的日志不再同步写到 /var/log/messages 等文本文件。

线 B:软中断 si 为什么高达 17.9%

软中断 vs 硬中断

网卡收到数据包
     ↓
硬中断 (hi)  →  通知 CPU "有数据来了!",非常快(微秒级)
     ↓
软中断 (si)  →  CPU 实际去处理数据包(毫秒级)
类型 耗时 top 指标 说明
硬中断 (hi) 微秒级 hi 硬件通知 CPU
软中断 (si) 毫秒级 si CPU 在处理网络包

si 正常范围

si 占比 判定 说明
< 1% ✅ 正常 大部分服务器
1-5% ⚠️ 偏高 有中等网络流量
5-10% 🔴 高 大流量或单队列瓶颈
10%+ 🔴 严重 需要立即排查

排查步骤

查看网卡中断分布:

cat /proc/interrupts | head -40

关键信号:某个网卡的中断集中在单一 CPU 上,其他 CPU 为 0。

查看软中断类型分布:

cat /proc/softirqs | grep -E "NET_RX|NET_TX|BLOCK|TIMER|TASKLET|RCU|SCHED|HRTIMER"

查看软中断线程 CPU 占用:

ps aux | grep ksoftirqd

单核 ksoftirqd 高(如 42%),其他核空闲 → 单队列瓶颈。

软中断类型参考

类型 功能 排查方向
NET_RX 网卡接收处理 单核突增 → 单队列/RPS
NET_TX 网卡发送处理 一般不异常
BLOCK 块设备 I/O 磁盘 I/O 瓶颈
TIMER 定时器 一般均匀
TASKLET 小任务回调 驱动相关
RCU RCU 回调 一般均匀
SCHED 调度器 一般均匀
HRTIMER 高精度定时器 网络超时伴随现象

根因:单 RX 队列

RX 队列是网卡暂存收到数据包的缓冲区。一个 RX 队列只绑定一个 CPU

# 查看网卡队列数
ethtool -l eth0

Combined: 1 → 只有一个 RX 队列,所有网络包中断全打在同一个 CPU 上。

虚拟化环境常见情况:

环境 网卡类型 队列数 说明
物理机 真实网卡(Intel/Mellanox) 多队列 硬件天然多核分摊
虚拟机 virtio-net 取决于宿主机配置 默认常为 1,需显式开启多队列

RPS 软件补救

RPS(Receive Packet Steering) 是内核的软件机制,在软中断阶段把数据包重新哈希到多个 CPU 上处理。

# 允许 CPU0-3 共同处理 eth0 接收队列的软中断
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
# 设置流数量
echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
echo 32768 > /proc/sys/net/core/rps_sock_flow_entries

一句话对比:多队列网卡是"硬件层面开多个窗口",RPS 是"软件层面让一个窗口的餐分给多个服务员搬"。

注意:RPS 对单队列网卡效果有限(哈希冲突率高),真正的根治需要在宿主机层增加 virtio 队列数。

Nginx Worker 的 D 状态

D 状态(Uninterruptible Sleep,不可中断睡眠)的进程也会计入 load average,导致负载虚高。

状态 符号 含义 常见场景
R Running/Runnable 正在运行或可运行 正常处理请求
S Sleeping 可中断睡眠,等事件唤醒 等待网络请求
D Uninterruptible Sleep 不可中断睡眠,等 I/O 完成 磁盘读写、NFS 卡住
Z Zombie 僵尸进程 父进程未调用 wait()
T Stopped 被暂停 调试中
I Idle 内核空闲线程 kworker 等

D 状态为什么危险kill -9 也杀不掉,只能等 I/O 完成。NFS 挂载卡住或磁盘故障时,D 状态进程会一直卡着,导致负载虚高。

auditctl 与审计日志

auditctl 是什么

auditctl 是 Linux 审计系统的控制命令,管理内核审计规则,决定"谁做了什么操作"需要记录。

本次看到的规则:

$ auditctl -l
-a never,exit -F arch=b64 -S clone,execve -F exe=/usr/local/dbappsecurity/edr/agent_service -F key=ahedr-proc
-a always,exit -F arch=b64 -S clone,execve -F key=ahedr-proc
规则 含义
-a always,exit -S clone,execve 监控所有 64 位系统的进程创建和执行
-a never,exit ... -F exe=.../edr/agent_service 排除 EDR 安全软件自身

阻断 audit 消息写入 rsyslog 的实际影响

操作 影响 安全效果
✅ 改 rsyslog 过滤 audit audit 规则仍在执行,只是不写文件 EDR 正常工作
❌ 删 auditctl 规则 不监控进程创建 有安全隐患

仍然保留的:

  • ✅ auditctl 规则仍在执行,EDR 安全软件正常工作
  • ✅ audit 日志保留在 journal 中:journalctl _TRANSPORT=audit
  • ✅ 其他日志(kernel、cron、mail、authpriv)不受影响

唯一损失:audit 消息不再写入 /var/log/messages 文本文件。

问题根因链条

  1. EDR 安全软件安装 audit 规则监控进程创建(正常安全需求)
  2. audit 日志级别是 always,所有进程创建全部记录,每小时 28 万条
  3. rsyslog 配置了 *.debug 级别,imjournal 从 journal 拉取所有消息,包括这些 audit 信息,全部写入 messages

前两个是必要的安全措施,第三个才是真正的问题 — *.debug 太宽松了,且没有对 audit 这类高频消息做过滤。

批量排查其他服务器

# 1. 查 journal 大小(>500M 就有嫌疑)
journalctl --disk-usage | awk '{print $NF}'

# 2. 查最近 1 小时的前 3 个日志来源
journalctl --since "1 hour ago" | awk '{print $5}' | sort | uniq -c | sort -rn | head -3

# 3. 查 rsyslogd CPU
ps aux | grep rsyslogd | grep -v grep | awk '{print $1, $3, $11}'

# 4. 一行脚本批量检查
for host in server1 server2 server3; do
  echo "=== $host ==="
  ssh $host "journalctl --disk-usage | tail -1; echo '---TOP3---'; journalctl --since '1 hour ago' | awk '{print \$5}' | sort | uniq -c | sort -rn | head -3; ps aux | grep [r]syslogd | awk '{print \$1, \$3}'"
done

异常判断标准

指标 正常 异常
journal 大小 < 500M > 1G
rsyslogd CPU < 2% > 10%
每小时 TOP1 消息数 < 1000 > 10000(可能是 audit)
audit 消息占比 - > 50%(需要过滤)

一键排查脚本

#!/bin/bash
# sysload-check.sh — 系统负载高一键排查脚本

echo "=== 1. 基础信息 ==="
lscpu | grep -E "^(CPU\(s\)|Model name)"
uptime

echo "=== 2. CPU 消耗拆解 ==="
top -bn1 | head -5 | tail -3

echo "=== 3. 进程 TOP10 ==="
ps aux --sort=-%cpu | head -11

echo "=== 4. 软中断分布 ==="
cat /proc/softirqs | grep -E "NET_RX|NET_TX"

echo "=== 5. 网卡中断分布 ==="
cat /proc/interrupts | head -45

echo "=== 6. 磁盘 I/O ==="
iostat -x 1 3

echo "=== 7. 网络流量 ==="
sar -n DEV 1 3

echo "=== 8. 日志分析 ==="
journalctl --disk-usage
echo "TOP10 log sources (last 1 hour):"
journalctl --since "1 hour ago" | awk '{print $5}' | sort | uniq -c | sort -rn | head -10

echo "=== 9. D 状态进程 ==="
ps aux | awk '$8 ~ /D/'
echo "D state wchan:"
ls -la /proc/*/wchan 2>/dev/null | grep -v "0$"

核心要点

  1. load average 是排队人数,不是诊室利用率 — 先看 us/sy/id/wa/hi/si/st 找到问题方向
  2. rsyslogd CPU 高 ≠ rsyslogd 本身有 bug — 往往是它处理的内容太多,查 journal 找刷屏来源
  3. 软中断排查链路长 — 从 si 异常 → NET_RX 集中 → 单 RX 队列 → virtio 限制 → RPS 软件补救
  4. audit 消息是运维中的隐蔽噪声 — 安全工具产生的审计日志量容易被忽视,稳定地每小时 28 万条
  5. 排查的第一性原理是不断排除 — 从整体负载 → CPU 拆解 → 进程级别 → 日志来源,像漏斗一样逐层收窄

关联页面

页面关联点
linux-load-average-guideLinux Load Average 概念与解读
linux-load-high-cpu-low-troubleshootingLoad 高但 CPU 低的排查思路
linux-server-load-case-study服务器负载过高排查
cpu-spike-troubleshooting-guideCPU 飙高排查指南
journalctl-log-tracking-guidejournalctl 日志管理完全指南
server-suddenly-slow-troubleshooting-sop服务器突然变慢排查 SOP
cpu-100-full-chain-diagnosisCPU 100% 全链路诊断
kernel-log-persistence-guide内核日志持久化指南