如何清理Docker ZFS遗留的共享资源?

11

概述

鉴于以下情况:

  • docker存储驱动程序使用ZFS;
  • docker创建legacy数据集;

Bash:

$ docker ps -a | wc -l
16

$ docker volume ls | wc -l
12

$ zfs list | grep legacy | wc -l
157

这里有16个容器(正在运行和停止的)。12个卷。 157个数据集。 这似乎是许多遗留数据集。我想知道它们中的很多是否已经变得孤立,以至于甚至连docker也不再知道它们,所以它们不会被清理。

背景

在我的Debian zfs池中有一个巨大的遗留卷列表。当我在这台机器上开始使用Docker时,它们开始出现:

$ sudo zfs list | grep legacy | wc -l
486

它们都是以下形式:

pool/var/<64-char-hash>                  202K  6,18T   818M  legacy

此位置仅供docker使用。

$ docker info | grep -e Storage -e Dataset
Storage Driver: zfs
 Parent Dataset: pool/var

我开始清理了。
$ docker system prune -a
  (...)
$ sudo zfs list | grep legacy | wc -l
154

这样就好了。不过,我只运行了大约15个容器,在运行docker system prune -a之后,每个容器的历史记录显示只有最后一个镜像层仍然可用。其余的都是<missing>(因为它们已被清理掉)。

$ docker images | wc -l
15

如果在清理其余部分后所有容器仅使用最后一个镜像层,那么Docker是否只应使用15个镜像层和15个运行容器,总共30个卷?
$ sudo zfs list | grep legacy | wc -l
154

我能否找出容器/镜像是否在使用中?是否有一条命令可以遍历ZFS中所有pool/var/<hash>数据集,并找出它们属于哪个docker容器/镜像?可能有很多可以删除的内容,但我不知道如何确定它们已经无用(除了信任docker system prune之外)。

由于docker过度使用zfs卷,我的zfs list命令混乱不堪,无论是视觉上还是性能上。现在列出zfs卷需要大约10秒钟,而不是小于1秒钟。

证明docker不再看到悬空计数

$ docker ps -qa --no-trunc --filter "status=exited"
  (no output)
$ docker images --filter "dangling=true" -q --no-trunc
  (no output)
$ docker volume ls -qf dangling=true
  (no output)

zfs list 的例子:

NAME                                                                                       USED  AVAIL  REFER  MOUNTPOINT
pool                                                                                      11,8T  5,81T   128K  /pool
pool/var                                                                                   154G  5,81T   147G  /mnt/var
pool/var/0028ab70abecb2e052d1b7ffc4fdccb74546350d33857894e22dcde2ed592c1c                 1,43M  5,81T  1,42M  legacy
pool/var/0028ab70abecb2e052d1b7ffc4fdccb74546350d33857894e22dcde2ed592c1c@211422332       10,7K      -  1,42M  -
# and 150 more of the last two with different hashes

你尝试过这里建议的指令吗?https://dev59.com/WlsV5IYBdhLWcg3w4iT- - Dan
我现在已经做了,感谢您的建议。不幸的是,它无法用于查找用于映像或层的挂载点。它只能找到具有特定“卷”的容器,例如docker volume ls中的容器 - 这些容器仅约有15个卷(如预期)。 - Redsandro
我已经读了你的问题超过10次。然后我意识到也许你根本不是在谈论Docker中的卷。由于Docker中的卷不能从空气中出现,我们必须使用“-v”标志来指定它们。你能否提供一下“sudo zfs list”的内容?也许在那之后我应该编辑下面的答案... - Light.G
1
@Light.G 由于使用了 zfs 存储驱动,它们很可能也是 Docker 层。但我怀疑它们是来自一个古老版本的孤立层。请参考问题末尾编辑后的示例输出。 - Redsandro
1
@Redsandro:可以确认最新的Docker行为,做了修剪操作,看起来像是Docker中的(又一个)bug :/这个命令对于Docker有效(但会删除所有卷/镜像等等):zfs list -r rpool/docker | awk '/docker\// { print $1 }' | xargs -l zfs destroy -Rrpool/docker替换为您本地的Docker数据集。 - kei1aeh5quahQu4U
显示剩余2条评论
3个回答

6

我有同样的问题,但找不到满意的答案。由于这个问题是搜索结果中排名靠前的之一,因此添加了我最终找到的

背景

Docker 的 ZFS 存储驱动程序将每个镜像的每个层存储为单独的传统数据集。

即使只有少量镜像,也可能会导致大量层,每个层对应一个legacy ZFS 数据集。

  • 引用来自Docker ZFS 驱动程序文档

    图像的基础层是一个 ZFS 文件系统。每个子层都是基于下面一层的 ZFS 快照的 ZFS 克隆。容器是基于其创建时所使用的镜像的顶层的 ZFS 快照的 ZFS 克隆。

调查

您可以通过运行以下命令来检查一个镜像使用的数据集:

 $ docker image inspect [IMAGE_NAME]

示例输出:
...
"RootFS": {
    "Type": "layers",
    "Layers": [
        "sha256:f2cb0ecef392f2a630fa1205b874ab2e2aedf96de04d0b8838e4e728e28142da",
        ...
        ...
        ...
        "sha256:2e8cc9f5313f9555a4decca744655ed461e21fbe48a0f078ed5f7c4e5292ad2e",
    ]
},
...

这就解释了为什么在运行十几个容器时可以看到创建了150多个数据集。

解决方案

  1. Prune and delete unused images.

    $ docker image prune -a
    
  2. To avoid a slow zfs list, specify the dataset of interest.
    Suppose you store docker in tank/docker and other files in tank/data. List only the data datasets by the recursive option:

    # recursively list tank/data/*
    $ zfs list tank/data -r
    

我创建了一个小的Python工具,可以将ZFS数据集以树状结构列出,显示使用的空间大小,并以方框形式展示快照。这帮助我整理了那些混乱的数据。至少我可以直观地看到它们。https://github.com/vizyon-sa/zfs-tree - undefined

2
我使用docker-in-docker容器,它还会生成很多未使用的快照。
根据@Redsandro的评论,我使用了以下命令。
sudo zfs list -t snapshot -r pool1| wc -l
sudo zpool list

(sudo zfs get  mounted |grep "mounted   no" | awk '/docker\// { print $1 }' |  xargs -l sudo zfs destroy -R ) 2> /dev/null

仅删除所有快照会破坏Docker的一致性。但是,由于Docker挂载了所有使用/var/lib/docker/zfs/graph目录下的镜像(对于docker-in-docker镜像也是如此),因此忽略那些仅被挂载的内容应该只删除未正确释放的悬空镜像/卷/容器。您需要运行此命令直到快照数量减少。


0

在 docker.com 上修剪介绍

我假定您的docker版本低于V17.06。由于您执行了docker system prune -a,旧的层构建信息和卷已经丢失。而-a / --all标志表示将删除没有至少一个容器的所有镜像。如果没有-a / --all标志,则只会删除悬空镜像。

此外,我认为您对<missing>标记和悬浮图像存在误解。 <missing>并不意味着标记为缺失的图层确实缺失。它只是表示这些图层可能在其他计算机上构建。悬浮图像是无引用图像。即使名称和标记标记为<none>,该图像仍然可以被其他图像引用,这可以通过docker history image_id进行检查。

在您的情况下,这些层被标记为缺失,因为您已经删除了包括构建信息的旧版本图像。您上面说过 - 只有最新版本的图像可用 - 因此,只有最新的层未被标记为缺失。

请注意: docker system prune 是一种懒惰的方式来管理 Docker 的所有对象(镜像/容器/卷/网络/缓存)。

1
谢谢你和我一起思考。然而,system prune 意味着 volume prune,所以我已经这样做了。为了确保,我进行了 volume prune。它返回了“总回收空间:0B”。 - Redsandro
@Redsandro 哦,是的,到目前为止似乎没有针对 system prune 的撤销操作。下次尝试使用 volume prune - Light.G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接