结论: 服务器磁盘清理的正确顺序是:先用 df -h 确认是哪块分区满、df -i 确认是不是 inode 耗尽,再用 du -sh * | sort -hr 或 ncdu 逐层定位占用目录,然后按"日志 → Docker → 包缓存 → 应用数据"的顺序清理,最后用 logrotate 建立长效机制防止复发。删了大文件空间却没释放时,用 lsof | grep deleted 找出仍占用句柄的进程并重启它。
磁盘满不是小问题。系统盘写满后,MySQL 会崩溃、systemd 无法写日志、SSH 登录会卡在认证环节,甚至连 sudo 都可能因为无法写临时文件而失败。更麻烦的是,很多人一上来就 rm -rf 各种目录,结果要么删错了正在使用的数据,要么删完发现 df -h 显示的占用一点没降。本文按"定位 → 排查 → 清理 → 防复发"四段式组织,给出每一步的可复制命令与安全边界。
第一步:确认到底是哪块盘满了
结论:不要一上来就删文件,先用 df -h 确定挂载点、用 df -i 排除 inode 耗尽的可能。 这两者表现相似但处理方式完全不同。
# 查看各分区空间占用(-h 人性化单位,-T 显示文件系统类型)
df -hT
# 查看 inode 使用情况(重点看 IUse% 列)
df -i
# 只看根分区与使用百分比
df -h / | awk 'NR==2 {print $5}'
# 快速找大于 500MB 的单个文件
sudo find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null | sort -k5 -hr | head -20两种"满"需要区分:
- 空间满:
df -h中Use%达到 100%,处理方式是删除大文件。 - inode 满:
df -h还有剩余空间,但df -i的IUse%到 100%,创建文件时报No space left on device。inode 是文件索引节点,每个文件占一个,海量小文件(如 PHP session、邮件队列、容器镜像层)会耗尽 inode。处理方式是删除包含大量小文件的目录,而非删几个大文件。
-xdev 参数表示不跨文件系统,避免把挂载的数据盘、网络存储也扫一遍,既慢又可能误判。
第二步:按目录排查谁在占空间
结论:从根目录下逐层使用 du 定位,或直接装 ncdu 交互查看,两者结合效率最高。 du 统计的是目录实际占用,df 统计的是文件系统分配,两者数值不一致时通常意味着存在"已删除但被进程占用"的文件。
# 查看根目录下各一级目录占用并排序
sudo du -h --max-depth=1 / 2>/dev/null | sort -hr | head -15
# 深入 /var 查看(日志与缓存的重灾区)
sudo du -sh /var/* 2>/dev/null | sort -hr | head -10
# 查看当前目录下各子项占用
sudo du -sh * 2>/dev/null | sort -hr
# 交互式工具,支持键盘进入子目录、d 键删除(需先安装)
sudo apt install -y ncdu # Ubuntu / Debian
# sudo dnf install -y ncdu # CentOS Stream 9
ncdu /按目录占用的排查顺序表
结论:按下面这张表的顺序排查,能用最少时间覆盖 90% 的实际占用来源。 顺序按"出现频率 × 可安全清理程度"排列。
| 排查顺序 | 目录 | 常见占用来源 | 处理动作 | 风险等级 |
|---|---|---|---|---|
| 1 | /var/log | journald 日志、Nginx/Apache 访问日志、应用日志 | journalctl --vacuum-size、logrotate、truncate | 低 |
| 2 | /var/lib/docker | 无用镜像、停止的容器、容器 json 日志、构建缓存 | docker system prune、truncate 容器日志 | 中 |
| 3 | /var/cache | apt/dnf 包缓存、pip/npm 缓存 | apt clean、dnf clean all | 低 |
| 4 | /tmp | 临时文件、上传残留、崩溃转储 | systemd-tmpfiles --clean | 低 |
| 5 | /var/lib/mysql | binlog、慢查询日志、ibdata、表碎片 | PURGE BINARY LOGS、清理慢日志 | 高 |
| 6 | /boot | 旧内核残留(100~300MB/个) | apt autoremove --purge | 中 |
| 7 | /home、/data | 用户上传、备份文件、下载的安装包 | 人工确认后删除或转对象存储 | 高 |
| 8 | /root、各用户目录 | 忘记删除的大备份、core dump | 人工确认后删除 | 高 |
第三步:清理 systemd 日志(journald)
结论:journald 日志是最常见的空间杀手,用 journalctl --vacuum-size=500M 即可安全瘦身,并建议写入配置文件形成长期约束。 这一步对系统无任何副作用,可以放心执行。
# 查看 journal 日志当前占用
journalctl --disk-usage
# 保留最近 500MB(按体积裁剪,最常用)
sudo journalctl --vacuum-size=500M
# 或按时间裁剪:只保留最近 7 天
sudo journalctl --vacuum-time=7d
# 先演练不实际删除(--verify 检查完整性)
sudo journalctl --vacuum-size=500M --verify写入配置实现永久限制:
# /etc/systemd/journald.conf
[Journal]
SystemMaxUse=500M
SystemKeepFree=1G
MaxRetentionSec=7day
ForwardToSyslog=nosudo systemctl restart systemd-journald
journalctl --disk-usage # 确认已降至目标值第四步:清理 Docker 占用
结论:Docker 的空间占用主要来自无用镜像、已停止容器和容器日志,用 docker system df 先看清楚,再用 prune 系列命令回收。 注意:-a 会删除所有未被使用的镜像,--volumes 会删除数据卷,生产环境执行前必须确认数据卷中没有需要保留的数据。
# 查看 Docker 各类资源的占用明细
docker system df
# 安全清理:删除停止的容器、悬空镜像、无用网络与构建缓存
docker system prune
# 彻底清理:额外删除所有未被容器引用的镜像
docker system prune -a
# 单独清理数据卷(危险:会删除未挂载的卷数据)
docker volume prune
# 只清理构建缓存(BuildKit)
docker builder prune
# 容器 json 日志往往是隐形大户,逐个清空(不删文件,避免容器写入失败)
sudo truncate -s 0 /var/lib/docker/containers/*/*-json.log为防止容器日志无限增长,建议在 /etc/docker/daemon.json 中统一限制:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}修改后执行 sudo systemctl restart docker 生效(仅对新创建的容器有效,已有容器需重建)。
第五步:清理应用日志并配置 logrotate
结论:访问量大的站点,Nginx 访问日志一天就能涨几个 GB。正确做法不是定期手工删,而是配置 logrotate(日志轮转)让它自动压缩、保留、删除。
# 应急:清空正在写入的日志(用 truncate 而不是 rm,避免进程句柄失效)
sudo truncate -s 0 /var/log/nginx/access.log
sudo truncate -s 0 /var/log/nginx/error.log
# 查看各应用日志的体积
sudo du -sh /var/log/* 2>/dev/null | sort -hr | head -10
# 手工触发一次轮转(-d 为调试模式,不实际执行)
sudo logrotate -d /etc/logrotate.d/nginx
sudo logrotate -f /etc/logrotate.d/nginx一份标准的 Nginx logrotate 配置如下,若文件已存在则按需修改保留天数:
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # 每天轮转
rotate 14 # 保留 14 份
compress # 压缩旧日志
delaycompress # 延后一份再压缩,避免正在写入
missingok # 文件不存在不报错
notifempty # 空文件不轮转
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}postrotate 中的 kill -USR1 是让 Nginx 重新打开日志文件的关键,缺少它会导致日志继续写进已被重命名的旧文件,空间依旧不降。
第六步:清理包缓存、临时文件与旧内核
结论:这一组操作风险最低、见效最快,可以放心执行。 包缓存与旧内核加起来常常能释放 1~3GB。
# ===== Ubuntu 24.04 / Debian 12 =====
sudo apt clean # 清空 /var/cache/apt/archives
sudo apt autoclean # 只删过期包
sudo apt autoremove --purge -y # 删除不再需要的依赖与旧内核
sudo dpkg -l 'linux-image-*' | grep '^ii' # 确认剩余内核
# ===== CentOS Stream 9 / Rocky Linux 9 =====
sudo dnf clean all
sudo dnf autoremove -y
# 清理语言包与缓存(可选)
sudo rm -rf /var/cache/pip /root/.cache/pip
# 清理 /tmp:推荐用 systemd 的临时文件清理机制,比直接 rm 安全
sudo systemd-tmpfiles --clean
sudo find /tmp -type f -atime +7 -delete
# 清理 core dump(崩溃转储,单个可达数 GB)
sudo find / -xdev -name 'core.*' -size +100M -delete 2>/dev/null需要提醒的是,直接 rm -rf /tmp/* 有风险:/tmp 下可能存放着 MySQL socket、SSH agent、PHP-FPM socket 等正在使用的文件,删除后对应服务会异常。优先用 systemd-tmpfiles --clean 或按访问时间删除 7 天前的文件。
特殊情况:删了大文件但空间没有释放
结论:这是 Linux 的经典陷阱——文件被删除后,如果仍有进程持有它的文件句柄,磁盘块不会被回收。 df -h 依旧显示 100%,但 du 已经统计不到那个文件,两者差值就是被占用的空间。
# 找出所有"已删除但仍被进程占用"的文件
sudo lsof | grep deleted
# 只看大文件的(带 size 列,单位为字节)
sudo lsof -nP | grep deleted | awk '$7 > 104857600'
# 查看某进程打开的文件描述符
sudo ls -l /proc//fd/ | grep deleted 处理方式有二,按影响从小到大排列:
- 在线清空(推荐,服务不中断):找到对应的 fd 编号后执行
sudo truncate -s 0 /proc/,把文件内容清零,空间立即释放,进程无需重启。/fd/ - 重启进程:
sudo systemctl restart nginx(或对应服务名),进程关闭句柄后空间自动回收。生产环境应在低峰期执行。
注意:不要重启数据库来释放空间,正确做法是先在数据库内部清理(如 PURGE BINARY LOGS),重启 mysqld 可能导致数分钟的不可用与缓冲池冷启动带来的性能骤降。
企业QQ咨询




