news 2026/9/7 16:48:06

Linux根分区被journald日志占满?从清理到SystemMaxUse配置的实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux根分区被journald日志占满?从清理到SystemMaxUse配置的实战指南

一、从“磁盘满了”到揪出嫌疑人

先说一个真实的场景。某天下午,监控突然弹出告警:某台服务器的根分区使用率已经超过90%,再过几个小时业务就可能在“磁盘只读”的边缘挣扎。登录上去执行df -h,发现/挂载点已经用掉了97%,而里面最大的几个目录里,/var/log/journal占了绝大部分空间。du -sh /var/log/journal一跑,结果直接是十几个GB,和“平时没几MB”的印象差了一整个量级。

这不是个例。只要你的Linux服务器启用了systemd来管理服务和记录日志,/var/log/journal就会成为系统日志的主要落盘目录。它不像传统日志那样按天切片、按量滚动,journald默认的策略是“有空间就往里写”,你要是没主动给它设置上限,它会默默地把根目录塞满,直到某天彻底的“No space left on device”。

这篇文章就围绕这个实际故障展开:journal文件是如何累积的、为什么占了这么多空间、怎么做紧急清理、怎么从根上限制它不再失控,最后再分享一些面向长期运行的维护手段。无论你是第一次遇到“根目录爆满”的新手,还是常年和服务器打交道的运维老手,这其中的排查思路和配置思路都能直接拿来套用。

二、问题表象与首位嫌疑人:/var/log/journal

2.1 磁盘告警后必须做的三步定位

遇到“磁盘爆满”,第一步永远不是删文件,而是搞清楚什么在吃空间。不同目录对应不同的日志类型和业务数据,盲目清理容易误删重要审计记录。我惯用的排查顺序是:

  • df -h先看整体空间,确认哪个分区满了;
  • du -h -x -d 1 / 2>/dev/null | sort -rh | head -20找出根目录下最大的几个一级子目录;
  • 再对最大目录继续深入一层,直到定位到具体文件中。

注意,这里的-x参数非常关键。它用来告诉 du“不要跨文件系统”,否则如果你的/home/data是独立挂载的数据盘,du会把它们里的全部内容也统计进来,误以为根目录下的目录也占用了根分区空间。

用这套方法定位后,你会看到类似下面的输出:

# df -h Filesystem Size Used Avail Use% Mounted on /dev/vda1 40G 39G 1.1G 97% / # du -h -x -d 1 / 2>/dev/null | sort -rh | head 16G /var 1.2G /usr ...

继续往/var下挖一层,/var/log/journal赫然在列。到这里,嫌疑对象基本锁定。

2.2 journal日志与其他日志的区别

传统Linux日志体系里,多数程序会通过syslog把日志写进/var/log/messages/var/log/secure这类纯文本文件,并由logrotate按天或按大小切割轮转,超过保留份数就会删除旧档。而systemd-journald是另一个体系,它直接把各服务的标准输出、内核日志、启动日志等统一收集进二进制日志文件,存放在/var/log/journal(持久化模式下)或/run/log/journal(内存模式,重启即失)。

二进制格式带来了不少好处:结构化的字段、精确的时间戳、支持按服务/优先级/时间范围查询,配合journalctl工具使用体验远远好过直接翻文本文件。但也正因为是二进制“追加式”写入,journald的默认策略偏向“只进不出”,缺少像logrotate那样的强制轮转和删除机制,一旦某个服务输出量增大,journal文件就会以肉眼可见的速度膨胀。

/var/log/journal里的实际文件命名规则类似:

/var/log/journal/机器ID/system@xxxx.journal /var/log/journal/机器ID/user-1000@xxxx.journal

其中system@开头的来自系统服务或内核,user-UID@开头的是对应用户会话的日志。多个文件的存在并不奇怪,它们对应不同时间段的归档切片,属于journald自动切割机制的一部分。所以“文件多”本身不算问题,真正的判断指标是它们占用空间的总量。

2.3 journal为何会疯狂增长的常见诱因

正常情况下,普通负载的服务器一天产生的journal日志量在几十兆到两三百兆左右。但当你发现journal目录占用几个G甚至十几个G时,说明背后一定有什么在持续高频输出。我在实际环境中遇到最多的几个原因:

  • 某个应用服务进入异常循环,例如频繁报错启动失败、连接池疯狂重连、死循环写日志;
  • 数据库或消息队列等中间件开启了调试级别日志,又叠加了错误的日志轮转配置;
  • 内核被某些驱动或模块大量刷告警,例如网卡反复断开重连、磁盘IO错误反复上报;
  • 程序对systemd的输出处理不当,没有使用自带日志而依赖标准输出,即便程序内部没写文件,启动后也会被journal全部捕获;
  • 系统本身有定时任务频繁执行并输出错误,叠加成持续的日志流。

如果你能在刚发现磁盘告警时就快速定位到具体服务,不仅解决了“磁盘满”的表象,还能顺手揪出背后的真实故障。这个话题我会在第五部分专门展开。

三、核心原理入门:systemd-journald的存储机制与配置项

3.1 journald为什么会把日志写进 /var/log/journal

这里先说一个基础概念:journald有两种日志持久化模式。

  • 默认情况下,如果/var/log/journal目录存在,journald会把日志写入该目录,实现“跨重启保留”;
  • 如果该目录不存在且配置文件里也没有强制开启Storage=persistent,journald则会把日志写到/run/log/journal,这是一种临时内存文件系统,系统重启后日志就会随之丢失。

很多Linux发行版在安装时就已经创建了/var/log/journal目录,或者在systemd的tmpfiles配置里有相关规则负责在启动时自动创建。因此,只要目录存在,日志就是持久化状态。日志文件数量因此只会累积,不会主动缩减。

systemd-journald主配置文件一般位于/etc/systemd/journald.conf,其中控制空间增长的关键参数是SystemMaxUse。如果不设置它,journald默认采用“留出15%磁盘空间给其他用途”的策略。也就是说,根分区剩余空间的85%理论上都可能被journal占用。听起来可能有点反直觉,但它确实是官方默认策略:不管多少空间,它会尽量使用,以便保存尽可能多的历史日志。当分区即将满了才触发清理,所以大家才会在“根目录爆满”时才注意到它。

3.2 几个核心配置项的取舍

journald.conf里常用参数作用如下:

配置项默认值作用说明
Storageauto可选值:persistent(强制持久化到/var/log/journal)、volatile(强制写入/run/log/journal,重启丢失)、auto(自动判断,目录存在则持久化)、none(只转发不落盘)
Compressyes对超过一定大小的日志条目使用zstd压缩后再落盘,能显著降低磁盘占用
SystemMaxUse未设置所有journal文件合计体积的上限阈值,超过后会触发基于时间的旧日志清理
SystemKeepFree未设置为其他用途(如系统文件、业务数据)保留的最小磁盘空间,相当于“给自己留后路”
MaxRetentionSec未设置日志条目的最大保留时间,早于该时间的日志会被清理
MaxFileSec1month单个日志文件最大的时间跨度,到期后自动切换新文件

如果只有一个参数需要记住,那就是SystemMaxUse。它直接划定了journal“永远不能超过多少容量”,配合上MaxRetentionSec可以在空间和时间两个维度上约束日志。多数场景下,你根本不需要精确到日志内容层面的筛选,只要给这两项设一个合理的值,磁盘就不会被再次膨胀。

3.3 在动手修改前必须先做的检查

改了配置之后,日志清理不是瞬间发生的。journald大约每30秒执行一次空间检查和过期清理,因此你把SystemMaxUse从“无限制”改成“500M”之后,并不会马上把已有的几个G立刻清掉,它会逐步触发vacuum流程,最终压缩到限定范围内。

我通常建议:修改配置只是“治本”的第一步,但在磁盘已经告急的现场,必须先用后面第四条要讲的紧急清理手段把空间立刻释放出来,否则紧张的存储环境可能撑不到journald自己完成清理。另外一个经验是:改完配置后最好执行一次systemctl restart systemd-journald,虽然journald支持配置reload(systemctl reload systemd-journald),但某些版本对配置重载的支持并不完整,重启服务更可靠。不过重启journald会导致已有的日志写入句柄短暂中断,影响很小,绝大多数场景都可以安全操作。

四、实操记录:从紧急清理到配置生效的全过程

4.1 先执行紧急清理:vacuum三连

在你已经确认了是journal占满了磁盘、并且没有时间慢慢研判的时候,最快的操作是直接在命令行执行清理命令。journald自带了vacuum系列清理方式,比手动删文件安全得多。

# 只保留最近2天的日志,其余全部清理 journalctl --vacuum-time=2d # 限制journal总大小不超过200M,超过的删除 journalctl --vacuum-size=200M # 限制日志文件个数,只保留最近的5个文件 journalctl --vacuum-files=5

三条命令可以组合或单独使用。--vacuum-size=200M是最直观的一种,执行后journal目录会尽量压缩到200M以内,清理动作几乎是实时的。命令运行时会输出类似“Deleted archived journal ... (83.4M)”的提示,告诉你每个文件释放了多少空间。

很多人会问我:“直接rm -rf /var/log/journal/*行不行?”我的回答是:能行,但不推荐。因为如果当前有服务正在写journal日志,进程已经打开了对应的文件句柄,直接rm会把文件从目录结构中删除,但文件占用的磁盘块不会立刻释放,直到所有句柄关闭,而且删除后还可能导致journald写入异常,需要重启journald服务才能恢复。而用vacuum命令,journald会自己处理好文件轮转、句柄切换,不会影响正在写入的日志流,安全性高得多。

在磁盘告警场景下,我个人推荐的顺序是:先执行journalctl --vacuum-size=200M立即释放空间,然后去改journald.conf限制长期增长,最后再思考业务层面为什么会产出这么多日志。

4.2 配置文件的正确改法

以限制journal最大500M、保留时间最多7天为例,配置如下:

[Journal] Storage=persistent Compress=yes SystemMaxUse=500M MaxRetentionSec=7day

这里我特意写了Storage=persistent,目的是明确要求日志持久化到磁盘,防止发行版默认行为不一致导致配置效果不确定。Compress=yes是压缩开关,很多发行版默认就是开的,但如果之前有人关掉了,建议重新打开,能节约不少空间。

修改完成后需要使配置生效:

systemctl restart systemd-journald

重启完成后,再用下面的命令验证配置是否正确加载,以及目录当前实际占用情况:

journalctl --disk-usage # 预期输出类似于 # Archived and active journals take up 312.0M on disk.

如果你发现做了配置后占用依然超过限定值,检查一下是不是有其他的/etc/systemd/journald.conf.d/*.conf覆盖了主配置。在较新的发行版中,drop-in目录的配置优先级高于主配置,有时候这里残留的旧配置可能把SystemMaxUse改成了奇奇怪怪的值。

4.3 让日志空间配置持久生效的细节

回到根目录磁盘空间的问题:配置SystemMaxUse保护的是“journal不会无限侵蚀根分区”。但因为它只是把总量控制住,并不表示这里不会继续增长——达到阈值后journald会循环清理最老日志,保证总量恒定。所以你会发现磁盘占用稳定在设定值附近,不再继续上涨,这本身是符合预期的健康状态。

另一个容易踩的细节是SystemKeepFree。如果你的机器根分区本来就是40G这样的小盘,而业务本身也消耗大量空间,建议不要只设置SystemMaxUse,还应同时设置SystemKeepFree为至少2G到5G,让journald在计算容量时主动预留出这一部分空余空间,防止“日志没超限,但整个盘还是满了”的边界情况。

SystemMaxUse=500M SystemKeepFree=5G

上面的组合意思是:journal最多占500M,同时始终为系统保留至少5G可用空间。对根分区压力大的机器,这个保护性更强。

4.4 清理时日志依然被占用怎么办

有些场景下,即便执行了journalctl --vacuum,某些journal文件还是没有被清理掉。原因通常是:该文件当前仍处于“活跃写入”状态,也就是正在被某个服务的标准输出实时记录。journald会保留当前活跃文件不清理,因为一旦清理会导致当前日志流中断,记录缺失。

这时如果你确实急需释放空间,可以找到对应占用大量日志的服务,先停掉或重启它:

# 找到占用输出最高的服务 journalctl --since today | cut -d' ' -f5 | sort | uniq -c | sort -nr | head # 重启对应服务,或者干脆让它不再疯狂报错 systemctl restart 你的服务名

服务重置后,旧的journal文件会被journald轮转归档,此时再执行一次journalctl --vacuum-time=1h(模拟当场紧急清空),空间就会正常回收。这是我处理过很多次“vacuum了但还是没变小”问题后的通用解法,优先怀疑活跃文件,特别是一个服务在疯狂刷日志的情况下。

五、疯狂写日志的服务才是罪魁祸首:定位方法与实践

5.1 用 journalctl 找出高频输出者

journald自身的日志清理只是解决“结果”,真正导致磁盘爆满的原因往往是某个业务模块异常。要想避免下次重蹈覆辙,就必须找到那个高频日志输出源。下面这个命令组合可以统计当前时间段内各个unit(服务)的日志输出条数:

# 统计当天各服务输出的日志条数排名前10 journalctl --since today --no-pager -o short-unix \ | awk '{print $5}' | sort | uniq -c | sort -nr | head -10

看一下$5对应的内容:由于journalctl输出的第五列通常就是unit名称(类似sshd[1234]:形式),这样统计出最大数量的服务名就能帮助你快速筛选重点怀疑对象。得到高输出服务后,再用针对性命令查看该服务的具体日志:

journalctl -u 服务名 --since "1 hour ago" --no-pager | tail -100

如果日志内容显示为同一个错误反复出现,基本可以断定是业务逻辑异常或程序进入死循环。此时优先修复程序,而不是单纯限制日志空间。

另外,很多程序本身会记录日志到自己的文本文件里(例如/var/log/myapp/),但请注意它们打到标准输出或系统日志的内容同样会进入journald。如果程序有两个日志出口,总日志量可能成倍增加。你需要判断程序到底“是否持续报错”、“是否需要保留审计记录”,再决定是把程序日志级别调低,还是直接在systemd服务单元里让其输出不再进入journal。

5.2 服务单元层的日志限流方案

如果某个第三方应用“必须”把大量日志打到标准输出,而你又没法快速改造它,那么可以在systemd的服务单元文件里设置日志限流:

[Service] # 该服务每10秒内最多允许写入1000条日志 StandardOutput=journal LogRateLimitIntervalSec=10 LogRateLimitBurst=1000

LogRateLimitBurstLogRateLimitIntervalSec是systemd针对单元日志速率限制的黑科技,超过突发阈值的日志会被systemd直接丢弃。虽然听起来有点暴力,但是在应急时刻可以救你一命。注意这不是journald全局配置项,而是每个服务单元文件里的独立配置,通过daemon-reload生效。

生产环境里,我见过一个异常服务在几小时内把journal打爆的真实案例。当时临时给服务加了限流之后,磁盘不再告急,随后开发者定位到是一个第三方库的调试日志被误开,清理掉问题代码便彻底解决。这种“应用与系统双层限制”的思路,值得推广到每一台服务器。

5.3 内核日志与硬件错误的高频刷屏

除了服务日志,还有一类高频来源是内核本身的printk消息。这些消息也会被journald采集,对应文件为system@*.journal。如果你发现日志里大量出现kernel: ...同一行重复出现,比如网卡、磁盘、内存控制器报错,需要在修复硬件问题之前先用sysctl限制内核日志打印级别:

# 只保留严重等级以上的内核日志(0-3级) sysctl -w kernel.printk="3 3 3 3"

同时还可以调整journald采集内核日志的频率,不过优先级一般不高。排查内核日志刷屏之后,还是要回归硬件设施本身——换线、换内存、检查磁盘健康状态,这才是治本。

六、长期维护建议:别让根分区再次失控

6.1 写一个简单的journal日志健康巡检脚本

一次清干净了不代表一劳永逸。配置SystemMaxUse之后,journal空间得到了控制,但如果业务服务异常,磁盘依然可能被应用自己的日志打满。所以我习惯在服务器上维护一个小脚本,每天跑一次,既能报告根分区使用率,也能针对journal做一次状态检查。

以下是一个可直接套用的示例脚本,存放在/usr/local/bin/check_disk.sh

#!/bin/bash # 根分区使用率、/var/log/journal 占用情况检查 # 配合cron每天执行一次,发现问题后通过local0设备输出 THRESHOLD=85 ROOT_USAGE=$(df / | awk 'NR==2 {print $5}' | tr -d '%') JOURNAL_USAGE=$(du -sh /var/log/journal 2>/dev/null | cut -f1) if [ "$ROOT_USAGE" -ge "$THRESHOLD" ]; then echo "$(date): 根分区使用率 ${ROOT_USAGE}% 超过阈值 ${THRESHOLD}%" | logger -p local0.warning fi if [ -n "$JOURNAL_USAGE" ]; then echo "$(date): journal目录当前占用 ${JOURNAL_USAGE}" | logger -p local0.info fi

把脚本加入系统的cron或systemd timer前,记得先赋予执行权限并手动跑一遍验证语法:

chmod +x /usr/local/bin/check_disk.sh /usr/local/bin/check_disk.sh

这里的思路不是“等到出问题再处理”,而是把异常检测前置,让运维人员能在日志还在增长、但还没打爆磁盘的初期阶段就收到警告并介入处理。

6.2 配置logrotate或专用清理工具的必要性

journald有自带的容量阈值清理机制,但对于应用自身的文本日志,比如Nginx、MySQL、Java应用日志,依然要依赖logrotate进行切割和保留。你可以把logrotate想象成“日志界的定时管家”——它负责按天或按大小把日志文件改名,压缩旧文件,只保留指定份数,并执行程序自带的日志重开动作(比如nginx的USR1信号)。没有logrotate的应用日志是另一个常见的根目录爆满元凶,这里就不展开细说具体每个应用的配置了,但通用思路都一样。

回到journal自身,针对“日志文件时间跨度轮转”的控制,journald的MaxFileSec也是值得关注的参数。系统默认每1个月自动切换到新文件,保持活动文件不过大,加速清理时的扫描效率。如果你的服务器是日志高频写入场景(比如网关、代理节点),可以考虑把MaxFileSec改为7day或更短,让journal文件以更细粒度滚动,清理时就能更精确地丢弃无用部分。

6.3 磁盘容量管理的黄金法则

从根本上看,日志永远不可能被完全关闭,也不应该被完全关闭。但给“日志存储”一个明确的上限、给它一个独立的挂载点,是最干净的做法。

有条件的话,我会建议把/var/log/journal单独挂载到一个独立分区或独立逻辑卷,而不是让它和根目录共享空间。这样即便日志量真的异常爆炸,最多也只是“日志盘满了”,不会影响根分区运行中的业务。对云服务器而言,如果数据盘容量更大、价格更低,完全可以单独划分一块给日志使用。

如果实在没法做独立挂载,严格设定SystemMaxUse+ 持续监控,也会让journal成为一个“有界”的资源消耗者,不会再像滚雪球一样失控下去。我的个人经验是:任何日志系统都要“预设规模”,事前设置配额比事后反复清理轻松百倍。

七、常见问题排查速查表

问题现象主要可能原因快速解决方案
df -h显示根分区满,du定位到/var/log/journal体积很大journal持久化日志未限制容量执行journalctl --vacuum-size=200M紧急清理,再配置SystemMaxUse
vacuum后磁盘空间没有立刻释放当前活跃journal文件被服务占用,或deleted但fd未关闭重启高频日志服务或journald;确认没有进程保持已删除文件的句柄
配置了SystemMaxUse但journal体积仍超过限制drop-in配置覆盖了主配置,或者journald尚未执行周期清理检查/etc/systemd/journald.conf.d/*.conf;执行systemctl restart systemd-journald
某服务日志量异常大,刷屏输出服务业务bug、调试级别日志被误开、死循环journalctl -u 服务名查看内容,修复业务或限制日志级别;紧急时加LogRateLimitBurst
journal目录文件非常多正常文件轮转,但不一定异常检查总体积,若在SystemMaxUse范围内则不必担心,必要时调整MaxFileSec降低文件粒度
根分区时不时被上传文件/缓存填满不是journal的问题,而是业务数据积累用du从根分区逐层排查,定位真正的写入目录(例如容器镜像、临时目录、上传目录)

八、末尾说几句实在话

服务器的磁盘告警从来不是“突发”的,它一定经历了从量变到质变的过程。journald给这个量变过程提供了丰富的追溯信息,但前提是你给它的存储空间设好了边界。我踩过几次“根分区被journal打爆”的坑之后,现在每装一台新服务器,都会第一时间把/etc/systemd/journald.conf里的SystemMaxUse改好,然后配一个磁盘监控脚本。这些动作加起来不到五分钟,但能在未来省下无数个被叫起来处理磁盘告警的夜晚。

最后再分享一个个人技巧:每当遇到根目录爆满,不要只盯着最大的那个目录一顿删,建议花费额外两分钟用du -x -d 2 / | sort -rh看看排名前20的路径,确保没有“兄弟目录”同时也在悄悄膨胀。磁盘瘦身和体重管理是一个道理,找到病根、控制摄入量,才能长期保持健康。直接把/var/log/journal放进“按容量配额管理”的清单里,以后这类根目录磁盘报警,会少去一大半。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/7 16:45:25

RoGe:端到端隐式重建与生成式新视角合成解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 16:41:34

ERP进销存明细查询管理:Web ERP设计要点与Spring Boot实现

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/7 16:40:56

Gradle Wrapper下载卡死?从distributionUrl到国内镜像的彻底解决指南

最近在一个老项目上又碰见了这个折腾人的问题:Build 窗口卡在 Downloading https://services.gradle.org/distributions/gradle-7.6-all.zip ,几十分钟过去进度纹丝不动,偶尔还会直接抛一个 SocketTimeoutException 。可能很多人一看到 …

作者头像 李华
网站建设 2026/9/7 16:40:49

非科班转AI第3周,特征工程的坑逼着我重刷人工智能入门,精度从0.62跳到0.83

非科班转AI第3周,特征工程的坑逼着我重刷人工智能入门,精度从0.62跳到0.83 转行学人工智能的第22天,我坐在电脑前盯着 accuracy: 0.62 的结果发愣。这套房价预测项目我已经调了三次模型,从线性回归换到随机森林,超参调优也试过网格搜索,可验证集上的表现就是上不去。我翻回自己…

作者头像 李华