journald 日志 232M,97% 来自 audit:先限额再清旧
加固完的机器过了十二天,df -h 还算体面:40G 的盘用了 8.2G,22%。但 du 一跑,/var/log 已经 306M,而其中 282M 是同一批事件的两份拷贝:
$ du -sh /var/log /var/log/journal /var/log/audit
306M /var/log
233M /var/log/journal
49M /var/log/audit
这台机器只有 1.7G 内存、跑两个容器,一个月能产生多少日志、什么时候会撞上 40G 的天花板,靠猜没意义。下面是把这件事量清楚、再收口的完整过程,数据全部来自这台机器 2026-11-07 的实测。
一、先量,别先改配置
三条命令,三十秒,能把「日志到底存在哪、有多大、谁写的」定下来。
# 1. journal 的实际占用(比 du 准,journald 自己算的)
journalctl --disk-usage
# Archived and active journals take up 232.0M in the file system.
# 2. 文件个数与轮转痕迹,能直接看出单文件上限
sudo ls -lht /var/log/journal/*/*.journal
# -rw-r-----+ 1 root systemd-journal 96M Nov 7 08:29 .../system.journal
# -rw-r-----+ 1 root systemd-journal 128M Nov 1 13:26 .../system@...0e23....journal
# -rw-r-----+ 1 root systemd-journal 8.0M Oct 26 09:03 .../system@...08e8....journal
# 3. 配置里到底写了什么(只留有效行)
grep -vE '^\s*#|^\s*$' /etc/systemd/journald.conf
# [Journal]
第三条的输出最值得多看一眼:[Journal] 下面一行配置都没有。这台机器上 journald 跑的全是默认值。第 2 条输出里的那两行也说明问题:128M 那份在 11 月 1 日 13:26 写好封存,96M 这份是当前活跃文件——单文件在 128M 处轮转。
这里有个坑我踩过:按 man 里「SystemMaxFileSize 默认取 SystemMaxUse 的 1/8」去推,40G 盘的 SystemMaxUse 是 4G,1/8 应该是 512M,跟实际观察到的 128M 对不上。类似这种「默认值靠推算」的判断,不如直接看文件大小和 journalctl --header --file=... 的输出。默认值会随 systemd 版本变,文件大小不会骗人。
顺带记一个内存数字:
$ systemctl show systemd-journald -p MemoryCurrent
MemoryCurrent=148045824
约 141MiB。1.7G 的机器上,一个日志守护进程常驻 140M 不算小数,这也解释了为什么这台机器要开 swap——那是另一个话题了。
二、97% 的行来自 audit
知道有 232M 之后,下一步是「谁写的」。journalctl 的 _TRANSPORT 字段把来源分得很干净:
for t in audit kernel syslog stdout journal driver; do
printf '%-8s ' "$t"
journalctl _TRANSPORT=$t --since "7 days ago" -o cat --no-pager | wc -l
done
# audit 134524
# kernel 5
# driver 0
# syslog 969
# stdout 291
# journal 2383
同期总行数 138029,audit 占 97.4%。余下的 systemd 自身消息(journal,2383)、syslog 转发(969)、容器 stdout(291)加起来不到 3%。
再往下钻一层,看 audit 这 13 万行是什么类型:
journalctl _TRANSPORT=audit --since "1 day ago" -o cat --no-pager \
| sed -E 's/^audit\[[0-9]+\]: //' | awk '{print $1}' | sort | uniq -c | sort -rn | head
# 5776 PATH
# 4558 SYSCALL
# 4558 PROCTITLE
# 4375 CWD
# 120 NETFILTER_CFG
# 119 SOCKADDR
一天 4558 条 syscall 事件,被拆成 SYSCALL / PATH / CWD / PROCTITLE 四类、合计约一万九千行落进 journal。这就是「行数」和「事件数」的差别:人看日志,一条事件就是一行 USER_START;系统记账,一条事件是四五个对象。
谁是触发者?
journalctl _TRANSPORT=audit --since "1 day ago" -o cat --no-pager \
| grep -o 'exe="[^"]*"' | sort | uniq -c | sort -rn | head -4
# 4536 exe="/usr/lib/systemd/systemd"
# 156 exe="/usr/sbin/cron"
# 145 exe="/usr/sbin/sshd"
# 119 exe="/usr/sbin/xtables-nft-multi"
答案有点扫兴:绝大部分是 systemd 自己。这台机器上有 43 条 audit 规则,其中一批是文件监视:
auditctl -l | head -4
# -w /root/.ssh/authorized_keys -p wa -k ssh_authkeys
# -w /etc/passwd -p wa -k identity
# -w /etc/shadow -p wa -k identity
# -w /etc/group -p wa -k identity
-w ... -p wa 这类规则抓的是「写 + 属性变更」,本意是抓篡改。想法没错,但它对谁写的不设限,systemd 在正常运维里摸这些文件(用户/组同步、运行目录初始化),一条动作就会展开成一串 SYSCALL + 若干 PATH + CWD,全部进 journal。
所以「journal 膨胀」这件事,配置只占一半,另一半是审计规则的粒度。
三、为什么落盘是文本的 8 倍
同样七天,-o cat 打出来的纯文本是 17,596,690 字节,约 16.8M;折算下来文本 2.4M/天、落盘 19.4M/天,差八倍。
差值不是压缩没开。文件头里明写着 Incompatible flags: COMPRESSED-ZSTD KEYED-HASH,压缩是生效的。开销在结构上——journalctl --header 里有两个数字很说明问题:
Field hash table size: 333
Data hash table size: 233016
Objects: 222043
Entry objects: 114579
字段名只有 333 种(高度复用、去重存储),但值有 23 万个槽位。每条记录除了内容本身,还要带索引、时间戳、boot id、machine id 这一套。折成「每条记录实际占多少字节」,比看纯文本大一个量级是常态。
这一点对容量规划有用:不要拿 journalctl -o cat | wc -c 的结果去估 journal 占用,会严重低估。
四、限额:用 drop-in,不要动主配置
/etc/systemd/journald.conf 是包管理器管的文件,unattended-upgrades 升级 systemd 时可能提示合并。正确做法是写 drop-in,让发布版的文件保持原样。
sudo mkdir -p /etc/systemd/journald.conf.d
sudoedit /etc/systemd/journald.conf.d/99-size.conf
内容:
[Journal]
# 持久化到 /var/log/journal(目录存在时 auto 也会持久化,显式写出来更清楚)
Storage=persistent
# 上限:给 journal 400M,40G 盘的 1%,够查两周,也不至于挤占业务
SystemMaxUse=400M
# 单文件 64M,轮转粒度细一点,vacuum 才不会一次砍掉半周日志
SystemMaxFileSize=64M
# 保留最少 3 个文件,避免日志太少时被清空
SystemMaxFiles=6
# 内存态(未落盘前)也有上限,防止短时洪峰把内存吃掉
RuntimeMaxUse=64M
# 限流:默认 30 秒内 10000 条,这里收紧到 2000 条,先保证不丢关键信息
RateLimitIntervalSec=30s
RateLimitBurst=2000
改完不要急着重启,先看最终生效值——cat-config 会把主配置和所有 drop-in 按优先级拼出来给你看,这是排查「改了没生效」最快的办法:
systemd-analyze cat-config systemd/journald.conf
确认拼出来的值是你想要的,再动手:
sudo systemctl restart systemd-journald
journalctl --disk-usage
sudo systemctl status systemd-journald --no-pager | head -5
systemctl status 的输出里会打印一行类似 SystemMaxUse=419430400 的 limits 摘要,这是 journald 自己认定的上限,比配置文件更可信。
三个参数怎么定,我的取法是:
SystemMaxUse取磁盘的 1%,不要取默认的 10%。默认值在小盘上问题不大,在大盘上相当于默许日志吃掉几十 G。SystemMaxFileSize手动设成SystemMaxUse的 1/6 左右。文件太大,--vacuum-size一次就得删掉一整周的记录;文件太小,文件数量涨上去,ls的时候眼睛疼。RuntimeMaxUse一定要设。它在内存里兜底,洪峰时先丢日志而不是 OOM。
五、顺序错了等于白干:先限额,再 vacuum
这是最容易做反的一步。很多人看到 --disk-usage 报了 232M,直接:
sudo journalctl --vacuum-size=100M
然后一小时后发现又涨回去了——因为上限还是 4G,journald 有空间就继续写。正确的顺序是先改配置重启,再清旧:
# 1. 先让上限生效(上一节的 drop-in 已写好并重启)
# 2. 再清旧文件
sudo journalctl --vacuum-size=300M
sudo journalctl --vacuum-time=14d
sudo journalctl --vacuum-files=6
# 3. 复核
journalctl --disk-usage
三条 vacuum 是「或」的关系,按需求挑一条用就行,连着跑会以前一条的结果为基础继续删。--vacuum-time=14d 我一般会留着,配合 SystemMaxFiles 一起用:前者控时间跨度,后者控文件个数,两个维度都兜住。
一个版本边界:systemd 249(22.04 自带)的 journalctl 没有 --dry-run,journalctl --help | grep dry-run 是空的。想先看会删什么,只能靠 ls -lht 看文件列表和日期。--dry-run 是 250 之后才有的,别照抄新版本的教程。
六、回到 audit 侧做减法
配完 400M 上限,这台机器的 journal 会从 4G 的潜在水位压到 400M,问题算是「不会炸」。但如果 audit 还是每天一万九千行,400M 只够撑二十天,查日志的体验也照样糟糕。真正的减法在 audit。
先确认账本对不对:/var/log/audit/audit.log 和 journal 里那 97% 其实是同一批事件,前者由 auditd 自己写,后者走 kernel audit 到 journald。这台机器上前者已经 49M 单文件、还没轮转:
$ ls -lh /var/log/audit/
-rw-r----- 1 root adm 49M Nov 7 08:30 audit.log
auditd 的默认轮转是 8M、留 5 份,这里显然改过。要么接受两份拷贝(journal 一份供 journalctl -k 式检索,audit.log 一份给合规留存),要么只留一份。我倾向于两份都留但都限住,因为 journal 的检索体验确实好得多,而合规审计要的是一份不可随手改的文本。
限 audit 的写盘:
sudoedit /etc/audit/auditd.conf
max_log_file = 40
num_logs = 5
max_log_file_action = ROTATE
space_left_action = SYSLOG
admin_space_left_action = SUSPEND
space_left_action = SYSLOG 的意思是磁盘紧时只发系统日志、不停审计;admin_space_left_action = SUSPEND 才是最后的刹车。这两个的组合决定了「日志写不下时机器是报警还是罢工」,生产机上别用默认的 SUSPEND 一把梭。
再回到规则本身。-w /etc/shadow -p wa 这种全量文件监视,在「只想抓篡改」的场景下太粗——它连 systemd 的正常写入都抓。可行的替代是把规则从「监文件」换成「监关键命令」,并把排除项放在前面:
# 排除 systemd 自身对 identity 类文件的写入,必须写在 always 规则之前
-a never,exit -F arch=b64 -F path=/etc/shadow -F perm=wa -F exe=/usr/lib/systemd/systemd -k identity
# 只留真正关心的:authorized_keys 的任何改动
-w /root/.ssh/authorized_keys -p wa -k ssh_authkeys
-a never 是按加载顺序生效的:augenrules 会按文件里的顺序拼接规则,never 规则必须排在对应的 always 规则前面,写反了等于没写。这一点没有捷径,改完只能实测——auditctl -l 看加载顺序,隔一天再统计 exe= 分布,看 systemd 那 4500 条有没有降下去。
要提醒一句:审计规则不是越多越好,也不是越少越好。有合规要求(等保、等级保护测评)的机器,-w /etc/passwd -p wa 这类是必查项,不能为了省日志删掉。能不能删、删哪条,得看你的责任范围,我这里只能说清「它一天产生多少行」。
七、报错对照
| 现象 | 原因 | 处理 |
|---|---|---|
journalctl 只有本次开机的日志,重启就没了 |
Storage=volatile,或 /var/log/journal 不存在导致退回内存态 |
sudo mkdir -p /var/log/journal 后 sudo systemd-tmpfiles --create --prefix /var/log/journal,再重启 journald |
改了 journald.conf 完全没反应 |
drop-in 里同名项后加载的覆盖了主配置,或忘了 restart | systemd-analyze cat-config systemd/journald.conf 看拼接结果,再 systemctl restart systemd-journald |
内存态限制写了 RuntimeMaxUse,实际仍涨 |
只改了 SystemMaxUse,两个是不同的池 |
两个都写;RuntimeMaxUse 管 /run/log/journal |
--vacuum-size 后一小时又涨回原样 |
只清旧没改上限,journald 有空间就继续写 | 先落 drop-in 并重启,再 vacuum |
日志里出现 Suppressed N messages |
触发 RateLimitIntervalSec / RateLimitBurst |
先查是谁在刷(按 _TRANSPORT 或 unit 排行),定位后再决定是否放宽限流 |
普通用户执行 journalctl 提示 No journal files were found |
用户不在 systemd-journal 或 adm 组 |
sudo usermod -aG systemd-journal $USER,重新登录生效 |
audit: backlog limit exceeded |
audit 规则命中量超过内核缓冲(-b 默认值偏小) |
先减规则;确实需要保留才调 -b,调大缓冲只是把问题推后 |
df 显示占用远大于 --disk-usage |
已轮转但被进程持有的删除文件 | sudo lsof +L1 | head,找到持有者后重启对应服务 |
八、边界
- 全文命令在 Ubuntu 22.04(systemd 249.11-0ubuntu3.23、auditd 3.x)上验证过。Debian 12 的 systemd 252 上
--dry-run可用,journalctl --vacuum-*的语义一致,其余发行版请自行确认。 - 文中「默认 SystemMaxUse 为磁盘 10%、上限 4G」是 systemd 的通用默认,但这台机器的
journald.conf是空的,我无法从配置反推,只能从「文件在 128M 轮转」这个现象侧证。要精确值请用systemctl show systemd-journald和journalctl --header对账。 - audit 规则的增删涉及合规判断,本文只给量化的影响面(每天约 1.9 万行、97% 来自 audit),不给「哪些能删」的结论。
- 没有动
rsyslog。这台机器的/var/log/syslog只有 290K、auth.log51K,占比可以忽略;如果你的机器上 rsyslog 还在全量转存,那journald限额只解决了一半问题,另一半在/etc/rsyslog.d/。
一句话收尾:日志占空间这件事,先量来源再定上限,最后回到产生日志的规则本身——顺序反过来,配多少上限都是在给噪声腾地方。
- 点赞
- 收藏
- 关注作者
评论(0)