云服务器磁盘空间不足怎么办?快速定位大文件与清理日志

云服务器磁盘使用率达到百分之百后,数据库可能无法写入,Nginx 可能停止记录日志,软件更新也会失败。此时最危险的做法是直接进入 /var/log 执行批量删除:文件可能仍被进程占用,删错路径还会破坏系统,而且未经备份的日志会失去审计价值。

正确处理顺序是先确认哪个文件系统或 inode 耗尽,再用 du、ncdu 和 find 定位目录与文件,最后针对日志、缓存、容器数据或业务文件选择对应方案。这篇文章给出可直接执行的排查命令,并明确标注删除风险、备份方法和更安全的替代操作。

先用 df 判断容量、inode 还是已删除文件问题

先记录现场,不要立刻清理:

date
df -hT
df -i
lsblk -f
findmnt

df -hT 显示文件系统类型、总量、已用量和挂载点。重点找 Use% 接近百分之百的行,并确认它对应 /、/var、数据盘还是容器盘。df -i 检查 inode;大量零字节或小文件也可能让 inode 用尽,此时容量还有剩余,系统仍无法创建新文件。

现象 可能原因 下一步
某挂载点容量接近满 大日志、备份、上传、容器层、数据库文件 用 du 和 find 在该挂载点内定位
inode 使用率接近满 海量小文件、会话、缓存、邮件队列 按目录统计文件数量并查生成来源
df 很满但 du 对不上 文件已删除但仍被进程打开、挂载覆盖、权限不可读 用 lsof +L1 和 findmnt 排查
根分区正常但应用报无空间 独立数据盘、配额、只读文件系统或保留块 查应用实际路径、挂载与内核日志

df 统计文件系统分配,du 统计可见目录项,两者口径不同。如果日志被删除时服务仍保持文件描述符,目录里看不到它,但占用不会释放。安装并使用 lsof:

# RHEL、Rocky、CentOS
sudo dnf install -y lsof

# Debian、Ubuntu
sudo apt update
sudo apt install -y lsof

sudo lsof +L1

输出中 NLINK 为零的文件就是已删除但仍打开的文件。更安全的释放方式是确认对应服务后,在维护窗口平滑重载或重启,让它关闭旧文件描述符。不要随意终止数据库进程,也不要直接操作陌生的 /proc//fd/,否则可能造成数据损坏或业务中断。

还要留意挂载覆盖。例如,某目录原本写入了大量数据,后来又挂载新磁盘,旧数据会被遮住,du 从当前路径看不到。使用 findmnt 和部署记录确认挂载变化,不要贸然卸载生产数据盘。

用 du、ncdu 和 find 定位大目录与大文件

确定满的是根文件系统后,从同一挂载点逐层检查。-x 可以避免跨入其他文件系统:

sudo du -xhd1 / 2>/dev/null | sort -h

如果 /var 最大,再继续:

sudo du -xhd1 /var 2>/dev/null | sort -h
sudo du -xhd1 /var/log 2>/dev/null | sort -h

du 扫描目录会产生磁盘读取,在文件数量极大的生产服务器上可能影响业务。优先从已知大目录开始,在低峰期执行,并使用 nice 和 ionice 降低排查任务的优先级:

sudo ionice -c2 -n7 nice -n 19 du -xhd1 /var

若希望交互浏览,可以安装 ncdu:

# RHEL、Rocky、CentOS;可能需要扩展仓库
sudo dnf install -y ncdu

# Debian、Ubuntu
sudo apt update
sudo apt install -y ncdu

sudo ncdu -x /var

ncdu 可以按目录大小排序。它也支持交互删除,但生产环境不建议在界面中边看边删,因为容易误选且不便审计。把它作为定位工具,记录路径后退出,再单独确认文件所有者、用途、备份和清理方式。

查找当前文件系统中超过一吉字节的普通文件:

sudo find / -xdev -type f -size +1G -printf '%s\t%p\n' 2>/dev/null | sort -nr | head -n 50

若目标是独立挂载的 /data,把起始路径改成 /data。-xdev 不跨文件系统,能避免扫描网络盘和其他挂载。按修改时间找出三十天未变的文件时,先只打印,不要直接加 -delete:

sudo find /var/log -xdev -type f -mtime +30 -printf '%TY-%Tm-%Td %10s %p\n' | sort

文件旧不代表可以删除。数据库归档、合规日志和业务上传都可能需要长期保留。应与文件所有者确认保留周期,检查是否还有硬链接、进程占用或备份任务依赖。

inode 用尽时,可逐层统计文件数量。下面命令会遍历指定目录,文件极多时同样应在低峰执行:

sudo find /var -xdev -type f -printf '%h\n' 2>/dev/null | sort | uniq -c | sort -nr | head -n 30

会话目录、缓存目录或邮件队列中出现数百万小文件时,先修复清理策略和生成速度,再按应用官方方式清理。单纯删除一批文件而不修根因,磁盘很快会再次耗尽。

安全清理 systemd、应用和容器日志

限制 systemd journal 占用

先查看 journal 当前空间:

sudo journalctl --disk-usage

按总容量保留到五百兆字节,可先轮转当前活动日志,再清理旧归档:

sudo journalctl --rotate
sudo journalctl --vacuum-size=500M
sudo journalctl --disk-usage

--vacuum-size 主要删除已归档的旧 journal,保留量是目标上限附近,不应理解为精确字节。也可以使用 --vacuum-time=14d 按时间保留,但容量策略与审计要求要先确认。若服务器需要保存更长安全日志,应先转发到远端日志平台或备份,再执行清理。

长期可在 /etc/systemd/journald.conf 中配置 SystemMaxUse、SystemKeepFree 等参数。修改前备份文件,变更后重启 systemd-journald 会影响日志服务,应在维护窗口验证。比起故障时临时清理,预先设置容量上限更可靠。

处理普通文本日志

先确认文件大小、类型、最近更新时间和占用进程:

sudo ls -lh /var/log/myapp/
sudo file /var/log/myapp/app.log
sudo lsof /var/log/myapp/app.log

删除风险提示:不要对 /var/log、/var/lib 或任何变量为空的路径执行 rm -rf,也不要复制网上的通配符删除命令。rm 删除通常不进入回收站,路径写错会造成不可逆损失;日志被进程打开时,直接删除目录项还可能出现“文件看不到但空间未释放”。删除前必须用 du 和 ls 再次确认完整路径,核对服务与保留要求,并把重要内容备份到另一块磁盘或远端存储。

如果 /mnt/backup 确认是空间充足的独立备份盘,可以压缩备份单个日志:

findmnt /mnt/backup
df -h /mnt/backup
sudo gzip -c /var/log/myapp/app.log > /mnt/backup/app.log.$(date +%F-%H%M%S).gz
sudo gzip -t /mnt/backup/app.log.*.gz

通配符校验可能匹配多个文件,正式操作时最好写入刚生成的准确文件名。若没有独立备份盘,应先传到受控远端,不要在已经满的同一文件系统内复制大文件。

对仍在写入的日志,优先配置并执行 logrotate,让服务重新打开文件。紧急情况下,在完成备份、确认文件确实只是可丢弃日志并获得批准后,可受控截断:

sudo truncate -s 0 /var/log/myapp/app.log

truncate 会立即丢弃该文件现有内容,仍属于破坏性操作,只是不会删除目录项,服务可继续使用同一个文件描述符。必须写准确绝对路径,执行前用 du -h、ls -l 和 lsof 确认。对数据库数据文件、二进制日志、WAL、队列或未知文件绝不能使用此命令。

若确实要删除一个已确认无进程占用、已有可恢复备份的普通旧文件,至少使用交互确认并阻止以连字符开头的路径被解析为选项:

sudo rm -i -- /完整路径/已确认的旧文件

这不是首选清理方案。能通过应用保留策略、logrotate、归档工具或 truncate 处理的日志,不应直接 rm。执行后还要复查服务和磁盘,而不是假设空间一定释放。

检查容器日志和镜像

Docker 默认 JSON 日志可能持续增长。先查看整体占用:

docker system df -v
sudo du -h /var/lib/docker/containers/*/*-json.log 2>/dev/null | sort -h | tail -n 20

不要直接对 /var/lib/docker 执行批量删除,那里包含镜像层、容器状态和数据卷。docker system prune -a 也不是通用清理命令,它可能删除停用容器和后续回滚所需镜像。应先用 Docker 自身命令确认对象是否仍被使用,并为容器配置日志轮转,例如在 /etc/docker/daemon.json 中设置 json-file 的 max-size 与 max-file,修改前备份配置并用测试节点验证。

数据卷可能保存数据库与上传文件,不能按“未运行容器”就判定无用。任何卷清理都应找到业务所有者、挂载关系和备份,完成恢复验证后再处理。

清理软件缓存、临时文件并考虑扩容

发行版包缓存通常可以安全重建,但清理后下次安装需要重新下载。RHEL、Rocky、CentOS 使用:

sudo dnf clean all

旧版可使用:

sudo yum clean all

Debian、Ubuntu 使用:

sudo apt clean

查看 /tmp 前应确认应用是否正在使用临时文件。相比手工删除,优先使用 systemd 的临时文件规则:

systemd-tmpfiles --cat-config
sudo systemd-tmpfiles --clean

它只按已配置策略处理文件,但仍应先了解规则。生产应用可能把长任务中间文件放在临时目录,错误的过期策略会影响任务,因此要先在测试环境验证。

如果增长来自有效业务数据,清理只是推迟故障,应扩容磁盘或迁移数据。先识别分区与文件系统:

lsblk -f
df -hT
findmnt /目标挂载点

云盘扩容通常分为控制台扩大块设备、扩展分区、扩展文件系统三个阶段。不同平台、分区表、LVM、ext4 与 XFS 的命令不同,不能在不知道设备名时照抄。操作前创建快照与异地备份,阅读平台文档,并在维护窗口完成。常见的文件系统扩展工具是 ext4 的 resize2fs 和 XFS 的 xfs_growfs,但它们必须作用于正确设备或挂载点。

不要尝试通过删除系统库、内核、包数据库或 /var/lib 下的未知内容换空间。若磁盘已经完全写满,可以先从已确认可重建的包缓存、受控 journal 保留和已备份应用日志释放少量空间,再让数据库和系统恢复写入,然后安排正式扩容。

用 logrotate 和监控防止再次写满

logrotate 可以按时间或大小轮转文本日志,并保留指定份数。下面是 /etc/logrotate.d/myapp 的示例:

/var/log/myapp/*.log {
    daily
    rotate 14
    size 100M
    compress
    delaycompress
    missingok
    notifempty
    create 0640 myapp myapp
    sharedscripts
    postrotate
        /bin/systemctl kill -s HUP myapp.service >/dev/null 2>&1 || true
    endscript
}

daily 表示每天检查,size 100M 让大日志达到条件时轮转,rotate 14 保留十四份,compress 压缩旧文件。create 的用户和组必须替换为真实服务账号。postrotate 通过 HUP 请求程序重新打开日志,前提是该服务支持该信号;不支持时应使用应用官方重载方式。

不要随意使用 copytruncate。它适合无法重新打开日志的少数程序,但复制与截断之间存在写入丢失窗口,大日志复制还会产生额外 I/O。能让应用规范重新打开文件时,应优先采用重命名加信号方式。

先用调试模式检查规则,不实际轮转:

sudo logrotate -d /etc/logrotate.conf

确认路径、权限和脚本都正确后,再等待定时任务执行。若要在维护窗口强制测试,可以使用 logrotate -f,但它会真的轮转所有命中规则,必须先评估并尽量只测试单独配置文件。

监控至少覆盖文件系统容量、inode、日增长量和预测耗尽时间。可以设置多个阈值,例如达到百分之七十提醒趋势、百分之八十五告警、百分之九十五触发紧急响应,但具体值应根据扩容速度和业务波动调整。数据库盘还应保留足够空间应对临时表、索引重建和恢复操作。

日志策略要同时定义保留天数、单文件上限、压缩、远端归档与合规要求。只设置“保留十四天”但不限制单文件大小,突发错误仍可能在几小时内写满磁盘。应用发生异常日志风暴时,还要修复错误重试和日志级别,不能完全依赖轮转。

常见问题

df 显示很满,du 却找不到大文件怎么办?

先运行 lsof +L1 查已删除但仍打开的文件,再检查挂载覆盖、权限和文件系统保留空间。确认进程后平滑重载或重启,通常可以释放被占用的已删除文件。

日志太大能直接删除吗?

不建议。先确认路径、内容、占用进程和保留要求,并备份到独立存储。优先使用 logrotate;紧急时只对明确的文本日志执行受控 truncate,不能处理数据库文件。

journalctl --vacuum-size 为什么没有精确降到目标值?

它处理的是可清理的归档日志,并受活动日志和文件粒度影响。可先执行 journalctl --rotate,再清理并复查 --disk-usage。

inode 满了但容量没满,扩容有用吗?

扩容可能增加 inode,但没有修复海量小文件持续产生的根因。应先定位文件最多的目录,按应用策略清理并修复会话、缓存或队列的生命周期。

云盘扩容后为什么 df 仍没变化?

控制台扩容通常只扩大块设备,还需要根据分区、LVM 和文件系统类型执行后续扩展。操作前备份,并严格使用平台与发行版对应步骤,不能凭设备名猜测。

总结

  • 磁盘告警先用 df -hT、df -i 和 findmnt 判断具体文件系统、inode 与挂载问题,再开始扫描。
  • 使用 du、ncdu 和只打印结果的 find 逐层定位,文件量大时在低峰降低扫描优先级。
  • df 与 du 不一致时重点检查已删除但仍打开的文件,优先平滑重载服务释放空间。
  • 日志清理前必须确认并备份,优先使用 journalctl --vacuum-size、logrotate 或受控 truncate,禁止粗暴批量 rm。
  • 有效业务数据持续增长时应扩容或迁移,并通过日志轮转、容量与 inode 告警、增长预测防止再次写满。