Docker] Docker中`overlay2`磁盘占用爆满的清理方案

1 需求描述

  • 个人服务器的磁盘空间不足了,恍然发现主要是 docker 占用了太多空间。那么如何安全地清理Docker服务占用的磁盘空间呢?

回到顶部(Back to Top)

2 解决方案

检查磁盘空间情况

  • 检查磁盘空间,确认overlay2占用的空间
复制代码

|---|------------------------------------------------------------------------------------------------------------------------------|
| | # df -h |
| | Filesystem Size Used Avail Use% Mounted on |
| | udev 7.9G 0 7.9G 0% /dev |
| | tmpfs 1.6G 2.1M 1.6G 1% /run |
| | /dev/sda1 197G 59G 131G 32% / |
| | tmpfs 7.9G 0 7.9G 0% /dev/shm |
| | tmpfs 5.0M 0 5.0M 0% /run/lock |
| | /dev/sda15 124M 12M 113M 10% /boot/efi |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/3ea43957615f592f2a7b28512fd7f344ac762bfc80a4a964ac467b17f562203e/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/bd8702eb9dcc24aff1a54387374f6609b431aabb1c7131359296867986dc84a0/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/b6b66bb24dc1186c12f18ddb3487a3b81ef40767993a722b08359808635461bd/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/0bf724bd62f24f7411c573b03fef2816c0b14a696ce04e8b78c4616b704b1b86/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/d7caeb11110a19f032d90855ae491c9cb35c5cbbd57daf266e5b12c3494ba21e/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/e4ec25032cb6814980100348c68e937bb4b9e48c098dbf99c5a543428f73e8b7/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/53277c320141d4efa122ad91fe38fe9e7362d29d2cdabb5c3d8c2a1bdea12120/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/b5b65a3ebe16e33091590dbdbea7c51b6ffc8ab894358d9cb7d95934a14a8579/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/864d8e14ba8c74bc627a35847aff844e45acc43686abc7d81c471969fe2b8386/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/5d0d903a3ccd4b5b2cd6741d4eb9654b10667fd5707c13d3d51f2678b5c4d7a2/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/4c2bf66ffcd7f2a8bb03dcee3f5d445c4be9c56b899aaea319689a92193fbaf4/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/3a9e03fd9038307f133da2800527610a6334e458d42d8a406ea30e108dd8ec58/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/4c509ec79872340afca9c9a763782005fa043f5e4c988acc9f24f371d7c79b5c/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/d9bad19c0ce3f9ff18364ee882e52bb9fef3db1c5a99bdbfb97fe4dbbea6f985/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/492549dc47bc8b55a73c945ad3eb699fe34c5e563d22cf4b16383048420fbffe/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/31a7a2393fa100d485b852cda049e4efe7e2d57240a638bde911901a9878e6bd/merged |
| | overlay 197G 59G 131G 32% /var/lib/docker/overlay2/c56e33e03da1a8e849eb8d02a660cfdbf1b21774a99adb8bb1435e072ead0eaf/merged |
| | tmpfs 1.6G 0 1.6G 0% /run/user/0 |
| | |
| | |
| | |
| | # cd /var/lib/docker/overlay2 |
| | # du --max-depth=1 -ah ./ |
| | ... |
| | 3.9G ./cbd00072f337eac535231f5070dd185eb5396a2a38f503a180d85f4c3dfd56dc |
| | 52K ./492f6bb1fa6939b3b4c9bde42c488e821e39dc284a506a69009232666b3b646e |
| | 24K ./4ac719b443482d3094f55f87465fd0a2e055e03d8884aab104c9a9b749b6a888 |
| | 311M ./98f0e8fe08003084033830be8d14843ab31751dd66abec2a859e1123bd4264cf |
| | 4.1M ./d569993160caa256ff9578a2e2c88a809eb94cbdb19b42bcf789ade5e3ede6f2 |
| | 6.8M ./40cfa823fdcda9ddd582bc3ae8c2e2409f53c2484488b919dd0c90b35759500e |
| | 56K ./ff0d25275d523771576a3e7ef0249fbcf88e7b94ca0f2e4bc806a2bbcf1391be |
| | 28K ./c04b54aa554e1a1c82ba1d3af8effefd02d4f1aa2b246b6f863e6643fad56fb8 |
| | 4.1G ./2e645b5187bc64ae8bcd1eb93c70343c68bd94c610681b37faf486b4015eb62e |
| | ... |
| | 786M ./c195dcf2dc6fc330b450cfdf76066d661bbd9c8c306e68de8c183c9786ee82d8 |

如果看到 overlay Use% 这一栏占用百分比过高,说明确实是dockeroverlay目录占用过高导致服务器磁盘空间过小的问题

检查容器的占用情况

  • 检查docker 各模块(镜像、容器、本地卷、缓存)中的占用情况
复制代码

|---|-------------------------------------------|
| | # docker system df |
| | TYPE TOTAL ACTIVE SIZE RECLAIMABLE |
| | Images 20 18 8.739GB 684.5MB (7%) |
| | Containers 18 17 11.12MB 6.23kB (0%) |
| | Local Volumes 7 6 348.8MB 72.39MB (20%) |
| | Build Cache 754 0 40.38GB 40.38GB |

  • 行说明
  • Images: 镜像的数量及占用大小
  • Containers: 容器的数量及占用大小
  • Local Volumes: 本地卷数量及占用大小
  • Build Cache: 打包构建时的缓存大小
    我们主要是清理ImagesContainersBuild Cache中的文件
  • 列说明
  • RECLAIMABLE 列:英译"可收回的;可教化的",此处指的是"未使用"的镜像或镜像占用的空间(意思是:没有基于这些镜像运行的容器)。
复制代码

|---|-------------------------------------------------------------------------|
| | 这是您可以删除而不会破坏任何内容的镜像总大小。 |
| | 这正是为什么如果您运行docker system prune -a或docker image prune -a,Docker 会删除它们。 |
| | -a 告诉 Docker 删除所有未使用的镜像,没有它,Docker 只会删除悬挂(未标记)的镜像。 |

执行清理操作

清理无用的Images

  • 先查看一下目前存在的镜像
复制代码

|---|-----------------|
| | docker images |

结果

复制代码

|---|-------------------------------------------------|
| | REPOSITORY TAG IMAGE ID CREATED SIZE |
| | <none> <none> defd79220cd6 2 months ago 239MB |

可以看到有很多到<none>字样的镜像。这里我只截取一个作为参考。发现数量和占用大小都挺多的。
none镜像 被官方称为dangling镜像------------代表没有标签且没有被使用过的镜像,可以安全放心的清理。

  • 清理方法也很简单,执行下面命令:
复制代码

|---|----------------------|
| | docker image prune |

这条命令会自动帮我们清除带有<none>无效镜像

命令执行完毕会提示释放了多少个空间。

然后,我们再来执行docker images命令会发现带有<none>的镜像全部被清理干净了磁盘空间也得到了释放!

清理Containers容器中的日志

  • Containers容器占用最多的基本上就是日志文件
  • Docker 日志(也就是 docker logs 输出的东西) 默认存放在:

这个日志文件可能会越积越大,需要定期清理

复制代码

|---|---------------------------------------------------------------------|
| | /var/lib/docker/containers/<container-id>/<container-id>-json.log |

例如:

复制代码

|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| | [root@xxx yy]# docker ps |
| | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS 675c9b2dcccd nginx:1.24.0 "/docker-entrypoint...." 9 months ago Up 54 minutes 0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp nginx |
| | |
| | [root@xxx yy]# ls -la /var/lib/docker/containers/675c9b2dcccd648a2f36f70348cc0c2c43cc3de80a51b0ac47215b5044175f42/ |
| | total 35744 |
| | drwx--x--- 4 root root 4096 Dec 31 23:19 . |
| | drwx--x--- 7 root root 4096 Sep 19 23:22 .. |
| | -rw-r----- 1 root root 36556073 Dec 31 23:37 675c9b2dcccd648a2f36f70348cc0c2c43cc3de80a51b0ac47215b5044175f42-json.log |
| | drwx------ 2 root root 4096 Mar 23 2025 checkpoints |
| | -rw------- 1 root root 3821 Dec 31 23:19 config.v2.json |
| | -rw------- 1 root root 1740 Dec 31 23:19 hostconfig.json |
| | -rw-r--r-- 1 root root 13 Dec 31 23:19 hostname |
| | -rw-r--r-- 1 root root 206 Dec 31 23:19 hosts |
| | drwx--x--- 2 root root 4096 Mar 23 2025 mounts |
| | -rw-r--r-- 1 root root 306 Dec 31 23:19 resolv.conf |
| | -rw-r--r-- 1 root root 71 Dec 31 23:19 resolv.conf.hash |

  • 从根源限制Docker日志大小(一劳永逸的方法 推荐)

我们可以设置限制日志大小从而不用每次都来手动删除日志

复制代码

|---|---------------------------------------|
| | vim /etc/docker/daemon.json //编辑内容: |
| | |
| | { |
| | "log-driver": "json-file", |
| | "log-opts": { |
| | "max-size": "10m", |
| | "max-file": "3" |
| | } |
| | } |

这样,每个容器 最多只会占用30MB的日志空间。

写入完成后并不会立刻生效 ,需要重启 运行中的容器。

如果有条件的情况下,建议重启docker会对所有容器都生效:

复制代码

|---|----------------------------|
| | systemctl restart docker |

  • 解决方法1:
  • 先找到日志文件路径:
复制代码

|---|-----------------------------------------------------|
| | docker inspect <容器名或ID> --format='{``{.LogPath}}' |

  • 然后清空它
复制代码

|---|------------------------------------------------------------------------|
| | truncate -s 0 "$(docker inspect <容器名或ID> --format='{``{.LogPath}}')" |

  • 解决方法2: 还有另一种方式可以一次性清理所有容器
复制代码

|---|---------------------------------------------------------------------------------|
| | find /var/lib/docker/containers/ -name "*-json.log" -exec truncate -s 0 {} \; |

不删除文件、不重启服务,不影响任何容器运行

清理构建缓存(Clear Build Cache)

  • Build Cache主要是构建时的缓存,清理它们下次打包构建时速度可能会慢点,对于系统没有任何影响。

如果磁盘占用过高需要及时清理。清理方法也特别简单就一行命令

复制代码

|---|------------------------|
| | docker builder prune |

执行完成后,可以发现服务器的内存又可以释放一截。

回到顶部(Back to Top)

3 Docker 服务的文件目录体系

/var/lib/docker/overlay2

Docker Overlay2 : 基于内核的图层存储驱动程序

  • DockerOverlay2存储驱动程序 】是一个基于内核的图层存储驱动程序 ,用于创建和管理 Docker 容器。
  • 它通过将多个【只读层】叠加 到单个【可写层】来实现这一功能,这使得 Docker 可以高效地共享复用【镜像层】,从而减小了【镜像大小】并加速了【容器的启动速度】。
  • /var/lib/docker/overlay2Overlay2 存储驱动程序 的【默认存储目录】,用于保存 Docker 【容器镜像】和【容器数据】。这个目录的结构和用途如下:
  • lower 目录:这个目录包含了所有基础镜像层,它们是只读的并且被叠加在一起。每个基础镜像层都有一个对应的子目录,例如"sha256:c77159850506976d0a9b83b21155b51d88e49c72b1f09493e80d8c664f44a4c41"。
  • upper 目录:这个目录包含了所有叠加的读写层,它们位于基础镜像层之上。这些读写层通常包含容器的修改和新增内容,例如容器的配置文件、日志等。
  • merged 目录:这个目录包含了最终的容器镜像,它是所有叠加层内容的聚合。Docker 使用这个目录来提供容器运行时所需要的文件系统视图。
  • diff 目录:这个目录包含了叠加层的差异内容,即哪些文件或目录在叠加过程中发生了变化。这些差异内容对于 Docker 的备份和迁移操作非常有用。
  • Overlay2 驱动程序使用了基于 inode存储模型,它将不同的【图层】都挂载到【相同的文件系统目录】下,同时使用不同的【命名空间】来进行隔离。

这种设计使得 Overlay2 能够高效地管理【容器】、【镜像】和【数据】,并且能够实现快速的【数据恢复】和【备份】。

在实际应用中,/var/lib/docker/overlay2 目录的大小可能会随着容器的数量和大小而增长。

如果服务器磁盘空间不足 ,可能会导致容器无法正常启动或运行。因此,定期监控和管理 /var/lib/docker/overlay2 目录的大小是非常重要的。

在管理 /var/lib/docker/overlay2 目录时,可以通过删除不必要的容器清理旧的镜像层增加磁盘空间 等方法来减小其大小。

另外,也可以考虑使用 Docker 的磁盘清理工具第三方工具来帮助管理 Docker 存储空间。

用生活类比理解 overlay2 的本质

想象你有一叠透明的画画纸:

  • 最下面一张是「基础画纸」(相当于 Docker 镜像),上面画着固定的图案(程序和系统文件)。
  • 每次在上面盖一张新的「透明纸」(相当于容器的层),你可以在新纸上修改或添加图案,不会破坏下面的纸。
  • overlay2 就是管理这叠纸的「文件夹」,让所有纸看起来像一张完整的画(容器运行时的文件系统)。

overlay2 的核心组成部分及作用

  • 目录结构总览
复制代码

|---|-----------------------------|
| | /var/lib/docker/overlay2/ |
| | ├── l # 软链接文件夹,指向具体层的路径 |
| | ├── mnt # 容器挂载点,合并所有层的文件 |
| | ├── <hash1>/ # 镜像层(只读) |
| | │ ├── diff/ # 存储该层的文件变更 |
| | │ ├── link # 层的唯一标识 |
| | │ └── lower # 记录下层的路径 |
| | └── <hash2>/ # 容器层(可写) |
| | ├── diff/ # 存储容器运行时的修改 |
| | ├── link # 层的唯一标识 |
| | ├── lower # 记录所有下层镜像的路径 |
| | └── work # 临时工作目录,用于文件修改 |

  • 关键部分详解
  • 镜像层(只读层):

像图书馆的书,多个容器可以共享同一本书(镜像层),节省空间。

  • 容器层(可写层):

像在书上贴便签,容器运行时的修改(如新建文件、修改内容)都存在这里,不影响原书。

  • mnt 目录

像「魔法桌面」,把所有层的文件合并显示,容器看到的就是这个合并后的文件系统
例如:

复制代码

|---|------------------------------------------------------------------------------------------------------------------------------|
| | (base) [root@xxxxx halo]# ls -la /var/lib/docker/overlay2/98f0e8fe08003084033830be8d14843ab31751dd66abec2a859e1123bd4264cf |
| | total 52 |
| | drwx--x--- 4 root root 4096 Feb 15 2025 . |
| | drwx--x--- 160 root root 28672 Dec 31 23:07 .. |
| | drwxr-xr-x 8 root root 4096 Feb 15 2025 diff |
| | -rw-r--r-- 1 root root 26 Feb 15 2025 link |
| | -rw-r--r-- 1 root root 260 Feb 15 2025 lower |
| | drwx------ 2 root root 4096 Feb 15 2025 work |

overlay2 背后的工作原理

复制代码

|---|----------------------------------------------------------------|
| | ┌──────────────────────────────────────────────────────────┐ |
| | │ 当启动容器时,overlay2 做了这些事: │ |
| | ├───────────────────┬──────────────────────────────────────┤ |
| | │ 1. 找到镜像的所有只读层(如 A、B、C 层) │ |
| | │ ▼ │ |
| | │ 2. 创建一个新的可写层 D,用于存储容器的修改 │ |
| | │ ▼ │ |
| | │ 3. 在 mnt 目录中,用「联合文件系统」把 A+B+C+D 合并成一个 │ |
| | │ ▼ │ |
| | │ 4. 容器访问的就是这个合并后的文件系统,修改会存在 D 层 │ |
| | └──────────────────────────────────────────────────────────┘ |

  • 联合文件系统原理:

就像叠透明纸,下面的层(镜像)是只读的,上面的层(容器)可以修改。当修改一个文件时:

  • 先把原文件从下层「复制」到上层(称为「写时复制」)
  • 在上层修改这个复制后的文件,下层文件保持不变

使用场景

  1. 多个容器共享同一镜像

场景:运行 10 个 Nginx 容器,每个容器共享同一个 Nginx 镜像层,只需要存储一次镜像文件。

好处:节省磁盘空间,像 10 个人看同一本漫画书,不用每人买一本。

  1. 容器快速创建和删除

场景:测试环境中频繁创建、删除容器(如 CI/CD 部署)。

原理:删除容器只需要删除可写层,镜像层不受影响,像撕掉便签,书还在。

  1. 数据持久化

场景:容器中数据库的数据需要保存,即使容器删除也不丢失。

做法:把数据目录挂载到宿主机(绕过 overlay2 的层机制),像把重要的便签单独贴在书桌抽屉里。

底层技术关键点(用代码比喻)

  • 虽然 overlay2 是用 C 语言写的,但可以用 PHP 逻辑理解核心思想:
复制代码

|---|------------------------------------------------------------------------------------------|
| | // 模拟 overlay2 的核心逻辑(非实际代码,仅用于理解) |
| | class Overlay2 { |
| | // 存储所有层的信息 |
| | private $layers = []; |
| | |
| | // 添加一个镜像层(只读) |
| | public function addImageLayer($layerHash, $parentLayers) { |
| | $this->layers[$layerHash] = [ |
| | 'type' => 'read-only', // 标记为只读 |
| | 'parent' => $parentLayers, // 记录下层是谁 |
| | 'path' => "/var/lib/docker/overlay2/$layerHash/diff" // 层的文件路径 |
| | ]; |
| | echo "创建镜像层 {$layerHash},像添加一本新漫画书\n"; |
| | } |
| | |
| | // 创建容器的可写层 |
| | public function createContainerLayer($containerId, $imageLayers) { |
| | $layerHash = md5(uniqid()); // 生成唯一标识 |
| | $this->layers[$layerHash] = [ |
| | 'type' => 'read-write', // 标记为可写 |
| | 'parent' => $imageLayers, // 下层是所有镜像层 |
| | 'path' => "/var/lib/docker/overlay2/$layerHash/diff", |
| | 'work_path' => "/var/lib/docker/overlay2/$layerHash/work" // 临时工作目录 |
| | ]; |
| | |
| | // 创建软链接,方便快速找到层 |
| | symlink($this->layers[$layerHash]['path'], "/var/lib/docker/overlay2/l/$containerId"); |
| | echo "为容器 {$containerId} 创建可写层 {$layerHash},像准备一张空白便签\n"; |
| | } |
| | |
| | // 合并所有层,生成容器看到的文件系统 |
| | public function mountContainer($containerId, $mountPath) { |
| | // 获取所有下层(镜像层)的路径 |
| | $lowerPaths = []; |
| | foreach ($this->layers as $hash => $layer) { |
| | if (in_array($hash, $this->layers[$containerId]['parent'])) { |
| | $lowerPaths[] = "lowerdir={$layer['path']}"; |
| | } |
| | } |
| | |
| | // 添加当前可写层的路径 |
| | $lowerPaths[] = "upperdir={$this->layers[$containerId]['path']}"; |
| | $lowerPaths[] = "workdir={$this->layers[$containerId]['work_path']}"; |
| | |
| | // 用联合文件系统挂载(实际是调用 Linux 的 mount 命令) |
| | $command = "mount -t overlay overlay -o " . implode(',', $lowerPaths) . " $mountPath"; |
| | exec($command); |
| | echo "把所有层合并到 {$mountPath},像把所有透明纸叠在一起\n"; |
| | } |
| | |
| | // 当容器修改文件时(写时复制) |
| | public function modifyFile($containerId, $filePath) { |
| | $layer = $this->layers[$containerId]; |
| | $sourceLayer = $this->findFileSourceLayer($filePath, $layer['parent']); |
| | |
| | if ($sourceLayer) { |
| | // 从下层复制文件到当前可写层 |
| | $sourceFile = "{$sourceLayer['path']}/" . substr($filePath, 1); |
| | $targetFile = "{$layer['path']}/" . substr($filePath, 1); |
| | copy($sourceFile, $targetFile); |
| | echo "从下层复制文件到可写层,像把书上的内容抄到便签\n"; |
| | } else { |
| | // 文件是新建的,直接存在可写层 |
| | touch($targetFile); |
| | echo "新建文件,存在便签上\n"; |
| | } |
| | } |
| | |
| | // 查找文件在哪个下层 |
| | private function findFileSourceLayer($filePath, $parentHashes) { |
| | foreach ($parentHashes as $hash) { |
| | $layerPath = "{$this->layers[$hash]['path']}/" . substr($filePath, 1); |
| | if (file_exists($layerPath)) { |
| | return $this->layers[$hash]; |
| | } |
| | } |
| | return false; |
| | } |
| | } |

思维导图:overlay2 的全貌

复制代码

|---|---------------------------------------|
| | /var/lib/docker/overlay2 的世界 |
| | ├── 核心作用:管理容器和镜像的文件层,像叠透明纸 |
| | ├── 组成部分: |
| | │ ├── 镜像层(只读):多个容器共享的「基础画纸」 |
| | │ ├── 容器层(可写):每个容器独有的「便签纸」 |
| | │ ├── mnt 目录:合并所有层的「魔法桌面」 |
| | │ └── work 目录:临时修改文件的「草稿本」 |
| | ├── 工作原理: |
| | │ ├── 联合文件系统:把多层文件合并显示 |
| | │ ├── 写时复制:修改时先复制到可写层,不影响下层 |
| | │ └── 软链接:快速找到层的路径,像书签 |
| | ├── 使用场景: |
| | │ ├── 多容器共享镜像:节省空间,像多人看同一本书 |
| | │ ├── 快速创建容器:只加便签,不复制整本书 |
| | │ └── 数据持久化:重要数据单独存,不放在便签上 |
| | └── 底层技术: |
| | ├── Linux 内核的 overlay 或 overlay2 驱动 |
| | ├── mount 命令实现文件系统挂载 |
| | └── 写时复制(Copy-on-Write)机制 |

总结:透过现象看本质

overlay2 就像一个「文件层管理员」,它的核心魔法是:

相关推荐
wwj888wwj16 小时前
Docker基础(复习)
java·linux·运维·docker
DONG99917 小时前
配置docker代理
docker·容器
怎么就重名了17 小时前
docker可以动态修改端口映射吗
运维·docker·容器
JEECG低代码平台18 小时前
敲敲云零代码平台一键部署实战:命令安装 vs Docker 安装
运维·docker·容器
p***769820 小时前
NAS飞牛Docker 部署OmniBox影视资源聚合平台:网盘秒播、影视聚合、自定义直播,超神的一条龙服务
运维·docker·容器
http阿拉丁神猫21 小时前
kubernetes知识点汇总31-36
云原生·容器·kubernetes
爱学习的程序媛1 天前
Docker 完全指南:从入门到生产级实践
运维·docker·容器
分布式存储与RustFS1 天前
Windows原生版RustFS:无需Docker,1分钟本地对象存储环境搭建
windows·docker·容器·对象存储·minio·企业存储·rustfs
问道飞鱼1 天前
【分布式技术】RustFS 非 Docker 部署完整指南:从单机到生产集群
分布式·docker·容器·rustfs