前言

由于前两天的 PHP 漏洞,我登录服务器进行漏洞修复时,顺手看了一眼磁盘,发现根盘占用率这几年越来越高:40G 的盘已经用掉 32G,使用率到了 85%。虽然还没真正爆盘,但再装点东西或者日志一涨,很容易出问题。

这次我没有一上来就删文件,而是按“先定位、再清理、最后防反弹”的顺序,把服务器盘和 Docker 里的青龙面板一起梳理了一遍。本文记录的就是这次清理过程。文中的图片是我事后按当时的命令输出重绘的示意图,不是真实截图。

img

先定位:空间到底被谁占了

我先用 df -h 确认是 /dev/vda3 快满了,然后用不跨文件系统的 du -x 逐层定位:

1
2
df -h
du -x -h --max-depth=1 / 2>/dev/null | sort -rh | head -20

一开始直接跑 du -sh / 得到 37G,比 df 的 32G 还大,后来才意识到是 Docker 的 overlay2/…/merged 挂载点被重复统计了。加上 -x 之后数据就正常了。

img

定位结果很明确:/www 13G,/var 11G,/usr 3.8G,/home 1.1G。真正的大头在宝塔备份、系统日志和 Docker,而不是青龙面板本身。

Docker 先看账本,不急着 prune

我先看了 Docker 的占用明细:

1
2
docker system df
docker system df -v

当时三个镜像都在运行中:青龙、qd、redis。容器可写层里青龙约 1.96GB,本地卷合计约 891MB,构建缓存为 0。也就是说 Docker 总共约 4G,不是 32G 的主因。

img

进入青龙容器可写层继续看,发现大部分是缓存和临时文件:

/.pnpm-store/v3 约 1.1G

/ql/.tmp 约 281M,里面是 7 个月前的 data.tgz、static.zip 等更新临时包

/root/.cache 约 119M

/root/.npm 约 70M

/tmp/pip-unpack-* 约 61M

这些都可以在容器内安全清理。注意不要在宿主机直接 rm -rf Overlay2 的 diff 目录,正确做法是进入容器删除,让 Overlay 正常记录删除操作。

1
2
3
4
5
6
7
8
docker exec -it qinglong sh -lc '
rm -rf /ql/.tmp/* 2>/dev/null
rm -rf /.pnpm-store/* 2>/dev/null
npm cache clean --force 2>/dev/null
pip cache purge 2>/dev/null
rm -rf /root/.cache/* /root/.npm/* 2>/dev/null
rm -rf /tmp/pip-unpack-* /tmp/node-compile-cache 2>/dev/null
'

同时删了一个 LINKS=0 的无引用本地卷,约 160.9MB。没有执行 docker volume prune,避免误删以后还要用的卷。

宝塔面板备份:真正的大头

继续看 /www,发现 /www/backup 有 7.7G,其中 /www/backup/panel 一个目录就 7.5G。进去一看,是从 2024-04-27 到 2026-10-22 的每日面板备份 zip,900 多个文件,连续积累了两年多。

img

我的处理方式是保留最近 7 天,其余删除。先预览,再删除:

1
2
ls /www/backup/panel/*.zip | sort | head -n -7
ls /www/backup/panel/*.zip | sort | head -n -7 | xargs -r rm -f

宝塔面板里没有直接显示“备份面板”的计划任务,所以我没有去改某个配置文件,而是用自定义脚本加 crontab 做保留策略。新建 /www/server/panel/script/clean_panel_backup.sh:

1
2
#!/bin/bash
find /www/backup/panel/ -name "*.zip" -type f -mtime +7 -delete

赋权后加入 crontab,每天凌晨 3 点执行:

1
2
3
4
chmod +x /www/server/panel/script/clean_panel_backup.sh
crontab -e
cron
0 3 * * * /www/server/panel/script/clean_panel_backup.sh

这样既保留最近一周的面板备份,又不会再次无限累积。

journal 日志:3.9G 清到 500M

/var/log 当时 4.2G,其中 /var/log/journal 3.9G。这个清理没有风险:

1
2
3
journalctl --disk-usage
journalctl --vacuum-size=500M
journalctl --disk-usage

img

为了避免以后再长回去,我又在 /etc/systemd/journald.conf 里加了上限:

1
SystemMaxUse=500M

然后重启 systemd-journald。

lsof +L1 的 deleted 文件:大部分不用管

中途我也跑了 lsof +L1 | grep deleted,看到 php-fpm、mysqld、BT-Panel、docker 等进程持有 deleted 文件。但关键要看 SIZE/OFF 列:大量项都是 0,真正有大小的只有十几个 MB。

img

我的结论是:只有持有 deleted 可执行文件的系统服务在低峰期值得重启,比如 systemd-logind、agetty、unattended-upgrades;php-fpm、mysqld、BT-Panel、docker 持有的临时/信号量文件不建议仅为它们重启业务。这批文件也不是磁盘占用主因。

青龙依赖怎么判断必要和冗余

青龙的依赖冗余,不能只看“装了多少”,而要看“脚本实际用了什么”。我的做法是:

在青龙面板“依赖管理”里看已安装列表和状态;

命令行辅助确认:

1
2
docker exec -it qinglong pip list
docker exec -it qinglong npm list -g --depth=0

运行主要任务,如果报 Cannot find module 'xxx' 或 ModuleNotFoundError: No module named 'xxx',这个依赖就是必要的;

状态为“安装失败”、且完全没印象的依赖,优先清理;

清理后在“设置 → 历史数据保留与手动清理”里清除 Node/Python 依赖缓存。

这次先清了缓存,依赖本身没有大动。后续准备按“删一个、跑任务、看是否报错”的方式慢慢精简,避免误删导致脚本失败。

清理结果和防反弹

全部处理完,根盘从 32G 已用降到 20G,使用率从 85% 降到约 50%。

img

这次真正起作用的几块是:

项目 约释放
/www/backup/panel 旧 zip 约 7.4G
journal 约 3.4G
青龙容器缓存 约 1.6G
无引用 Docker 卷 约 160M

为了防止反弹,我又做了三件事:

  1. 用 crontab 每天清理 7 天前的面板备份;
  2. 限制 journal 最大 500M;
  3. 给 Docker 配置日志轮转,编辑 /etc/docker/daemon.json:
1
2
3
4
5
6
7
{
"log-driver": "json-file",
"log-opts": {
"max-size": "50m",
"max-file": "3"
}
}

然后重启 Docker。注意这个配置主要影响之后新建的容器,已有容器需要重建才生效。

后记

这次清理最大的感受是:磁盘告警不要上来就 rm,先用 df、du -x、docker system df 把账算清楚。看起来吓人的 lsof +L1 deleted 文件,很多其实只有 0 字节;真正吃掉空间的,往往是宝塔备份、journal 这类“每天都在长、但平时没人看”的目录。

青龙依赖也是一样,依赖列表本身不能说明必要性,最终还是要以脚本能不能正常跑为准。先清缓存,再慢慢删依赖,比一次性大清理稳妥得多。