来源:一起登山 | 发布日期: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 的系统日志守护进程,从三个来源收日志:
- imuxsock — 从
/dev/log收本地进程日志 - imjournal — 从 systemd journal 拉取日志
- 网络 — 收远程机器发来的 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 用~,新版推荐stopcontains "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 文本文件。
问题根因链条
- EDR 安全软件安装 audit 规则监控进程创建(正常安全需求)
- audit 日志级别是
always,所有进程创建全部记录,每小时 28 万条 - 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$"
核心要点
- load average 是排队人数,不是诊室利用率 — 先看
us/sy/id/wa/hi/si/st找到问题方向 - rsyslogd CPU 高 ≠ rsyslogd 本身有 bug — 往往是它处理的内容太多,查 journal 找刷屏来源
- 软中断排查链路长 — 从 si 异常 → NET_RX 集中 → 单 RX 队列 → virtio 限制 → RPS 软件补救
- audit 消息是运维中的隐蔽噪声 — 安全工具产生的审计日志量容易被忽视,稳定地每小时 28 万条
- 排查的第一性原理是不断排除 — 从整体负载 → CPU 拆解 → 进程级别 → 日志来源,像漏斗一样逐层收窄
关联页面
| 页面 | 关联点 |
|---|---|
| linux-load-average-guide | Linux Load Average 概念与解读 |
| linux-load-high-cpu-low-troubleshooting | Load 高但 CPU 低的排查思路 |
| linux-server-load-case-study | 服务器负载过高排查 |
| cpu-spike-troubleshooting-guide | CPU 飙高排查指南 |
| journalctl-log-tracking-guide | journalctl 日志管理完全指南 |
| server-suddenly-slow-troubleshooting-sop | 服务器突然变慢排查 SOP |
| cpu-100-full-chain-diagnosis | CPU 100% 全链路诊断 |
| kernel-log-persistence-guide | 内核日志持久化指南 |