十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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/journal2.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/systemxxxx.journal /var/log/journal/机器ID/user-1000xxxx.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会把日志写入该目录实现“跨重启保留”如果该目录不存在且配置文件里也没有强制开启Storagepersistentjournald则会把日志写到/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支持配置reloadsystemctl reload systemd-journald但某些版本对配置重载的支持并不完整重启服务更可靠。不过重启journald会导致已有的日志写入句柄短暂中断影响很小绝大多数场景都可以安全操作。四、实操记录从紧急清理到配置生效的全过程4.1 先执行紧急清理vacuum三连在你已经确认了是journal占满了磁盘、并且没有时间慢慢研判的时候最快的操作是直接在命令行执行清理命令。journald自带了vacuum系列清理方式比手动删文件安全得多。# 只保留最近2天的日志其余全部清理 journalctl --vacuum-time2d # 限制journal总大小不超过200M超过的删除 journalctl --vacuum-size200M # 限制日志文件个数只保留最近的5个文件 journalctl --vacuum-files5三条命令可以组合或单独使用。--vacuum-size200M是最直观的一种执行后journal目录会尽量压缩到200M以内清理动作几乎是实时的。命令运行时会输出类似“Deleted archived journal ... (83.4M)”的提示告诉你每个文件释放了多少空间。很多人会问我“直接rm -rf /var/log/journal/*行不行”我的回答是能行但不推荐。因为如果当前有服务正在写journal日志进程已经打开了对应的文件句柄直接rm会把文件从目录结构中删除但文件占用的磁盘块不会立刻释放直到所有句柄关闭而且删除后还可能导致journald写入异常需要重启journald服务才能恢复。而用vacuum命令journald会自己处理好文件轮转、句柄切换不会影响正在写入的日志流安全性高得多。在磁盘告警场景下我个人推荐的顺序是先执行journalctl --vacuum-size200M立即释放空间然后去改journald.conf限制长期增长最后再思考业务层面为什么会产出这么多日志。4.2 配置文件的正确改法以限制journal最大500M、保留时间最多7天为例配置如下[Journal] Storagepersistent Compressyes SystemMaxUse500M MaxRetentionSec7day这里我特意写了Storagepersistent目的是明确要求日志持久化到磁盘防止发行版默认行为不一致导致配置效果不确定。Compressyes是压缩开关很多发行版默认就是开的但如果之前有人关掉了建议重新打开能节约不少空间。修改完成后需要使配置生效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在计算容量时主动预留出这一部分空余空间防止“日志没超限但整个盘还是满了”的边界情况。SystemMaxUse500M SystemKeepFree5G上面的组合意思是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-time1h模拟当场紧急清空空间就会正常回收。这是我处理过很多次“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条日志 StandardOutputjournal LogRateLimitIntervalSec10 LogRateLimitBurst1000LogRateLimitBurst和LogRateLimitIntervalSec是systemd针对单元日志速率限制的黑科技超过突发阈值的日志会被systemd直接丢弃。虽然听起来有点暴力但是在应急时刻可以救你一命。注意这不是journald全局配置项而是每个服务单元文件里的独立配置通过daemon-reload生效。生产环境里我见过一个异常服务在几小时内把journal打爆的真实案例。当时临时给服务加了限流之后磁盘不再告急随后开发者定位到是一个第三方库的调试日志被误开清理掉问题代码便彻底解决。这种“应用与系统双层限制”的思路值得推广到每一台服务器。5.3 内核日志与硬件错误的高频刷屏除了服务日志还有一类高频来源是内核本身的printk消息。这些消息也会被journald采集对应文件为system*.journal。如果你发现日志里大量出现kernel: ...同一行重复出现比如网卡、磁盘、内存控制器报错需要在修复硬件问题之前先用sysctl限制内核日志打印级别# 只保留严重等级以上的内核日志0-3级 sysctl -w kernel.printk3 3 3 3同时还可以调整journald采集内核日志的频率不过优先级一般不高。排查内核日志刷屏之后还是要回归硬件设施本身——换线、换内存、检查磁盘健康状态这才是治本。六、长期维护建议别让根分区再次失控6.1 写一个简单的journal日志健康巡检脚本一次清干净了不代表一劳永逸。配置SystemMaxUse之后journal空间得到了控制但如果业务服务异常磁盘依然可能被应用自己的日志打满。所以我习惯在服务器上维护一个小脚本每天跑一次既能报告根分区使用率也能针对journal做一次状态检查。以下是一个可直接套用的示例脚本存放在/usr/local/bin/check_disk.sh#!/bin/bash # 根分区使用率、/var/log/journal 占用情况检查 # 配合cron每天执行一次发现问题后通过local0设备输出 THRESHOLD85 ROOT_USAGE$(df / | awk NR2 {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-size200M紧急清理再配置SystemMaxUsevacuum后磁盘空间没有立刻释放当前活跃journal文件被服务占用或deleted但fd未关闭重启高频日志服务或journald确认没有进程保持已删除文件的句柄配置了SystemMaxUse但journal体积仍超过限制drop-in配置覆盖了主配置或者journald尚未执行周期清理检查/etc/systemd/journald.conf.d/*.conf执行systemctl restart systemd-journald某服务日志量异常大刷屏输出服务业务bug、调试级别日志被误开、死循环用journalctl -u 服务名查看内容修复业务或限制日志级别紧急时加LogRateLimitBurstjournal目录文件非常多正常文件轮转但不一定异常检查总体积若在SystemMaxUse范围内则不必担心必要时调整MaxFileSec降低文件粒度根分区时不时被上传文件/缓存填满不是journal的问题而是业务数据积累用du从根分区逐层排查定位真正的写入目录例如容器镜像、临时目录、上传目录八、末尾说几句实在话服务器的磁盘告警从来不是“突发”的它一定经历了从量变到质变的过程。journald给这个量变过程提供了丰富的追溯信息但前提是你给它的存储空间设好了边界。我踩过几次“根分区被journal打爆”的坑之后现在每装一台新服务器都会第一时间把/etc/systemd/journald.conf里的SystemMaxUse改好然后配一个磁盘监控脚本。这些动作加起来不到五分钟但能在未来省下无数个被叫起来处理磁盘告警的夜晚。最后再分享一个个人技巧每当遇到根目录爆满不要只盯着最大的那个目录一顿删建议花费额外两分钟用du -x -d 2 / | sort -rh看看排名前20的路径确保没有“兄弟目录”同时也在悄悄膨胀。磁盘瘦身和体重管理是一个道理找到病根、控制摄入量才能长期保持健康。直接把/var/log/journal放进“按容量配额管理”的清单里以后这类根目录磁盘报警会少去一大半。
返回列表