硬盘空间消失之谜:Linux 服务器存储排查与优化全过程

前言

最近线上服务经常出现一些奇奇怪怪的问题,比如网页上的静态资源加载不出来,或者请求后端莫名报错,又或者 Redis 报错...

当我 SSH 登录到服务器上时,更不对劲了,敲个命令都卡顿...

如果是以前没经验,或许会以为遇到了疑难杂症,但作为多年的 Linux 用户,我已经知道了这种种异常的背后是「存储空间已满」在作祟!

那么问题就来到了「硬盘空间去哪儿了?」

排查

先用 df 看个大概

首先是最常用的命令

bash 复制代码
df -h

先来看看各个磁盘和挂载点的情况

在我的服务器上执行这个命令之后发现,根目录的可用空间已经只剩下几百 KB 了...

再用 du 看具体

使用 du 命令可以查看各个子目录占用的空间,然后再结合 sort 命令排个序

bash 复制代码
sudo du -h --max-depth=1 / | sort -hr

参数说明:

  • 因为要直接统计根目录,所以需要有 root 权限
  • --max-depth=1:仅显示当前目录及其下一级子目录的占用情况。
  • sort -hr:按照大小进行降序排列。

查看目录的总占用空间:

bash 复制代码
du -sh /

更好的工具

前面的 du 命令,还是没那么好用,主要是列出来的数据太多了。

这次我用上了 ncdu 工具

ncdu 是一个更加用户友好的磁盘使用分析工具,支持交互式界面。

不过很多发行版没有内置,需要先安装:

bash 复制代码
sudo apt install ncdu

使用 ncdu 分析根目录占用

bash 复制代码
sudo ncdu -x /

使用 -x 参数可以限制扫描范围为当前文件系统,不跨越挂载点。因为根目录下有很多其他硬盘的挂载点,根据前面使用 df 命令的分析,是主硬盘满了,所以我只要看主硬盘的就行。

ncdu 启动之后会扫描各个文件的存储空间,然后进入一个交互式界面,可以很直观的看到各个目录的占用空间大小,从大到小排序。

罪魁祸首

在 ncdu 里可以很直观看到 /var/lib/docker 这个目录占用了 70% 以上的存储空间,妥妥的答辩啊!

使用 du 分析

bash 复制代码
sudo du -h --max-depth=1 /var/lib/docker | sort -hr

之后大概的占用情况是这样(只是例子,不是真实数据)

c 复制代码
10G     /var/lib/docker/overlay2
5G      /var/lib/docker/volumes
1G      /var/lib/docker/containers
500M    /var/lib/docker/images

基本就是 docker 的镜像、容器日志、卷之类的把硬盘吃掉了

清理 Docker 的未使用资源

找到了问题,那就好办了

首先把没用的 docker 容器停掉

然后执行一下 docker 提供的一些清理命令。

清理未使用的资源(镜像、容器、卷和网络)

Docker 提供了 docker system prune 命令,能清理未使用的资源。

bash 复制代码
docker system prune -a
  • -a:删除所有未使用的镜像(包括没有关联到容器的镜像)。
  • 注意:这个命令不会删除被正在运行的容器依赖的资源,请小心操作。

仅清理未使用的卷

如果是 /var/lib/docker/volumes 占用空间较多:

bash 复制代码
docker volume prune

仅清理未使用的网络

如果是 /var/lib/docker/network 占用较多:

bash 复制代码
docker network prune

删除不需要的容器、镜像和卷

删除未运行的容器

bash 复制代码
docker container prune

删除无用的镜像

bash 复制代码
docker image prune -a

删除无用的卷

bash 复制代码
docker volume prune

清理旧的镜像和未使用的标签

如果在使用大量镜像,很多旧版本可能已经没有用了。

列出镜像按大小排序

bash 复制代码
docker images --format "{{.Repository}}:{{.Tag}} {{.Size}}" | sort -k2 -h

删除指定镜像

bash 复制代码
docker rmi <image-id>

啥玩意这么大?

在分析存储空间的占用过程中,我还发现有个文件特别离谱,下面这个文件,500多个G...

bash 复制代码
/var/lib/docker/containers/e4b5a99b429a612885417460214ea40a6a49a3360c29180af800ff7aef4c03df/e4b5a99b429a612885417460214ea40a6a49a3360c29180af800ff7aef4c03df-json.log

找出拉屎的容器

来看看是哪个容器拉的屎。

日志文件的路径中包含了容器的 ID:e4b5a99b429a612885417460214ea40a6a49a3360c29180af800ff7aef4c03df

来找一下是哪个容器:

bash 复制代码
docker ps | grep e4b5a99b429a

在容器列表中找不到该容器,可能它已经被停止或删除了。在这种情况下,可以使用 docker inspect 检查具体信息:

bash 复制代码
docker inspect e4b5a99b429a

分析日志内容

可以通过 tailless 查看日志内容,检查是否有异常输出:

bash 复制代码
sudo tail -n 50 /var/lib/docker/containers/e4b5a99b429a612885417460214ea40a6a49a3360c29180af800ff7aef4c03df/e4b5a99b429a612885417460214ea40a6a49a3360c29180af800ff7aef4c03df-json.log

日志的具体内容我就不贴了,看起来应该没啥问题,就是运行久了,日积月累...

处理这个问题的方法,见下一小节。

清理 docker 日志文件

如果 /var/lib/docker/containers 占用大量空间,可能是容器日志文件过大。

查看日志文件

每个容器的日志存储在 /var/lib/docker/containers/<container-id>/<container-id>-json.log

使用以下命令找到最大的日志文件:

bash 复制代码
sudo find /var/lib/docker/containers/ -type f -name "*.log" -exec du -h {} + | sort -hr | head -n 10

手动清理日志

清空一个特定容器的日志文件:

bash 复制代码
sudo truncate -s 0 /var/lib/docker/containers/<container-id>/<container-id>-json.log

设置日志文件大小限制

在 Docker 的配置文件中限制日志大小(推荐):

  1. 编辑 Docker 配置文件(通常是 /etc/docker/daemon.json):

    bash 复制代码
    sudo nano /etc/docker/daemon.json
  2. 添加或修改以下配置:

    json 复制代码
    {
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "10m",
        "max-file": "3"
      }
    }
    • max-size:单个日志文件最大 10 MB。
    • max-file:保留 3 个日志文件。
  3. 重新加载 Docker:

    bash 复制代码
    sudo systemctl restart docker

迁移 /var/lib/docker 到新磁盘

可以将 /var/lib/docker 挂载到其他磁盘,从而缓解当前磁盘的存储压力。这是一个常见的做法,尤其在有多块磁盘的情况下。

方法一:直接迁移 /var/lib/docker 到新磁盘

停止 Docker 服务

在迁移数据之前,需要先停止 Docker 服务:

bash 复制代码
sudo systemctl stop docker

移动 /var/lib/docker 到新位置

假设新磁盘挂载在 /mnt/new-disk,执行以下命令:

bash 复制代码
sudo mv /var/lib/docker /mnt/new-disk/docker

创建软链接

将新的路径链接回 /var/lib/docker,让 Docker 继续按默认路径工作:

bash 复制代码
sudo ln -s /mnt/new-disk/docker /var/lib/docker

启动 Docker 服务

bash 复制代码
sudo systemctl start docker

验证

运行以下命令确保 Docker 正常工作:

bash 复制代码
docker info

方法二:修改 Docker 配置文件,指定新存储位置

停止 Docker 服务

bash 复制代码
sudo systemctl stop docker

移动 /var/lib/docker 到新磁盘

将现有数据迁移到新磁盘挂载点。例如,新磁盘挂载在 /mnt/new-disk

bash 复制代码
sudo mv /var/lib/docker /mnt/new-disk/docker

修改 Docker 配置

编辑 Docker 的配置文件(通常是 /etc/docker/daemon.json),指定新的存储路径:

bash 复制代码
sudo nano /etc/docker/daemon.json

添加或修改以下内容:

json 复制代码
{
  "data-root": "/mnt/new-disk/docker"
}

启动 Docker 服务

bash 复制代码
sudo systemctl start docker

验证

再次检查 Docker 是否正常运行:

bash 复制代码
docker info | grep "Docker Root Dir"

你应该能看到新的路径(如 /mnt/new-disk/docker)。

方法三:直接挂载新磁盘到 /var/lib/docker

如果想直接将新磁盘作为 /var/lib/docker 的挂载点,可以使用以下方法

格式化新磁盘

假设新磁盘为 /dev/sdb1,先格式化并创建文件系统(如 ext4):

bash 复制代码
sudo mkfs.ext4 /dev/sdb1

挂载新磁盘

将新磁盘挂载到 /var/lib/docker

bash 复制代码
sudo mount /dev/sdb1 /var/lib/docker

迁移现有数据

如果 /var/lib/docker 目录下已有数据,需要先复制到新磁盘:

bash 复制代码
sudo rsync -a /var/lib/docker/ /mnt/new-disk/

然后再将新磁盘挂载回 /var/lib/docker

修改 /etc/fstab 确保开机自动挂载

编辑 /etc/fstab 文件,添加一行挂载配置:

bash 复制代码
/dev/sdb1  /var/lib/docker  ext4  defaults  0  2

注意事项

  1. 数据迁移风险 :在迁移或重建 /var/lib/docker 时,务必备份重要数据(如持久卷数据)。

  2. 权限问题:确保新目录的权限与原始目录一致:

    bash 复制代码
    sudo chown -R root:root /mnt/new-disk/docker
  3. 检查挂载点:确保新磁盘挂载成功,并设置自动挂载,避免系统重启后路径丢失。

通过以上方法,可以成功将 /var/lib/docker 挂载到其他磁盘,缓解存储压力并优化存储布局。

重建 /var/lib/docker

如果清理后空间仍然不足,可以重建 Docker 的存储目录(会删除所有容器、镜像和数据)

停止 Docker 服务:

bash 复制代码
sudo systemctl stop docker

备份现有 Docker 数据(可选):

bash 复制代码
sudo mv /var/lib/docker /var/lib/docker.bak

创建一个新的空目录:

bash 复制代码
sudo mkdir /var/lib/docker

启动 Docker 服务:

bash 复制代码
sudo systemctl start docker

总结

在这次 Linux 服务器硬盘空间消失问题的排查过程中,我经历了一次完整的存储分析和优化实战。

关键步骤概括:

  1. 初步排查存储占用情况
    • 使用 duncdu 等工具,快速定位占用空间较大的目录。
    • 发现 /var/lib/docker 目录占用了大量存储空间。
  2. 深入定位具体问题
    • 找到具体的容器日志文件路径,通过容器 ID 确认了是哪个容器产生了大量日志。
    • 使用 docker inspectdocker logs,进一步分析日志内容。
  3. 解决问题
    • 清空了过大的容器日志文件,通过 truncate 命令立即释放空间。
    • 修改 Docker 配置文件(daemon.json)限制了日志文件的大小,避免类似问题再次发生。
  4. 验证与优化
    • 重启 Docker 服务后,验证了服务正常运行。
    • 使用 docker system prune 清理了无用资源,并规划了日志管理策略。

个人收获与思考:

这次问题的解决让我深刻体会到以下几点:

  • 系统监控的重要性:及时监控存储使用情况,可以避免问题扩大化。
  • 日志管理最佳实践:过度增长的日志文件是常见的存储占用原因,必须设置合理的日志大小限制。
  • 工具的高效使用duncdu 和 Docker 命令等工具在排查问题中大大提升了效率。
  • 日常维护习惯:定期清理无用的容器资源(例如停止的容器、未使用的镜像),可以保持系统健康运行。

这次实践不仅解决了磁盘空间问题,也让我对 Linux 系统管理和 Docker 的运维有了更深的理解。在未来的运维工作中,我将更加注重系统的监控与优化,提前预防类似问题的发生。

相关推荐
一起逃去看海吧16 分钟前
dify-03
java·linux·开发语言
fengyehongWorld17 分钟前
Linux 根据端口进行的相关查询
linux
lihao lihao20 分钟前
linux匿名管道
linux·运维·服务器
うちは止水23 分钟前
weston出图调试
linux·wayland·weston
STDD24 分钟前
Farming Simulator 25(模拟农场 25) Linux 专服搭建完全指南
linux·运维·javascript
好好风格1 小时前
宝塔面板 HTTPS 端口证书不生效排查记录
linux·运维·nginx
用户2367829801682 小时前
Linux pgrep 命令详解:按名称查找进程 PID 的高效方法
linux
zzipeng2 小时前
Linux LCD驱动
linux·运维·服务器
思麟呀2 小时前
C++11并发编程:call_once一次性执行+atomic原子类型+CAS无锁编程+自旋锁
linux·开发语言·jvm·c++·windows