首页 > 解决方案 > 当我同时使用主机卷(绑定挂载)和命名卷(一种 docker 管理卷)时会发生什么?

问题描述

在问这个问题之前,我已经阅读了一些文件:

我对以下两个部分感到困惑:

  1. 绑定挂载:挂载到容器上的非空目录
  2. 命名卷:使用容器填充卷

他们的行为完全相反:

对于绑定安装:

如果您绑定挂载到容器上的非空目录,则该目录的现有内容会被绑定挂载遮盖。

对于命名卷和匿名卷:

如果你启动一个容器来创建一个新的卷,如上,并且容器在要挂载的目录中有文件或目录(如上面的 /app/),则目录的内容将被复制到卷中。然后容器安装并使用该卷,并且使用该卷的其他容器也可以访问预填充的内容。

我的问题是,如果我VOLUME在 dockerfile 中使用命令来创建一个命名卷匿名卷,同时使用绑定挂载将不存在的路径挂载到容器中,幕后发生了什么?

例如:

# in docker file
VOLUME /path/in/container

# when run container
docker run -v /not-exist-dir/in/host:/path/in/container  ... /< image >

我做了一些测试,结果是:

  1. 没有创建匿名卷/var/lib/docker/volumes/
  2. not-exist-dir 是在主机上创建的,现在它不是空的,我认为Populate a volume using a container已经生效。

所以,

  1. 为什么没有按预期创建匿名卷?
  2. 为什么目录的现有内容(在容器端)不会被绑定挂载遮盖?

这种情况背后发生了什么?


让我举一个更具体的例子:

这里是openfrontier/gerrit 镜像的 dockerfile,我们可以看到在 docker 文件的末尾,有一个 VOLUME 命令:

VOLUME $GERRIT_SITE

这实际上将在主机上创建一个匿名卷并将其挂载到/var/gerrit/review_site容器中的(GERRIT_SITE 的值),当任何容器从这个图像创建时,如下所示:

docker run -dit --name gerrit  openfrontier/gerrit:2.15.3

之后,我可以看到下面的匿名卷/var/lib/docker/volumes/,我可以用docker volume ls它来查看它的名称be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00,也可以使用docker inspect gerrit命令,我们可以看到:

"Mounts": [
            {
                "Type": "volume",
                "Name": "be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00",
                "Source": "/var/lib/docker/volumes/be4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00/_data",
                "Destination": "/var/gerrit/review_site",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],

这两个文件夹下还有一些文件,是容器运行后创建的。

到目前为止,这是一个使用 VOLUME 命令创建匿名卷的正常示例。

但是,如果我使用以下命令运行此容器:

docker run -dit --name gerrit -v /home/test:/var/gerrit/review_site openfrontier/gerrit:2.15.3

where/home/test主机上不存在,则不创建匿名卷,而是创建/home/test文件夹且不为空!!!,挂载信息如下:

"Mounts": [
            {
                "Type": "bind",
                "Source": "/home/test",
                "Destination": "/var/gerrit/review_site",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],

我认为绑定挂载在这里生效,但我只是不明白为什么主机上的路径不为空(因为绑定挂载到容器上的非空目录中,目录的现有内容被绑定挂载遮住了。)

在我使用启动容器之前,我还通过在“/home/test”下放置一些文件来测试将一个非空文件夹安装到docker run容器,结果是保留了原始文件并添加了新文件。

我知道上面的例子不是使用 docker volume 的好习惯,但我只是想知道后面发生了什么。

标签: dockerdocker-volumemount-point

解决方案


Dockerfile 中的卷声明在映像上设置了一些元数据,告诉docker run在任何时候从该映像创建容器时在该位置定义一个匿名卷。这不是命名卷或主机挂载,但它与两者有很多共同点(默认情况下它们都是绑定挂载,并且匿名卷docker volume ls与命名卷一起列出)。

但是,当您指定主机挂载到同一容器目录时,优先(不会发生两个卷挂载到容器内的同一目录)。容器内运行的命令对该目录的任何修改都将在主机上可见,但目录本身不会由图像内容初始化。

如果要使用映像内容初始化主机目录,可以使用执行绑定挂载的命名卷。与主机挂载的行为有一些不同。主要是目录必须预先存在,并且在撰写文件中您必须使用绝对路径而不是相对路径。我在这里的演示文稿中有语法:

https://sudo-bmitch.github.io/presentations/dc2018eu/tips-and-tricks-of-the-captains.html#48


推荐阅读