资讯动态

日志轮转实战:从零配置 logrotate 与管理磁盘空间

发布时间:2026/10/20 12:14:58 来源:尧图企业网站定制
日志轮转实战从零配置 logrotate 与管理磁盘空间干运维这行谁没有被日志吓出过冷汗某天早上到工位监控大屏一片飘红一看磁盘使用率 98%SSH 都登不上机器。登上去一查好家伙某个应用把日志写成了一个 80GB 的单个文件df -h看一眼都心疼。这种事故我碰过不止一次所以后来学乖了所有服务器的日志一律交给logrotate管。这东西没啥花头但只要你配置得当磁盘空间能省下一大截再也不用半夜爬起来清日志。这篇文章就专门聊logrotate的配置与实战。我会从最基础的概念讲起然后把配置文件的每一项拆开讲明白再分享几个真实场景下的完整配置方案和踩坑记录。不管你是刚入行的小白还是已经被日志折磨过的老手照着这篇文章操作一遍基本就能把日志轮转这件事理顺了。另外多说一句文中所有配置均基于 CentOS 7 和 Ubuntu 20.04 双环境验证其他发行版大同小异关键参数通用。1. 为什么日志必须轮转先弄清磁盘是怎么被塞满的很多新手不理解日志不就是追加写几个文件吗怎么会把磁盘撑爆这个问题的根源在于日志的“无限增长”特性。以常见的 Nginx 访问日志为例高并发站点一天就能产生几 GB 的 access.log。你如果不做任何干预这个文件只会越来越大。更麻烦的是日志文件一旦变得巨大还会带来三个连锁问题。1.1 日志无限增长的三大隐患第一个隐患是磁盘空间耗尽。这是最直接的也是大家最先感知到的。/分区或者/var分区一旦写满系统会变得非常不稳定很多服务会因为无法写入临时文件而直接崩溃甚至 SSH 登录都会失败。我遇到过一个真实案例某台数据库服务器因为 binlog 没清理直接把数据盘写满导致数据库宕机那场面堪称灾难。第二个隐患是查询效率极低。日志是排障的第一手资料但当某个日志文件达到几十 GB 时你用grep去搜索关键字速度会慢到让人抓狂。而且很多日志采集 Agent 在读取超大文件时也会出问题比如日志采集会读到重复数据、偏移量错乱等。第三个隐患是文件系统层面的限制。很多老旧的日志框架比如 Log4j 1.x 的某些配置不支持日志文件的分片一旦文件超过 2GB写入就开始异常。更现实的问题是tail -f一个 50GB 的文件光打开就要好几秒。1.2 logrotate 的工作原理不是魔法是策略logrotate干的事本质上非常简单按预设的周期或大小触发把当前日志文件改名、压缩然后新建一个空白日志文件让应用继续写。你可以把它想象成一个“轮班制”仓库——当前在用的日志文件是“上班的”到了换班时间轮转周期它就被归档到仓库角落改名或压缩新文件接替它的位置继续记录。它的触发机制有三种按时间daily、weekly、monthly、按大小size 100M、按时间大小组合。我们需要理解的是logrotate本身不是守护进程它靠 cron 任务来驱动。在 CentOS 上系统自带的/etc/cron.daily/logrotate会每天执行一次在 Ubuntu 上也有类似的机制。这意味着如果你配置的是hourly需要额外添加 cron 条目否则不会生效。这也是很多新手踩坑的第一个地方配置文件写好了但就是不轮转原因就是 cron 没接上。提示logrotate不是实时生效的最短生效周期取决于 cron 的最小精度通常 1 分钟。如果你改了配置想看效果可以用logrotate -vf /etc/logrotate.conf来强制触发而不是干等。2. 核心配置逐项拆解每个参数背后都有讲究logrotate的配置分为全局配置和单文件配置。全局配置在/etc/logrotate.conf里单文件配置放在/etc/logrotate.d/目录下每个应用一个文件。下面我把最常用的参数逐项拆开讲并且告诉你每个参数背后的实际考量。2.1 最基本的轮转指令rotate 与频率先看一个最典型的配置片段/var/log/nginx/*.log { daily rotate 7 compress delaycompress missingok notifempty create 0640 nginx adm }这个配置的每一项都值得细说daily每天轮转一次。因为是靠 cron 驱动所以实际执行时间是 cron 任务的时间CentOS 上一般在凌晨 3 点左右具体可以参考/etc/anacrontab。rotate 7保留 7 份归档日志。超过 7 份后最老的归档会被自动删除。这个数字直接决定了日志占用的磁盘总量你还需要考虑每份日志的大小。compress对归档的日志进行 gzip 压缩。压缩率通常在 80% 以上也就是说一个 1GB 的日志归档后只剩 100-200MB。这条指令应该默认开启。delaycompress延迟压缩。也就是本轮轮转后刚刚生成的归档文件不立即压缩等下一次轮转时再压缩。为什么要这样因为 Nginx 可能还在往旧的归档文件里写文件描述符FD立即压缩会丢失最后一段日志。对此delaycompress 通常和copytruncate搭配使用也可以和create搭配看应用是否重开日志文件。等等这里需要展开讲讲create和copytruncate的区别这是轮转方案选型的核心。2.2 create 与 copytruncate两种轮转模式怎么选create模式是 logrotate 的默认方式。它把日志文件重命名为归档文件然后在原路径下新建一个空白文件。这种方式的优点是不会丢日志缺点是需要应用支持“重新打开日志文件”即应用能感知到日志文件被替换了主动 reopen FD。Nginx、Apache、Tomcat 等大多数服务都支持前提是你配置了正确的信号或 reopen 机制。copytruncate模式则先把当前日志文件复制一份作为归档然后立即把原文件截断清零。这种方式不需要应用重新打开文件适合那些无法感知文件被替换的应用。但它的缺点也很明显从复制到截断之间的微小窗口内日志会丢失而且频繁复制大文件会消耗 IO。实操中的判断标准很简单如果应用支持 reopen能发信号用create 信号重载如果应用不支持用copytruncate。我用 Nginx 时习惯在轮转后执行kill -USR1 $(cat /var/run/nginx.pid)配合create模式百分百不丢日志。2.3 按大小轮转与按时间轮转怎么选、怎么算按时间轮转daily适合日志流量稳定、容量可预测的场景。按大小轮转size 100M适合日志生成量不均匀的场景——比如白天流量是夜里的 10 倍如果按天轮转白天可能两小时就产生 2GB 日志深夜则几乎不产生。此时按大小轮转更合理。size的触发逻辑要特别注意它和daily是 OR 关系而不是 AND。也就是说如果配置了dailysize 100M那么只要当天 cron 触发就会轮转不管文件有没有到 100M如果在 cron 触发前文件已经超过 100M也会被提前轮转可以说logrotate会在 cron 周期内检查大小。这在某些版本上的行为有差异更稳妥的做法是只写size不写时间频率。归档数量的计算有一个简单公式每日日志量GB × 保留天数 归档总容量GB 归档总容量GB ÷ 压缩比通常取 20%-30% 实际磁盘占用GB假设 Nginx 每天产 2GB 日志保留 14 天那么归档总容量是 28GB压缩后实际占磁盘不到 6GB。这个数字要记下来和你的分区容量对比就能算出留多少余量。2.4 其他实用细节missingok、notifempty、sharedscripts这几个参数看着不起眼但在实战中非常关键missingok如果日志文件不存在不报错继续执行。这个必须加否则当月日志还没生成logrotate 就报错退出后续的轮转都受影响。notifempty当日志文件为空时不轮转。不加这个参数空日志文件也会被轮转产生无数空归档文件纯粹浪费 inode。sharedscripts如果一个规则匹配了多个日志文件比如*.logpostrotate脚本默认每个文件执行一次。加了sharedscripts所有文件轮转完后只执行一次。这个对于重启服务的场景意义重大否则文件多时服务会被反复重启。还有dateext这个参数归档文件会带上日期后缀比如access.log-20250120.gz这样归档文件不会互相覆盖而且定位某个日期的日志非常方便。建议永远加上dateext哪怕你不用它查日志也要为自动化采集留后路。3. 生产环境配置实战从日志归档到压测验证光讲参数没意思下面直接上一份我压箱底的生产配置。这个配置同时涵盖 Nginx 访问日志、Java 应用日志和系统日志三类典型场景且经过了多个版本系统的验证。3.1 Nginx 日志轮转配置create 模式创建/etc/logrotate.d/nginx内容如下/var/log/nginx/*.log { daily rotate 14 compress delaycompress missingok notifempty dateext create 0640 nginx adm sharedscripts postrotate if [ -f /var/run/nginx.pid ]; then kill -USR1 cat /var/run/nginx.pid fi endscript }这条配置的核心点在于postrotate段。Nginx 收到USR1信号后会重新打开日志文件这样新的请求继续写入新的 access.log旧的归档文件不会再接收任何数据。务必确保 PID 文件路径正确否则信号没发出去Nginx 还会持续往旧归档文件里写数据导致磁盘空间不减反增。这里有一个我踩过的坑如果用systemd管理 Nginx某些发行版的 PID 文件路径会变化比如/run/nginx.pid而非/var/run/nginx.pid但只要 /var/run 是软链到 /run 的两者等价。保险的做法是先执行ls -l /var/run/nginx.pid确认一下。3.2 Java 应用的 copytruncate 配置有的 Java 应用写日志依赖 Log4j 的滚动机制但更多老应用根本不会自动重建文件。此时用copytruncate模式兜底/opt/myapp/logs/app.log { size 500M rotate 10 compress missingok notifempty copytruncate dateext }size 500M表示文件超过 500MB 就触发轮转这个值可以根据单文件写入性能来调整——如果应用经常写大日志文件超过 2GB 时copytruncate会非常吃力因为复制大文件期间 IO 会飙升。使用copytruncate时有一点要明白轮转完成的瞬间归档文件里的内容可能比源文件少了几 KB 到几 MB这是因为复制和截断之间有时间差。对于排障来说几乎无感但对于强审计要求的场景不能接受这行日志差异只能用create模式加应用配合。3.3 压测 logrotate 配置是否生效改完配置文件必须测试才能放心。测试命令是# 调试模式展示将要执行的操作不实际执行 logrotate -d /etc/logrotate.d/nginx # 强制轮转忽略周期限制立即执行一次 logrotate -vf /etc/logrotate.d/nginx-d是 dry run会输出详细的执行计划包含每条指令的判断结果比如rotating pattern、considering log、now: ...等。我每次改完配置都先跑一遍-d确认参数没问题后再用-vf强制轮转一次然后立刻ls -lh /var/log/nginx/看归档文件是否生成。注意-vf会真实执行轮转适合在低峰期验证。千万不要在生产高峰期强行 -vf尤其对于超大日志文件压缩 CPU 和复制 IO 会拖累业务。3.4 归档日志的可视化检查轮转之后日志文件变成了下面这种形态/var/log/nginx/access.log # 当前写入的文件 /var/log/nginx/access.log-20250120.gz # 昨日归档 /var/log/nginx/access.log-20250119.gz # 前日归档 ...判断是否到达保留上限可以跑这个命令ls -lh /var/log/nginx/ df -h /var/log如果发现归档文件数量没有增长就要检查 logrotate 状态文件。状态文件记录了每个日志文件上次轮转的时间默认在/var/lib/logrotate/logrotate.status。比如状态文件里显示某天已经轮转过了那么 cron 在当天再次触发时就会跳过。4. 常见问题与排查技巧实录这个板块是我在多次排障中沉淀下来的每一个问题我都亲自处理过而且有典型的“症状-原因-解法”路径。4.1 配置文件写好了但就是不轮转症状ls /var/lib/logrotate/logrotate.status显示某个日志文件没有轮转记录手动执行logrotate -f能轮转但到点后不自动执行。排查步骤先查 cron 是否在执行 logrotategrep logrotate /etc/crontab或systemctl status logrotate.timerUbuntu 18.04 用 systemd timer。再查/etc/cron.daily/logrotate是否有执行权限ls -l /etc/cron.daily/logrotate确认为可执行文件。手动执行/etc/cron.daily/logrotate看报错输出。大多数情况下问题出在配置语法上某个配置文件里日志路径写错导致 logrotate 在遍历时直接报错退出。此时/var/log/messages或/var/log/syslog里会有具体报错。4.2 轮转后日志内容丢失这个案例比较经典。某次我用create模式配置了 Tomcat 的 catalina.out但日志还是偶尔丢最后几行。后来排查发现是 Tomcat 的 logging 机制在每次写入时重新打开文件create模式新建的文件虽然存在但 Tomcat 的部分日志还是写到了旧的 FD 上。而postrotate没有执行touch或信号重载所以旧档案持续被写入最终还是丢日志。解决方案是在postrotate里主动重启应用或者触达 reload 机制。对于 Tomcat最简单的办法就是systemctl restart tomcat虽然粗暴但能确保 FD 被重新打开。4.3 磁盘占用比预期的大很多你设了compress可是归档文件还是非常大。这个原因通常是delaycompress带来的刚刚轮转的日志文件还没压缩要等下一次轮转。如果你设了rotate 30那么磁盘上最多同时存在 30 份未压缩归档和 30 份已压缩归档容量超预期是必然的。解决方案是确认delaycompress是否必要。如果应用在轮转后不会向旧 FD 写入数据就直接去掉delaycompress让归档立即压缩。另外还有一个细节compresscmd默认用 gzip压缩级别是 6。如果你对磁盘空间极度敏感可以换成 xz 或者设置compresscmd指向 zstd压缩率更高但耗时更长。优先级应该是先保证轮转成功再考虑压缩率。4.4 日志轮转导致服务启动失败这个问题在分布式环境下特别烦人。Nginx 的pid文件在轮转后被误删或有权限问题导致 reload 失败。或者轮转执行时服务恰好重启引发资源竞争。建议在postrotate脚本里加上sleep 1给服务一点缓冲时间同时用test -f /var/run/nginx.pid判断 PID 文件是否真实存在。脚本不要写得太复杂只要保证“信号发出去服务还在”即可。4.5 状态文件的坑轮转顺序颠倒如果你在同一个目录配置了多个日志文件logrotate 会逐个执行。但状态文件一旦错乱比如手动删除过可能导致同一天内重复轮转或者本该保留的归档被提前删除。稳妥的做法是每次手动修改轮转配置后用logrotate -d核对状态输出而不是直接清空状态文件。5. 配套措施与进阶技巧轮转只是日志管理的基础生产环境还需要配套以下手段。5.1 日志集中采集与远端存储轮转解决了磁盘占用的本地点但日志最终还要用于排障和分析。我现在的做法是所有节点日志通过采集 Agent 传到集中日志平台如 ELK 或 Loki。此时 logrotate 的dateext参数就显得很重要因为采集端通常以文件名后缀来识别进度。如果你不用dateext归档文件名固定采集端很容易重复读或漏读。5.2 磁盘空间预警再强的轮转策略也怕空间异常。建议在监控系统里加一条针对/var/log分区的阈值告警比如使用率超过 80% 就告警。日志分区如果写满后果比业务分区写满更隐蔽——很多服务不直接写日志分区但系统日志和审计日志一旦写不进去会影响认证和调度。5.3 手动整理归档日志的小脚本除了 logrotate 的自动轮转我还会写个临时脚本用来手动批量清理 N 天前的归档日志。这个脚本不常跑但在磁盘告警时非常好用#!/bin/bash LOG_DIR/var/log/nginx KEEP_DAYS7 find ${LOG_DIR} -name *.gz -mtime ${KEEP_DAYS} -delete find ${LOG_DIR} -name *.log.* -mtime ${KEEP_DAYS} -delete注意脚本里的-mtime 7是“7天以前”-mtime -7是“7天之内”别写反了。清理前建议-exec ls -lh {} \;先预览一遍确认没有误删风险。6. 写在最后的实践心得干运维这些年我最大的体会是日志管理拼的不是技巧而是“预判能力”。你预判到日志会涨多快预判到哪些服务不支持 reopen预判到轮转高峰会不会和业务高峰撞车才能设计出一套稳的轮转策略。我在实际配置中有几条从不妥协的底线一是所有应用日志必须配合日期后缀轮转二是生产环境必须提前压测一轮logrotate -vf三是定期抽查状态文件和磁盘用量。任何一条没做到后面都有可能在深更半夜以告警的方式找上门来。如果你手头正为日志磁盘占用烦恼照着文中的配置先跑一遍大概率能解决 90% 的问题。剩下的 10%大概率涉及到应用自身对日志文件描述符的管理逻辑需要针对具体的应用去查 reopen 文档。最后再送你一个小技巧改完 logrotate 配置后别急着删旧的 logrotate.status先执行logrotate -d把计划里的时间与状态文件对比一下。当你发现执行计划里的considering log和now时间完全对得上时就可以安心睡个好觉了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑