接手一台运行了半年的Ubuntu服务器,最常遇到的报警就是磁盘空间不足。登录后习惯性地敲下df -h,发现根分区使用率飙到了92%,但业务数据明明才占用了不到40%。这种时候,元凶十有八九藏在日志文件里。系统日志、应用日志、内核日志、邮件日志、调试日志层层堆积,加上journald默认的持久化策略,几十GB的空间就这样被无声吞噬。更棘手的是,很多运维人员不敢轻易动日志,担心删错文件导致服务异常或审计缺失。实际上,日志清理有章可循,配合合理的轮转策略和存储优化,完全可以把日志占用的空间控制在可预测的范围内。
先定位日志空间占用大户盲目删除是运维大忌。动手前必须搞清楚到底是哪些文件在消耗磁盘。du命令比df更细致,可以逐层深挖目录的实际占用。从根目录开始扫一遍:
sudo du -sh /* 2>/dev/null | sort -rh | head -20
这条命令会列出根目录下占用空间最大的20个目录,通常/var会排在前列。继续深入/var目录:
sudo du -sh /var/* 2>/dev/null | sort -rh | head -20
绝大多数情况下,/var/log是重灾区。但别急着冲进去删文件,先看看具体哪些日志文件在膨胀。用ls配合-S参数按大小排序:
sudo ls -lhS /var/log/
常见的空间大户包括:syslog和syslog.1这类系统日志轮转文件,kern.log记录的内核日志,以及dpkg.log这类包管理日志。如果服务器运行了Web服务,/var/log/nginx/或/var/log/apache2/下的访问日志和错误日志往往是真正的磁盘杀手,一个高流量站点的access.log几天不轮转就能涨到几十GB。还有容易被忽略的/var/log/journal/目录,这是systemd-journald的二进制日志存储位置,默认配置下它会持续增长,直到磁盘空间所剩无几。
安全清理已轮转的归档日志日志轮转机制会自动将当前日志重命名为带有数字后缀的归档文件,比如syslog.1、syslog.2.gz,数字越大表示越旧。这些归档文件在绝大多数场景下是可以安全清理的,特别是那些已经被压缩过的.gz文件。清理它们不会影响当前正在写入的日志,也不会中断任何服务。直接删除超过一定天数的归档日志:
sudo find /var/log -type f -name "*.gz" -delete sudo find /var/log -type f -name "*.1" -delete sudo find /var/log -type f -name "*.old" -delete
更精细的做法是按修改时间清理,比如删除7天前的日志归档:
sudo find /var/log -type f -mtime +7 -delete
执行前建议先用不带-delete的版本预览一下文件列表,确认没有误伤。对于某些应用特有的日志目录,比如/var/log/mysql/下的慢查询日志和错误日志归档,同样适用这个逻辑。清理完毕后再次运行df -h,通常能看到立竿见影的效果。
处理systemd-journald的持久化日志journald是Ubuntu从16.04版本开始默认启用的日志系统,它以二进制格式存储日志,提供了比传统syslog更丰富的查询能力。但它的默认存储策略相当激进,在/var/log/journal/目录下会持续累积日志,直到磁盘空间低于一定阈值才触发清理。很多服务器运行一两年后,这个目录轻松突破10GB。查看当前journald日志的磁盘占用:
journalctl --disk-usage
要立即缩减journald日志的体积,可以按时间或大小进行清理。保留最近7天的日志,删除更早的内容:
sudo journalctl --vacuum-time=7d
或者直接限制journald日志最大占用空间为500MB:
sudo journalctl --vacuum-size=500M
这两个命令可以立即释放空间。但更重要的是修改journald的配置文件,从根本上控制日志增长。编辑/etc/systemd/journald.conf,找到并修改以下参数:
SystemMaxUse=500M SystemKeepFree=10G MaxFileSec=7day ForwardToSyslog=yes
SystemMaxUse限制了journald日志在磁盘上的最大占用空间,SystemKeepFree确保磁盘至少保留10GB的可用空间,MaxFileSec让日志文件按时间分割,ForwardToSyslog则将日志同时转发给传统的rsyslog处理,保持兼容性。修改后重启服务:
sudo systemctl restart systemd-journald
重启后journald会按照新配置运行,已有的超量日志也会被自动清理。
配置logrotate实现自动化日志轮转手动清理只能解一时之急,真正让日志管理自动化的工具是logrotate。Ubuntu默认安装了logrotate,并且大部分系统服务已经预置了轮转配置,位于/etc/logrotate.conf和/etc/logrotate.d/目录下。但默认配置往往偏保守,轮转周期长、保留份数多,需要根据实际磁盘容量进行调整。先看主配置文件:
cat /etc/logrotate.conf
关键参数包括weekly(每周轮转一次)、rotate 4(保留4个归档)、compress(压缩归档文件)。如果磁盘空间紧张,可以把轮转周期从weekly改成daily,保留份数从4减到2,并启用delaycompress让最近一次的归档也压缩。对于Nginx日志,编辑/etc/logrotate.d/nginx:
/var/log/nginx/*.log {
daily
missingok
rotate 2
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
daily改为每天轮转,rotate 2只保留两份归档,加上compress和delaycompress,即使高流量的访问日志也能控制在可接受的大小范围内。postrotate段落里的kill -USR1信号会通知Nginx重新打开日志文件,避免写入已被删除的文件句柄。对于自定义应用的日志,可以在/etc/logrotate.d/下新建配置文件,指定日志路径和轮转策略。配置完成后可以用debug模式测试,不实际执行轮转,只验证配置是否正确:
sudo logrotate -d /etc/logrotate.conf
强制立即执行一次轮转,看看效果:
sudo logrotate -f /etc/logrotate.conf
轮转执行后检查对应日志目录,应该能看到新生成的归档文件,而当前日志文件已被清空或重新创建。
清理不再需要的应用日志和缓存除了系统级日志,各类应用程序也有自己的日志和缓存目录,这些地方同样容易堆积无用文件。APT包管理器的缓存就是一个典型例子,下载过的deb包会保留在/var/cache/apt/archives/中,时间久了能占好几个GB。清理这些缓存:
sudo apt-get clean
这条命令会清空所有下载的包缓存。如果只想删除已经过时、仓库中不再存在的包,用autoclean:
sudo apt-get autoclean
Snap包是另一个隐藏的空间消耗源。Snap会保留每个包的多个版本,旧版本虽然不再使用但不会自动删除。查看Snap包的版本留存情况:
snap list --all
清理所有已禁用的Snap包旧版本:
sudo snap remove --purge $(snap list --all | awk '/disabled/{print $1" --revision="$3}')
Docker环境下的日志问题更值得关注。Docker默认使用json-file日志驱动,容器的标准输出和标准错误会写入/var/lib/docker/containers/下的json文件中,而且默认没有大小限制。一个长期运行的容器,日志文件轻松突破10GB。查看Docker占用的磁盘空间:
sudo docker system df
清理所有停止的容器、未使用的镜像和构建缓存:
sudo docker system prune -a
但这条命令治标不治本,更好的做法是在Docker daemon配置中限制单个容器的日志大小。编辑/etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
重启Docker服务后,每个容器的日志文件最多100MB,保留3个轮转文件,单个容器的日志总占用不会超过300MB。对于已经存在的容器,需要重新创建才能应用新配置。
内核日志和崩溃转储的处理内核日志通过dmesg可以查看,它们存储在内存中的环形缓冲区里,通常不会占用磁盘空间。但一旦系统发生崩溃,内核转储文件(crash dump)会写入/var/crash/目录,每个文件可能高达几GB,与物理内存大小相当。检查这个目录:
ls -lh /var/crash/
如果服务器运行稳定,没有需要排查的内核问题,这些崩溃转储文件可以直接删除:
sudo rm -f /var/crash/*
如果不需要内核崩溃转储功能,可以彻底禁用。编辑/etc/default/kdump-tools,将USE_KDUMP设为0:
USE_KDUMP=0
然后运行sudo kdump-config unload卸载当前kdump服务。这样即使系统崩溃,也不会再产生巨大的转储文件。
建立日志监控和预警机制清理和轮转配置到位后,还需要一套轻量的监控手段,避免再次出现磁盘被日志撑爆的情况。最简单的做法是用cron定时任务配合df命令做阈值检测。在/etc/cron.d/下新建一个disk-check任务:
*/30 * * * * root /usr/local/bin/disk-alert.sh
对应的脚本/usr/local/bin/disk-alert.sh内容:
#!/bin/bash
THRESHOLD=85
CURRENT=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$CURRENT" -gt "$THRESHOLD" ]; then
echo "Disk usage on / is at ${CURRENT}%" | mail -s "Disk Alert" admin@example.com
fi
每30分钟检查一次根分区使用率,超过85%就发送邮件告警。对于更复杂的场景,可以用ncdu工具做交互式的磁盘占用分析,它比du更直观,能以树形结构展示目录大小,支持实时删除文件:
sudo apt install ncdu sudo ncdu /var
在ncdu界面中按d键就能直接删除选中的文件或目录,非常适合运维人员交互式排查。
存储空间优化的延伸思路日志清理只是存储空间运维的一个切面。如果清理后空间依然紧张,就需要考虑更底层的优化。检查是否有未删除的内核旧版本,Ubuntu升级内核后旧内核不会自动移除,/boot分区可能被占满:
dpkg --list | grep linux-image sudo apt-get autoremove --purge
autoremove会自动识别并删除不再需要的旧内核和依赖包。另外,/tmp目录下的临时文件也值得定期清理,可以配置tmpfiles.d让系统自动清理超过10天的临时文件:
echo 'd /tmp 1777 root root 10d' | sudo tee /etc/tmpfiles.d/tmp.conf
对于使用ext4文件系统的分区,默认保留给root用户的5%空间也可以通过tune2fs调低,在数据盘上这个保留比例完全可以设为1%甚至0%:
sudo tune2fs -m 1 /dev/sda1
综合运用这些手段,一台Ubuntu服务器的日志和存储空间管理就能从被动救火变成主动防控。日志轮转自动运行,过期归档按时清理,journald和Docker日志都有硬性的上限约束,再加上简单的监控告警,磁盘空间报警的频率会大幅降低。更重要的是,这套方案不依赖任何第三方商业工具,完全基于Ubuntu内置组件实现,稳定性和兼容性都有保障。
