soarli

Docker “悬案”:一个 66G 的 GitLab 容器如何吃掉我的硬盘
今天我想分享一个前几天遇到的真实案例:一个平平无奇的 Docker 容器,在几天之内悄无声息地“吃”掉了我服务器 ...
扫描右侧二维码阅读全文
19
2025/10

Docker “悬案”:一个 66G 的 GitLab 容器如何吃掉我的硬盘

今天我想分享一个前几天遇到的真实案例:一个平平无奇的 Docker 容器,在几天之内悄无声息地“吃”掉了我服务器 66GB 的硬盘空间。如果你也遇到了类似 /var/lib/docker 目录异常庞大的问题,希望这篇文章能帮你快速定位并解决问题。


案发现场:一切的开始 🕵️‍♂️

故事的开头很简单,我通过 docker run 命令在服务器上部署了一个 GitLab-CE 社区版容器。命令如下:

sudo docker run -d \
  -p 443:443 -p 80:80 -p 222:22 \
  --name gitlab \
  --restart always \
  -v /home/gitlab/config:/etc/gitlab \
  -v /home/gitlab/logs:/var/log/gitlab \
  -v /home/gitlab/data:/var/opt/gitlab \
  gitlab/gitlab-ce:17.11.4-ce.0

我明明已经将 GitLab 的三个核心数据目录 config, logs, data 都通过 -v 参数挂载到了机械盘的 /home/gitlab 目录下。然而,几天后,我收到了磁盘空间告警。使用 du 命令检查后,结果令我震惊:

root@gbfwq:~# du -h --max-depth=1 /var/lib/docker
...
66G     /var/lib/docker/containers
7.6G    /var/lib/docker/overlay2
73G     /var/lib/docker

/var/lib/docker/containers 目录竟然占用了 66GB!这不科学,我的数据不是应该都在机械盘上吗?一场“硬盘空间失踪案”的侦破就此展开。


侦破之路:一波三折的排查

第一条线索:容器内部文件?

我的第一反应是:是不是 GitLab 在容器内部的其他未挂载目录中写入了大量文件?我立刻 exec 进入容器内部进行调查:

# 进入容器
docker exec -it gitlab bash

# 在容器内检查根目录大小
root@69784fa27abc:/# du -h --max-depth=1 /
...
3.6G    /opt
4.4G    /var
...
8.2G    /

奇怪的事情发生了,容器内部所有目录加起来的总大小也才 8.2G,与宿主机上显示的 66G 相差甚远。这条线索断了。

第二条线索:“幽灵文件”?

熟悉 Linux 的朋友可能知道,如果一个进程打开了一个文件,然后这个文件被删除了,但进程的句柄(handle)没有关闭,那么文件的磁盘空间是不会被释放的。这种文件 du 是统计不到的,我称之为“幽灵文件”。

最简单的验证方法就是重启容器,因为重启会杀死并重建所有进程,自然会释放所有文件句柄。

docker restart gitlab

然而,重启后再次检查,66G 的空间纹丝不动。这条线索也断了。

峰回路转:find 命令立大功!

之前的排查都陷入了僵局。我决定回到宿主机,不再去猜测,而是直接寻找那个“大家伙”。我决定使用 find 命令,在 /var/lib/docker/containers 目录里找出所有大于 1GB 的文件。

sudo find /var/lib/docker/containers -type f -size +1G -exec ls -lh {} \;

几秒钟后,真相浮出水面:

-rw-r----- 1 root root 66G Oct 18 18:37 /var/lib/docker/containers/697.../697...-json.log

破案了! 元凶就是这个藏在容器目录深处的 -json.log 文件。这是 Docker 默认的容器日志文件。


真相大白与解决方案

问题的根源

Docker 默认会捕获容器主进程的所有标准输出(stdout)和标准错误(stderr),并以 JSON 格式存储在一个日志文件中。关键在于,Docker 默认对这个日志文件的大小和数量没有任何限制!

GitLab 是一个复杂的应用,内部有 Nginx、PostgreSQL、Redis、Puma 等多个服务,它们会持续不断地输出运行日志。日积月累,这个日志文件就长成了一个 66GB 的庞然大物。

解决方案(分两步走)

第 1 步:紧急止血 - 立即释放空间

首先,我们需要把这个巨大的日志文件清空,把被占用的空间立刻拿回来。使用 truncate 命令是最高效的方式。

🚨 警告:此操作会清空该容器的所有历史日志,请确认没有影响后再执行。

# 将下面的长路径替换为你自己 find 命令找到的路径
sudo truncate -s 0 /var/lib/docker/containers/697.../697...-json.log

执行完毕,66GB 的空间瞬间就回来了!

第 2 步:根治问题 - 配置日志轮转

为了防止未来再次发生同样的问题,我们必须给 Docker 的日志系统立个规矩。这通过配置 Docker 的守护进程文件 daemon.json 来实现。

  1. 编辑或创建配置文件

    sudo nano /etc/docker/daemon.json
  2. 写入日志轮转配置

假设我们给日志的预算是 10GB,可以设置为 10 个 1GB 的文件。

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "1g",
    "max-file": "10"
  }
}
    • "max-size": "1g":设置单个日志文件的最大体积为 1GB。
    • "max-file": "10":设置最多保留 10 个日志文件。当超过数量时,最旧的会被删除。
    1. 重启 Docker 服务使配置生效

      sudo systemctl restart docker

    重要提示:这个配置只对未来新产生的日志生效,它不会自动清理已经存在的旧文件。这就是为什么我们必须先手动执行第一步的 truncate 操作。


    最佳实践:迁移整个 Docker 目录

    如果你想从根本上为你的系统盘(通常是SSD)减负,最佳实践是将整个 Docker 的数据目录(包括镜像、容器、日志等)都迁移到机械盘。

    这同样通过修改 daemon.json 文件实现,增加一个 "data-root" 参数即可。

    {
      "data-root": "/home/docker-data",
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "1g",
        "max-file": "10"
      }
    }

    具体迁移步骤包括:停止Docker -> 修改配置 -> rsync 迁移数据 -> 重启Docker -> 删除旧数据。这里就不再赘述。


    总结

    这次“硬盘失踪案”给了我们一个深刻的教训:永远不要忽略对 Docker 容器日志的管理。对于任何生产环境或长期运行的容器,配置日志轮转 (log-opts) 都应该是一个标准操作。希望我的这次排查经历能帮你少走弯路!

    最后修改:2025 年 10 月 19 日 03 : 41 AM

    发表评论