日志是排查问题的重要依据,但不加管理的日志文件会悄悄吃满磁盘,轻则 Web 服务写入报错,重则系统无法正常运行。在搬瓦工 VPS 这类磁盘有限的环境中,建立良好的日志轮转与磁盘清理习惯尤为重要。本文介绍 logrotate 的配置、systemd journal 日志清理,以及找出大文件和定期清理的实用命令。

本文要点

  • logrotate 自动按周期轮转并压缩日志,防止单文件无限增长
  • systemd journal 日志占用可能几 GB,定期清理很有必要
  • du + find 快速找出磁盘空间杀手
  • 设置合理的保留周期,平衡排查需求与空间占用

logrotate 基础

logrotate 是 Linux 标准的日志轮转工具,绝大多数发行版默认已安装。它定期(每日/每周)对日志文件进行轮转:重命名旧日志、压缩存档、删除超出保留期的旧文件,并可在轮转后重启相关服务让其写入新日志。

# 查看 logrotate 是否已安装
which logrotate

# 查看全局配置
cat /etc/logrotate.conf

# 各应用的专属配置目录
ls /etc/logrotate.d/

logrotate 配置详解

以 Nginx 日志为例,展示一个完整的轮转配置:

/var/log/nginx/*.log {
    daily                  # 每天轮转
    missingok              # 日志文件不存在也不报错
    rotate 14              # 保留最近 14 份(超出的自动删除)
    compress               # 旧日志用 gzip 压缩
    delaycompress          # 保留最近一份不压缩(方便当天查看)
    notifempty             # 文件为空时不轮转
    create 0640 www-data adm  # 轮转后新建日志文件并设权限
    sharedscripts
    postrotate
        # 让 nginx 重新打开日志文件
        nginx -s reopen 2>/dev/null || true
    endscript
}
指令含义
daily / weekly / monthly轮转周期
rotate N保留最近 N 份,超出自动删除
compress / delaycompressgzip 压缩旧日志,最近一份延迟压缩
missingok日志不存在时跳过,不报错
notifempty日志为空时不轮转
postrotate轮转后执行的命令(通常是重载服务)

手动测试 logrotate

# 模拟运行(不实际轮转),查看会处理哪些日志
logrotate --debug /etc/logrotate.conf

# 强制立即轮转(不管是否满足周期条件)
logrotate --force /etc/logrotate.d/nginx

# 查看 logrotate 上次运行状态
cat /var/lib/logrotate/status

postrotate 必须通知服务重新打开日志

Linux 程序持有日志文件的文件描述符,即使旧文件被重命名,它仍会继续写旧文件。必须在 postrotate 中通知服务(如 nginx -s reopensystemctl reload php-fpm)重新打开新日志文件,否则轮转无效。

清理 systemd journal 日志

systemd-journald 会持久化系统日志,长期运行后可能占用数 GB。定期清理可释放大量空间:

# 查看当前 journal 占用
journalctl --disk-usage

# 保留最近 7 天的日志(删除更早的)
journalctl --vacuum-time=7d

# 限制总占用不超过 500 MB
journalctl --vacuum-size=500M

# 设置永久限制:编辑 /etc/systemd/journald.conf
# [Journal]
# SystemMaxUse=500M
# MaxRetentionSec=7day
# 改完后重启 journald
systemctl restart systemd-journald

查找磁盘空间占用大文件

# 查看各目录磁盘占用(从根目录开始,按大小排序)
du -sh /* 2>/dev/null | sort -rh | head -20

# 查找 /var 下最大的文件
du -sh /var/* | sort -rh | head -10

# 找出大于 100MB 的文件
find / -maxdepth 6 -type f -size +100M 2>/dev/null

# 查看 /var/log 下各日志大小
du -sh /var/log/* | sort -rh | head -20

定期清理的最佳实践

  1. df -h 监控各分区使用率,磁盘超 80% 时应立即清理
  2. 定期删除 /tmp 下的临时文件:find /tmp -mtime +7 -delete
  3. 清理 apt/yum 缓存:apt clean(Debian)或 yum clean all(CentOS)
  4. 检查 Docker 镜像和容器:docker system prune -f(如使用 Docker)
  5. 删除长期不用的备份文件和压缩包

设置磁盘告警

推荐用 cron 定期检查磁盘使用率,超过阈值时发送邮件提醒:
0 8 * * * df -h | awk 'NR>1 && int($5)>85{print}' | mail -s '磁盘告警' you@example.com
这样磁盘快满时你能提前收到通知,避免服务受影响。

小结

  • logrotate 配置 rotate 保留份数 + compress 压缩,是防止日志爆盘的标准方案
  • postrotate 一定要通知服务重开日志,否则轮转无效
  • journal 日志定期清理,建议限制在 500 MB 以内
  • du -sh /* | sort -rh 是找磁盘空间占用的快捷入口
  • 磁盘超 80% 立即处理,超 90% 可能导致服务异常

常见问题

logrotate 已经配置了,为什么日志还是很大?

可能是服务没有响应 postrotate 的信号,仍在写旧文件。检查旧日志文件是否还在被写入:lsof | grep log | grep deleted,有输出说明存在被删除但仍在写的文件,需重启相关服务。

/var/log 下有很多 .gz 文件,可以直接删除吗?

可以,这些是 logrotate 压缩的旧日志归档。删除前确认其时间范围是否还在你的排查需求范围内;如果已超过保留期且无排查需求,可以安全删除。

磁盘满了但找不到大文件,怎么回事?

可能是有进程持有已删除文件的文件描述符(常见于日志轮转后未重启服务)。用 lsof | grep deleted | sort -k 7 -rn | head 查找被占用的"幽灵文件",重启相关服务即可释放空间。

journal 清理后磁盘空间没有释放?

journal 清理后需等文件系统同步,或用 sync 强制刷盘。也可检查 /run/log/journal(内存日志)是否也占用了大量空间,用 journalctl --flush 将其持久化再清理。