为什么文件一直在增长?
如果经常使用 Docker,Docker.raw(或Docker.qcow2)的大小可以保持增长,即使文件被删除。
为了演示效果,首先查看主机上文件的当前大小:
$ cd ~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/
$ ls -s Docker.raw
9964528 Docker.raw
注意-s 的使用,它显示了文件实际使用的文件系统块的数量。使用的块数不一定与文件“大小”相同,因为文件可以是sparse。
接下来在单独的终端中启动一个容器,并在其中创建一个 1GiB 的文件:
$ docker run -it alpine sh
# and then inside the container:
/ # dd if=/dev/zero of=1GiB bs=1048576 count=1024
1024+0 records in
1024+0 records out
/ # sync
回到主机再次检查文件大小:
$ ls -s Docker.raw
12061704 Docker.raw
注意大小从9964528 增加到12061704,其中2097176 512-byte 扇区的增加约为 1GiB,正如预期的那样。如果切换回alpine容器终端并删除文件:
/ # rm -f 1GiB
/ # sync
然后检查主机上的文件:
$ ls -s Docker.raw
12059672 Docker.raw
文件没有变小!无论 VM 内的文件发生了什么变化,主机似乎都不知道。
接下来,如果你再次在容器中重新创建“相同”1GiB 文件,然后再次检查大小,你将看到:
$ ls -s Docker.raw
14109456 Docker.raw
它变得更大了!看来如果循环创建和销毁文件,Docker.raw(或Docker.qcow2)的大小会增加到上限(目前设置为64 GiB),即使VM内部的文件系统相对空。
这种奇怪行为的解释在于文件系统通常如何管理块。当要创建或扩展文件时,文件系统会找到一个空闲块并将其添加到文件中。当一个文件被删除时,从文件系统的角度来看,这些块变得“空闲”,但没有人告诉磁盘设备。更糟糕的是,新释放的块可能不会立即被重新使用——这完全取决于文件系统的块分配算法。例如,该算法可能被设计为支持为文件连续分配块:最近释放的块不太可能位于被扩展文件的理想位置。
由于实际中的块分配器倾向于偏爱未使用的块,结果是Docker.raw(或Docker.qcow2)将不断积累新块,其中许多包含陈旧数据。主机上的文件越来越大,尽管 VM 内的文件系统仍然报告有足够的可用空间。
修剪
TRIM 命令(或DISCARD 或UNMAP)允许文件系统向磁盘发出信号,告知磁盘范围内的扇区包含陈旧数据并且可以将其遗忘。这允许:
- 一个 SSD 驱动器,用于擦除和重复使用空间,而不是花时间在四处移动它;和
- Docker for Mac 释放主机文件系统中的块,压缩文件。
那么我们如何做到这一点呢?
Docker for Mac 中的自动 TRIM
在 Docker for Mac 17.11 中有一个名为 trim-after-delete 的“任务”containerd 正在侦听 Docker 映像删除事件。可以通过ctr命令查看:
$ docker run --rm -it --privileged --pid=host walkerlee/nsenter -t 1 -m -u -i -n ctr t ls
TASK PID STATUS
vsudd 1741 RUNNING
acpid 871 RUNNING
diagnose 913 RUNNING
docker-ce 958 RUNNING
host-timesync-daemon 1046 RUNNING
ntpd 1109 RUNNING
trim-after-delete 1339 RUNNING
vpnkit-forwarder 1550 RUNNING
当接收到图像删除事件时,进程会等待几秒钟(以防其他图像被删除,例如作为docker system prune 的一部分),然后在文件系统上运行fstrim。
回到上一节的例子,如果你删除alpine容器内的1 GiB文件
/ # rm -f 1GiB
然后从主机中的终端手动运行fstrim:
$ docker run --rm -it --privileged --pid=host walkerlee/nsenter -t 1 -m -u -i -n fstrim /var/lib/docker
然后检查文件大小:
$ ls -s Docker.raw
9965016 Docker.raw
文件恢复到(大约)原来的大小——空间终于被释放了!
希望this blog 会有所帮助,还请查看以下针对此问题的 macOS docker 实用程序脚本:
https://github.com/wanliqun/macos_docker_toolkit