找回密码
 立即注册
首页 业界区 安全 [Docker] Docker中`overlay2`磁盘占用爆满的清理方案 ...

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

扒钒 昨天 00:35
1 需求描述


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

检查磁盘空间情况


  • 检查磁盘空间,确认overlay2占用的空间
  1. # df -h
  2. Filesystem      Size  Used Avail Use% Mounted on
  3. udev            7.9G     0  7.9G   0% /dev
  4. tmpfs           1.6G  2.1M  1.6G   1% /run
  5. /dev/sda1       197G   59G  131G  32% /
  6. tmpfs           7.9G     0  7.9G   0% /dev/shm
  7. tmpfs           5.0M     0  5.0M   0% /run/lock
  8. /dev/sda15      124M   12M  113M  10% /boot/efi
  9. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/3ea43957615f592f2a7b28512fd7f344ac762bfc80a4a964ac467b17f562203e/merged
  10. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/bd8702eb9dcc24aff1a54387374f6609b431aabb1c7131359296867986dc84a0/merged
  11. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/b6b66bb24dc1186c12f18ddb3487a3b81ef40767993a722b08359808635461bd/merged
  12. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/0bf724bd62f24f7411c573b03fef2816c0b14a696ce04e8b78c4616b704b1b86/merged
  13. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/d7caeb11110a19f032d90855ae491c9cb35c5cbbd57daf266e5b12c3494ba21e/merged
  14. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/e4ec25032cb6814980100348c68e937bb4b9e48c098dbf99c5a543428f73e8b7/merged
  15. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/53277c320141d4efa122ad91fe38fe9e7362d29d2cdabb5c3d8c2a1bdea12120/merged
  16. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/b5b65a3ebe16e33091590dbdbea7c51b6ffc8ab894358d9cb7d95934a14a8579/merged
  17. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/864d8e14ba8c74bc627a35847aff844e45acc43686abc7d81c471969fe2b8386/merged
  18. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/5d0d903a3ccd4b5b2cd6741d4eb9654b10667fd5707c13d3d51f2678b5c4d7a2/merged
  19. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/4c2bf66ffcd7f2a8bb03dcee3f5d445c4be9c56b899aaea319689a92193fbaf4/merged
  20. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/3a9e03fd9038307f133da2800527610a6334e458d42d8a406ea30e108dd8ec58/merged
  21. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/4c509ec79872340afca9c9a763782005fa043f5e4c988acc9f24f371d7c79b5c/merged
  22. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/d9bad19c0ce3f9ff18364ee882e52bb9fef3db1c5a99bdbfb97fe4dbbea6f985/merged
  23. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/492549dc47bc8b55a73c945ad3eb699fe34c5e563d22cf4b16383048420fbffe/merged
  24. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/31a7a2393fa100d485b852cda049e4efe7e2d57240a638bde911901a9878e6bd/merged
  25. overlay         197G   59G  131G  32% /var/lib/docker/overlay2/c56e33e03da1a8e849eb8d02a660cfdbf1b21774a99adb8bb1435e072ead0eaf/merged
  26. tmpfs           1.6G     0  1.6G   0% /run/user/0
  27. # cd /var/lib/docker/overlay2
  28. # du --max-depth=1 -ah ./
  29. ...
  30. 3.9G        ./cbd00072f337eac535231f5070dd185eb5396a2a38f503a180d85f4c3dfd56dc
  31. 52K        ./492f6bb1fa6939b3b4c9bde42c488e821e39dc284a506a69009232666b3b646e
  32. 24K        ./4ac719b443482d3094f55f87465fd0a2e055e03d8884aab104c9a9b749b6a888
  33. 311M        ./98f0e8fe08003084033830be8d14843ab31751dd66abec2a859e1123bd4264cf
  34. 4.1M        ./d569993160caa256ff9578a2e2c88a809eb94cbdb19b42bcf789ade5e3ede6f2
  35. 6.8M        ./40cfa823fdcda9ddd582bc3ae8c2e2409f53c2484488b919dd0c90b35759500e
  36. 56K        ./ff0d25275d523771576a3e7ef0249fbcf88e7b94ca0f2e4bc806a2bbcf1391be
  37. 28K        ./c04b54aa554e1a1c82ba1d3af8effefd02d4f1aa2b246b6f863e6643fad56fb8
  38. 4.1G        ./2e645b5187bc64ae8bcd1eb93c70343c68bd94c610681b37faf486b4015eb62e
  39. ...
  40. 786M        ./c195dcf2dc6fc330b450cfdf76066d661bbd9c8c306e68de8c183c9786ee82d8
复制代码
如果看到 overlay Use% 这一栏占用百分比过高,说明确实是docker的overlay目录占用过高导致服务器磁盘空间过小的问题
检查容器的占用情况


  • 检查docker 各模块(镜像、容器、本地卷、缓存)中的占用情况
  1. # docker system df
  2. TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
  3. Images          20        18        8.739GB   684.5MB (7%)
  4. Containers      18        17        11.12MB   6.23kB (0%)
  5. Local Volumes   7         6         348.8MB   72.39MB (20%)
  6. Build Cache     754       0         40.38GB   40.38GB
复制代码


  • 行说明


  • Images: 镜像的数量及占用大小
  • Containers: 容器的数量及占用大小
  • Local Volumes: 本地卷数量及占用大小
  • Build Cache: 打包构建时的缓存大小
我们主要是清理Images、Containers和Build Cache中的文件


  • 列说明


  • RECLAIMABLE 列:英译"可收回的;可教化的",此处指的是“未使用”的镜像或镜像占用的空间(意思是:没有基于这些镜像运行的容器)。
  1. 这是您可以删除而不会破坏任何内容的镜像总大小。
  2. 这正是为什么如果您运行docker system prune -a或docker image prune -a,Docker 会删除它们。
  3. -a 告诉 Docker 删除所有未使用的镜像,没有它,Docker 只会删除悬挂(未标记)的镜像。
复制代码
执行清理操作

清理无用的Images


  • 先查看一下目前存在的镜像
  1. docker images
复制代码
结果
  1. REPOSITORY                                     TAG       IMAGE ID       CREATED        SIZE
  2. <none>                                         <none>    defd79220cd6   2 months ago   239MB
复制代码
可以看到有很多到字样的镜像。这里我只截取一个作为参考。发现数量和占用大小都挺多的。
none镜像被官方称为dangling镜像————代表没有标签且没有被使用过的镜像,可以安全放心的清理。


  • 清理方法也很简单,执行下面命令:
  1. docker image prune
复制代码
这条命令会自动帮我们清除带有的无效镜像
命令执行完毕会提示释放了多少个空间。
然后,我们再来执行docker images命令会发现带有的镜像全部被清理干净了磁盘空间也得到了释放!
清理Containers容器中的日志


  • Containers容器占用最多的基本上就是日志文件
  • Docker 日志(也就是 docker logs 输出的东西) 默认存放在:
这个日志文件可能会越积越大,需要定期清理
  1. /var/lib/docker/containers/<container-id>/<container-id>-json.log
复制代码
例如:
  1. [root@xxx yy]# docker ps
  2. 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
  3. [root@xxx yy]# ls -la /var/lib/docker/containers/675c9b2dcccd648a2f36f70348cc0c2c43cc3de80a51b0ac47215b5044175f42/
  4. total 35744
  5. drwx--x--- 4 root root     4096 Dec 31 23:19 .
  6. drwx--x--- 7 root root     4096 Sep 19 23:22 ..
  7. -rw-r----- 1 root root 36556073 Dec 31 23:37 675c9b2dcccd648a2f36f70348cc0c2c43cc3de80a51b0ac47215b5044175f42-json.log
  8. drwx------ 2 root root     4096 Mar 23  2025 checkpoints
  9. -rw------- 1 root root     3821 Dec 31 23:19 config.v2.json
  10. -rw------- 1 root root     1740 Dec 31 23:19 hostconfig.json
  11. -rw-r--r-- 1 root root       13 Dec 31 23:19 hostname
  12. -rw-r--r-- 1 root root      206 Dec 31 23:19 hosts
  13. drwx--x--- 2 root root     4096 Mar 23  2025 mounts
  14. -rw-r--r-- 1 root root      306 Dec 31 23:19 resolv.conf
  15. -rw-r--r-- 1 root root       71 Dec 31 23:19 resolv.conf.hash
复制代码

  • 从根源限制Docker日志大小(一劳永逸的方法 推荐)
我们可以设置限制日志大小从而不用每次都来手动删除日志
  1. vim /etc/docker/daemon.json  //编辑内容:
  2. {
  3.   "log-driver": "json-file",
  4.   "log-opts": {
  5.     "max-size": "10m",
  6.     "max-file": "3"
  7.   }
  8. }
复制代码
这样,每个容器最多只会占用30MB的日志空间。
写入完成后并不会立刻生效,需要重启运行中的容器。
如果有条件的情况下,建议重启docker会对所有容器都生效:
  1. systemctl restart docker
复制代码

  • 解决方法1:


  • 先找到日志文件路径:
  1. docker inspect <容器名或ID> --format='{{.LogPath}}'
复制代码


  • 然后清空它
  1. truncate -s 0 "$(docker inspect <容器名或ID> --format='{{.LogPath}}')"
复制代码

  • 解决方法2: 还有另一种方式可以一次性清理所有容器
  1. find /var/lib/docker/containers/ -name "*-json.log" -exec truncate -s 0 {} \;
复制代码
不删除文件、不重启服务,不影响任何容器运行
清理构建缓存(Clear Build Cache)


  • Build Cache主要是构建时的缓存,清理它们下次打包构建时速度可能会慢点,对于系统没有任何影响。
如果磁盘占用过高需要及时清理。清理方法也特别简单就一行命令
  1. docker builder prune
复制代码
执行完成后,可以发现服务器的内存又可以释放一截。
3 Docker 服务的文件目录体系

/var/lib/docker/overlay2

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


  • Docker 的 Overlay2 【存储驱动程序】是一个基于内核的图层存储驱动程序,用于创建和管理 Docker 容器。


  • 它通过将多个【只读层】叠加到单个【可写层】来实现这一功能,这使得 Docker 可以高效地共享复用【镜像层】,从而减小了【镜像大小】并加速了【容器的启动速度】。
  • /var/lib/docker/overlay2 是 Overlay2 存储驱动程序的【默认存储目录】,用于保存 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 的核心组成部分及作用


  • 目录结构总览
  1. /var/lib/docker/overlay2/
  2. ├── l   # 软链接文件夹,指向具体层的路径
  3. ├── mnt # 容器挂载点,合并所有层的文件
  4. ├── <hash1>/ # 镜像层(只读)
  5. │   ├── diff/ # 存储该层的文件变更
  6. │   ├── link # 层的唯一标识
  7. │   └── lower # 记录下层的路径
  8. └── <hash2>/ # 容器层(可写)
  9.     ├── diff/ # 存储容器运行时的修改
  10.     ├── link # 层的唯一标识
  11.     ├── lower # 记录所有下层镜像的路径
  12.     └── work # 临时工作目录,用于文件修改
复制代码

  • 关键部分详解


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


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


  • mnt 目录
像「魔法桌面」,把所有层的文件合并显示,容器看到的就是这个合并后的文件系统
例如:
  1. (base) [root@xxxxx halo]# ls -la /var/lib/docker/overlay2/98f0e8fe08003084033830be8d14843ab31751dd66abec2a859e1123bd4264cf
  2. total 52
  3. drwx--x---   4 root root  4096 Feb 15  2025 .
  4. drwx--x--- 160 root root 28672 Dec 31 23:07 ..
  5. drwxr-xr-x   8 root root  4096 Feb 15  2025 diff
  6. -rw-r--r--   1 root root    26 Feb 15  2025 link
  7. -rw-r--r--   1 root root   260 Feb 15  2025 lower
  8. drwx------   2 root root  4096 Feb 15  2025 work
复制代码
overlay2 背后的工作原理
  1. ┌──────────────────────────────────────────────────────────┐
  2. │ 当启动容器时,overlay2 做了这些事:                        │
  3. ├───────────────────┬──────────────────────────────────────┤
  4. │ 1. 找到镜像的所有只读层(如 A、B、C 层)                  │
  5. │   ▼                                                          │
  6. │ 2. 创建一个新的可写层 D,用于存储容器的修改                │
  7. │   ▼                                                          │
  8. │ 3. 在 mnt 目录中,用「联合文件系统」把 A+B+C+D 合并成一个  │
  9. │   ▼                                                          │
  10. │ 4. 容器访问的就是这个合并后的文件系统,修改会存在 D 层      │
  11. └──────────────────────────────────────────────────────────┘
复制代码

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

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


  • 多个容器共享同一镜像
场景:运行 10 个 Nginx 容器,每个容器共享同一个 Nginx 镜像层,只需要存储一次镜像文件。
好处:节省磁盘空间,像 10 个人看同一本漫画书,不用每人买一本。

  • 容器快速创建和删除
场景:测试环境中频繁创建、删除容器(如 CI/CD 部署)。
原理:删除容器只需要删除可写层,镜像层不受影响,像撕掉便签,书还在。

  • 数据持久化
场景:容器中数据库的数据需要保存,即使容器删除也不丢失。
做法:把数据目录挂载到宿主机(绕过 overlay2 的层机制),像把重要的便签单独贴在书桌抽屉里。
底层技术关键点(用代码比喻)


  • 虽然 overlay2 是用 C 语言写的,但可以用 PHP 逻辑理解核心思想:
  1. // 模拟 overlay2 的核心逻辑(非实际代码,仅用于理解)
  2. class Overlay2 {
  3.     // 存储所有层的信息
  4.     private $layers = [];
  5.    
  6.     // 添加一个镜像层(只读)
  7.     public function addImageLayer($layerHash, $parentLayers) {
  8.         $this->layers[$layerHash] = [
  9.             'type' => 'read-only', // 标记为只读
  10.             'parent' => $parentLayers, // 记录下层是谁
  11.             'path' => "/var/lib/docker/overlay2/$layerHash/diff" // 层的文件路径
  12.         ];
  13.         echo "创建镜像层 {$layerHash},像添加一本新漫画书\n";
  14.     }
  15.    
  16.     // 创建容器的可写层
  17.     public function createContainerLayer($containerId, $imageLayers) {
  18.         $layerHash = md5(uniqid()); // 生成唯一标识
  19.         $this->layers[$layerHash] = [
  20.             'type' => 'read-write', // 标记为可写
  21.             'parent' => $imageLayers, // 下层是所有镜像层
  22.             'path' => "/var/lib/docker/overlay2/$layerHash/diff",
  23.             'work_path' => "/var/lib/docker/overlay2/$layerHash/work" // 临时工作目录
  24.         ];
  25.         
  26.         // 创建软链接,方便快速找到层
  27.         symlink($this->layers[$layerHash]['path'], "/var/lib/docker/overlay2/l/$containerId");
  28.         echo "为容器 {$containerId} 创建可写层 {$layerHash},像准备一张空白便签\n";
  29.     }
  30.    
  31.     // 合并所有层,生成容器看到的文件系统
  32.     public function mountContainer($containerId, $mountPath) {
  33.         // 获取所有下层(镜像层)的路径
  34.         $lowerPaths = [];
  35.         foreach ($this->layers as $hash => $layer) {
  36.             if (in_array($hash, $this->layers[$containerId]['parent'])) {
  37.                 $lowerPaths[] = "lowerdir={$layer['path']}";
  38.             }
  39.         }
  40.         
  41.         // 添加当前可写层的路径
  42.         $lowerPaths[] = "upperdir={$this->layers[$containerId]['path']}";
  43.         $lowerPaths[] = "workdir={$this->layers[$containerId]['work_path']}";
  44.         
  45.         // 用联合文件系统挂载(实际是调用 Linux 的 mount 命令)
  46.         $command = "mount -t overlay overlay -o " . implode(',', $lowerPaths) . " $mountPath";
  47.         exec($command);
  48.         echo "把所有层合并到 {$mountPath},像把所有透明纸叠在一起\n";
  49.     }
  50.    
  51.     // 当容器修改文件时(写时复制)
  52.     public function modifyFile($containerId, $filePath) {
  53.         $layer = $this->layers[$containerId];
  54.         $sourceLayer = $this->findFileSourceLayer($filePath, $layer['parent']);
  55.         
  56.         if ($sourceLayer) {
  57.             // 从下层复制文件到当前可写层
  58.             $sourceFile = "{$sourceLayer['path']}/" . substr($filePath, 1);
  59.             $targetFile = "{$layer['path']}/" . substr($filePath, 1);
  60.             copy($sourceFile, $targetFile);
  61.             echo "从下层复制文件到可写层,像把书上的内容抄到便签\n";
  62.         } else {
  63.             // 文件是新建的,直接存在可写层
  64.             touch($targetFile);
  65.             echo "新建文件,存在便签上\n";
  66.         }
  67.     }
  68.    
  69.     // 查找文件在哪个下层
  70.     private function findFileSourceLayer($filePath, $parentHashes) {
  71.         foreach ($parentHashes as $hash) {
  72.             $layerPath = "{$this->layers[$hash]['path']}/" . substr($filePath, 1);
  73.             if (file_exists($layerPath)) {
  74.                 return $this->layers[$hash];
  75.             }
  76.         }
  77.         return false;
  78.     }
  79. }
复制代码
思维导图:overlay2 的全貌
  1. /var/lib/docker/overlay2 的世界
  2. ├── 核心作用:管理容器和镜像的文件层,像叠透明纸
  3. ├── 组成部分:
  4. │   ├── 镜像层(只读):多个容器共享的「基础画纸」
  5. │   ├── 容器层(可写):每个容器独有的「便签纸」
  6. │   ├── mnt 目录:合并所有层的「魔法桌面」
  7. │   └── work 目录:临时修改文件的「草稿本」
  8. ├── 工作原理:
  9. │   ├── 联合文件系统:把多层文件合并显示
  10. │   ├── 写时复制:修改时先复制到可写层,不影响下层
  11. │   └── 软链接:快速找到层的路径,像书签
  12. ├── 使用场景:
  13. │   ├── 多容器共享镜像:节省空间,像多人看同一本书
  14. │   ├── 快速创建容器:只加便签,不复制整本书
  15. │   └── 数据持久化:重要数据单独存,不放在便签上
  16. └── 底层技术:
  17.     ├── Linux 内核的 overlay 或 overlay2 驱动
  18.     ├── mount 命令实现文件系统挂载
  19.     └── 写时复制(Copy-on-Write)机制
复制代码
总结:透过现象看本质

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

  • 用「透明纸叠放」的方式管理镜像和容器的文件,让它们共享底层资源又互不干扰;
  • 当你修改文件时,它偷偷把修改内容放到最上面的「便签纸」上,不破坏下面的「原书」;
  • 最终呈现给容器的,是所有纸叠在一起的「完整画面」,但实际存储的是每一层的差异,节省了大量空间。
/var/lib/docker/containers/<container-id>/<container-id>-json.log

需求参见:本文的第2章 清理容器的日志
Y 推荐文献


  • [Docker] Docker 基础教程(概念/原理/基础操作) - 博客园/千千寰宇
  • [Linux]常用命令之【du/fdisk/df/ls】#磁盘管理/文件管理# - 博客园/千千寰宇
  • /var/lib/docker/overlay2到底是干什么的? - CSDN
X 参考文献


  • Docker中overlay2磁盘占用爆满清理方案 - segmentfault.com
  • 在docker system df中显示的“RECLAIMABLE”空间是什么? - dev59.com
  • 深入了解容器 overlay 文件系统和 /var/lib/docker/overlay2 下的目录作用 - 腾讯云
  • /var/lib/docker/overlay2 占用很大,清理Docker占用的磁盘空间,迁移 /var/lib/docker 目录... - 阿里云
    本文作者:        千千寰宇   
    本文链接:         https://www.cnblogs.com/johnnyzen   
    关于博文:评论和私信会在第一时间回复,或直接私信我。   
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA     许可协议。转载请注明出处!
    日常交流:大数据与软件开发-QQ交流群: 774386015        【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!   

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册