1 需求描述
- 个人服务器的磁盘空间不足了,恍然发现主要是 docker 占用了太多空间。那么如何安全地清理Docker服务占用的磁盘空间呢?
2 解决方案
检查磁盘空间情况
- # 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% 这一栏占用百分比过高,说明确实是docker的overlay目录占用过高导致服务器磁盘空间过小的问题
检查容器的占用情况
- 检查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: 打包构建时的缓存大小
我们主要是清理Images、Containers和Build Cache中的文件
- RECLAIMABLE 列:英译"可收回的;可教化的",此处指的是“未使用”的镜像或镜像占用的空间(意思是:没有基于这些镜像运行的容器)。
- 这是您可以删除而不会破坏任何内容的镜像总大小。
- 这正是为什么如果您运行docker system prune -a或docker image prune -a,Docker 会删除它们。
- -a 告诉 Docker 删除所有未使用的镜像,没有它,Docker 只会删除悬挂(未标记)的镜像。
复制代码 执行清理操作
清理无用的Images
结果
- REPOSITORY TAG IMAGE ID CREATED SIZE
- <none> <none> defd79220cd6 2 months ago 239MB
复制代码可以看到有很多到字样的镜像。这里我只截取一个作为参考。发现数量和占用大小都挺多的。
none镜像被官方称为dangling镜像————代表没有标签且没有被使用过的镜像,可以安全放心的清理。
这条命令会自动帮我们清除带有的无效镜像。
命令执行完毕会提示释放了多少个空间。
然后,我们再来执行docker images命令会发现带有的镜像全部被清理干净了磁盘空间也得到了释放!
清理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会对所有容器都生效:
- 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主要是构建时的缓存,清理它们下次打包构建时速度可能会慢点,对于系统没有任何影响。
如果磁盘占用过高需要及时清理。清理方法也特别简单就一行命令
执行完成后,可以发现服务器的内存又可以释放一截。
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 的核心组成部分及作用
- /var/lib/docker/overlay2/
- ├── l # 软链接文件夹,指向具体层的路径
- ├── mnt # 容器挂载点,合并所有层的文件
- ├── <hash1>/ # 镜像层(只读)
- │ ├── diff/ # 存储该层的文件变更
- │ ├── link # 层的唯一标识
- │ └── lower # 记录下层的路径
- └── <hash2>/ # 容器层(可写)
- ├── diff/ # 存储容器运行时的修改
- ├── link # 层的唯一标识
- ├── lower # 记录所有下层镜像的路径
- └── work # 临时工作目录,用于文件修改
复制代码
像图书馆的书,多个容器可以共享同一本书(镜像层),节省空间。
像在书上贴便签,容器运行时的修改(如新建文件、修改内容)都存在这里,不影响原书。
像「魔法桌面」,把所有层的文件合并显示,容器看到的就是这个合并后的文件系统
例如:
- (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 层 │
- └──────────────────────────────────────────────────────────┘
复制代码就像叠透明纸,下面的层(镜像)是只读的,上面的层(容器)可以修改。当修改一个文件时:
- 先把原文件从下层「复制」到上层(称为「写时复制」)
- 在上层修改这个复制后的文件,下层文件保持不变
使用场景
场景:运行 10 个 Nginx 容器,每个容器共享同一个 Nginx 镜像层,只需要存储一次镜像文件。
好处:节省磁盘空间,像 10 个人看同一本漫画书,不用每人买一本。
场景:测试环境中频繁创建、删除容器(如 CI/CD 部署)。
原理:删除容器只需要删除可写层,镜像层不受影响,像撕掉便签,书还在。
场景:容器中数据库的数据需要保存,即使容器删除也不丢失。
做法:把数据目录挂载到宿主机(绕过 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 就像一个「文件层管理员」,它的核心魔法是:
- 用「透明纸叠放」的方式管理镜像和容器的文件,让它们共享底层资源又互不干扰;
- 当你修改文件时,它偷偷把修改内容放到最上面的「便签纸」上,不破坏下面的「原书」;
- 最终呈现给容器的,是所有纸叠在一起的「完整画面」,但实际存储的是每一层的差异,节省了大量空间。
/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 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |