当我同时使用主机卷(绑定装载)和命名卷(docker托管卷的一种类型)时会发生什么?

当我同时使用主机卷(绑定装载)和命名卷(docker托管卷的一种类型)时会发生什么?,docker,docker-volume,mount-point,Docker,Docker Volume,Mount Point,在提出这个问题之前,我已经阅读了一些文件: 我对以下两个部分感到困惑: 绑定安装: 命名卷: 他们的行为完全相反: 对于绑定安装: 如果将挂载绑定到容器上的非空目录中,则绑定挂载会掩盖目录的现有内容 对于命名卷和匿名卷: 如果您启动一个创建新卷的容器(如上所述),并且该容器在要装入的目录(如/app/over)中有文件或目录,则该目录的内容将复制到该卷中。然后容器装载并使用该卷,使用该卷的其他容器也可以访问预填充的内容 我的问题是,如果我在dockerfile中使用VOLUME命令创建命名卷匿名

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

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

绑定安装: 命名卷: 他们的行为完全相反:

对于绑定安装:

如果将挂载绑定到容器上的非空目录中,则绑定挂载会掩盖目录的现有内容

对于命名卷和匿名卷:

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

我的问题是,如果我在dockerfile中使用VOLUME命令创建命名卷匿名卷,同时使用bind mount将不存在的路径装载到容器中,那么幕后会发生什么

例如:

# in docker file
VOLUME /path/in/container

# when run container
docker run -v /not-exist-dir/in/host:/path/in/container  ... /< image >
这将实际在主机上创建一个匿名卷,并将其装载到容器中gerrit\u站点的/var/gerrit/review\u sitevalue,当根据此映像创建任何容器时,如下所示:

docker run -dit --name gerrit  openfrontier/gerrit:2.15.3
之后,我可以在/var/lib/docker/volumes/下看到匿名卷,我可以使用docker volume ls查看其名称4538dbf3a51da463391c6eca9714fb6dd0c11379f1e2918f74c33d56633f00,还可以使用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
如果主机上不存在/home/test,则不会创建匿名卷,而是创建/home/test文件夹,并且该文件夹不是空的!!!,装载信息如下所示:

"Mounts": [
            {
                "Type": "bind",
                "Source": "/home/test",
                "Destination": "/var/gerrit/review_site",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            }
        ],
我认为绑定挂载在这里生效,但我不明白为什么主机上的路径不是空的。绑定挂载到容器上的非空目录中,该目录的现有内容被绑定挂载所掩盖

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


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

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

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

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


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

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

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


在你的问题中有一些混淆的东西,因为它并不总是直观的,所以我将尝试通过示例使其更清楚

注:

这是我为同事们写的一首小短裙。解释中肯定有一些缺陷,但所有的例子都经过了测试。 重要的是,您要按照以下顺序运行这些东西,不要重新运行命令,因为新的东西可能会存在,并且不符合它们的用途 我将挂载到某个系统路径。。。这只是一个示例目的:确保它们存在,同时避免重复初始创建命令。 Dockerfile

让我们删除所有以前的测试数据

请注意,它将删除所有不再使用的卷,包括您以前创建的卷

如果您有任何正在运行的容器,尤其是使用卷的容器,请出于这些测试的目的停止它们

root@host:~# docker container prune 
root@host:~# docker volume prune 
root@host:~# rm -r /does/not/exit/within/host
root@host:~# rm -r /does/not/exit/within/host2
root@host:~# rm -r /tmp/dockerfilevolumefromnowhere
一些图像和体积的创作和填充

此时,您有3个现有卷:

root@host:~# docker volume ls
DRIVER              VOLUME NAME
local               existingVolume
local               existingVolume2
local               existingEmptyVolume
现在让我们运行我们的容器

现在,我们已连接到新创建的容器上

让我们看看容器和主机上都有什么:

那么这些匿名容器是什么:它们实际上是从dockerfile创建的。 让我们检查一下他们的内容

root@host:~#  ll /var/lib/docker/volumes/63eeedcb1aa2e4d8785cca409698371381558348ce19bc614d87da372901d224/_data/
-rw-r--r-- 1 root root    0 Feb 19 10:14 emptyFile.txt
root@host:~#  ll /var/lib/docker/volumes/ea0ed5ff271cba03a8b7d35144b58e8da1b2e50b4e05c4cccda7f19b401d7f0b/_data/
..
看,他们拿着你从dockerfile放进去的东西。 它们是匿名的,因此您不能或实际上不能从其他容器中使用它们,因为每次从该映像运行容器时都会创建新的容器

注意:此时您可能已经注意到一些奇怪的事情:

root@host:~# ll /tmp/dockerfilevolumefromnowhere #YES : from HOST!
.. #it exists... but is not linked to the volume or the path in container in any way.
这里。。。不要问:为什么它是在主机上创建的,我仍然感到困惑。我猜这是一个bug,但这是一个应该在docker ML上讨论的问题,而不是在这里。 无论如何,您不应该这样做:在上面创建卷之前使用mkdir

我们可以从这些例子中看到:

从主机路径绑定/装载到容器/some/path/on/host:/a/path/on/container将在容器上创建路径,无论它以前是否存在,并在此处替换所有以前的数据。两个目录及其内容现在都相互绑定。 从卷someVolumeName:/a/path/on/container装载到容器时,会将卷绑定到容器上的路径。 将现有非空卷装载到容器路径将使用现有卷中的内容替换容器中可能的内容(如果为空):它将充当新卷 将新卷装载到容器的路径将绑定容器中路径的内容和卷。该卷现在将保存此内容(如供以后使用):此处不会删除/替换任何内容。 如果卷或路径不存在,则将创建卷或路径(无论其位于容器或主机上)。 Dockerfile中的卷将是匿名的,并且每次从该映像运行另一个容器时都会重新创建以前的卷。它们也将在/var/lib/docker/volumes下创建。 将创建在运行命令时声明的卷,即如果以前不存在新卷,则它们将是新卷。否则,将使用现有的。 通过run命令创建的卷将被持久保存在/var/lib/docker/volumes中,并位于与其名称匹配的目录下。
注意:这里我不是在谈论所有权和权利。有意:这是我们稍后可能讨论的另一个问题。

您的问题中有一些混淆的东西,因为它并不总是直观的,我将尝试通过示例使其更清楚

注:

这是我为同事们写的一首小短裙。解释中肯定有一些缺陷,但所有的例子都经过了测试。 重要的是,您要按照以下顺序运行这些东西,不要重新运行命令,因为新的东西可能会存在,并且不符合它们的用途 我将挂载到某个系统路径。。。这只是一个示例目的:确保它们存在,同时避免重复初始创建命令。 Dockerfile

让我们删除所有以前的测试数据

请注意,它将删除所有不再使用的卷,包括您以前创建的卷

如果您有任何正在运行的容器,尤其是使用卷的容器,请出于这些测试的目的停止它们

root@host:~# docker container prune 
root@host:~# docker volume prune 
root@host:~# rm -r /does/not/exit/within/host
root@host:~# rm -r /does/not/exit/within/host2
root@host:~# rm -r /tmp/dockerfilevolumefromnowhere
一些图像和体积的创作和填充

此时,您有3个现有卷:

root@host:~# docker volume ls
DRIVER              VOLUME NAME
local               existingVolume
local               existingVolume2
local               existingEmptyVolume
现在让我们运行我们的容器

现在,我们已连接到新创建的容器上

让我们看看容器和主机上都有什么:

那么这些匿名容器是什么:它们实际上是从dockerfile创建的。 让我们检查一下他们的内容

root@host:~#  ll /var/lib/docker/volumes/63eeedcb1aa2e4d8785cca409698371381558348ce19bc614d87da372901d224/_data/
-rw-r--r-- 1 root root    0 Feb 19 10:14 emptyFile.txt
root@host:~#  ll /var/lib/docker/volumes/ea0ed5ff271cba03a8b7d35144b58e8da1b2e50b4e05c4cccda7f19b401d7f0b/_data/
..
看,他们拿着你从dockerfile放进去的东西。 它们是匿名的,因此您不能或实际上不能从其他容器中使用它们,因为每次从该映像运行容器时都会创建新的容器

注意:此时您可能已经注意到一些奇怪的事情:

root@host:~# ll /tmp/dockerfilevolumefromnowhere #YES : from HOST!
.. #it exists... but is not linked to the volume or the path in container in any way.
这里。。。不要问:为什么它是在主机上创建的,我仍然感到困惑。我想这是一个bug,但这是一个应该解决的问题 骂docker ML,不是这里。 无论如何,您不应该这样做:在上面创建卷之前使用mkdir

我们可以从这些例子中看到:

从主机路径绑定/装载到容器/some/path/on/host:/a/path/on/container将在容器上创建路径,无论它以前是否存在,并在此处替换所有以前的数据。两个目录及其内容现在都相互绑定。 从卷someVolumeName:/a/path/on/container装载到容器时,会将卷绑定到容器上的路径。 将现有非空卷装载到容器路径将使用现有卷中的内容替换容器中可能的内容(如果为空):它将充当新卷 将新卷装载到容器的路径将绑定容器中路径的内容和卷。该卷现在将保存此内容(如供以后使用):此处不会删除/替换任何内容。 如果卷或路径不存在,则将创建卷或路径(无论其位于容器或主机上)。 Dockerfile中的卷将是匿名的,并且每次从该映像运行另一个容器时都会重新创建以前的卷。它们也将在/var/lib/docker/volumes下创建。 将创建在运行命令时声明的卷,即如果以前不存在新卷,则它们将是新卷。否则,将使用现有的。 通过run命令创建的卷将被持久保存在/var/lib/docker/volumes中,并位于与其名称匹配的目录下。

注意:这里我不是在谈论所有权和权利。故意:这是我们稍后可能讨论的另一个问题。

请注意,我强烈建议不要使用Dockerfile中定义的卷。他们以非直觉的方式破坏事物。例如,在定义卷后,任何使用RUN命令更改目录的尝试都将以静默方式失败。最后,您可以为临时容器创建大量匿名卷,而这些临时容器不需要保存数据。在运行时需要时定义卷是我的首选。@Max wordpress映像中的入口点可以做到这一点,docker不会初始化主机文件夹本身。@d如果使用-rm,请尝试运行容器:docker run-name redis test-rm-d redis:latest&&docker inspect redis test-format,匿名卷将自动删除{{json.Config.Volumes}}@Dzenly要获得匿名卷id,请查看Mounts:docker inspect redis test-format{{json.Mounts}' | jq@Dzenly您可以使用docker容器检查来消除歧义请注意,我强烈建议您不要使用docker文件中定义的卷。它们以非直观的方式破坏内容。例如,在定义卷后,任何尝试使用RUN命令更改目录的操作都将以静默方式失败。并且您可能最终会遇到大量临时的匿名卷您不需要保存数据的多个容器。在运行时需要时定义卷是我的首选。@wordpress映像中的最大入口点可以做到这一点,docker不会初始化主机文件夹本身。@Dzenly匿名卷在您使用-rm时会自动删除,请尝试运行容器:docker run-name redis test-rm-d redis:latest&&docker inspect redis test-format'{{json.Config.Volumes}}}@Dzenly要获取匿名卷id,请查看装载:docker inspect redis test-format'{json.Mounts}' | jq@Dzenly您可以使用docker容器检查来消除歧义,非常感谢您的友好帮助!我为这个问题添加了一个具体的示例以使其更清楚。我知道同时使用绑定装载和docker管理的卷是不好的,但我只想知道我这样做时实际发生了什么。BMitch给出了正确的答案:在定义卷后,任何使用RUN命令更改目录的尝试都将以静默方式失败Marvin,我询问了有关RUN的相关问题,并获得了指向doc的指针:从Dockerfile中更改卷:如果任何构建步骤在声明卷后更改卷内的数据,则这些更改将被放弃。但仅此而已应用于构建阶段。为什么docker在OP问题中使用小写字母有同样的效果-你能给我指一些文档吗?Thanx。@ChiuCheng,虽然我只是要求澄清,但我会尝试猜测至少你在上面的评论中的问题的答案-在我看来,在你引用的初始化文档中,内容最初是模糊的:目录的现有内容被绑定装载所掩盖。后来创建了第一个示例:它是在容器运行后由容器创建的。非常感谢您的帮助!我为这个问题添加了一个具体的示例以使其更清楚。我知道在t同时,我只想知道当我这么做的时候到底发生了什么。BMitch给出了正确的答案:在定义卷之后,任何试图用RUN命令更改目录的行为都会自动失败Marvin,我问了一个相关的问题
bout RUN and Get指向doc的指针:从Dockerfile中更改卷:如果任何构建步骤在声明卷后更改了卷中的数据,则这些更改将被放弃。但这只适用于建造阶段。为什么docker在OP问题中使用的小写字母也有同样的效果-你能给我指一些文档吗?Thanx.@ChiuCheng,虽然我只是想澄清一下,但我会尽量猜出你在上面评论中的问题的答案-在我看来,在你引用的初始化文档中,内容最初是模糊的:目录的现有内容被绑定挂载模糊了。并且是在以后创建的,您编写了第一个示例:它是在容器运行后由容器创建的。
root@host:~#  ll /var/lib/docker/volumes/63eeedcb1aa2e4d8785cca409698371381558348ce19bc614d87da372901d224/_data/
-rw-r--r-- 1 root root    0 Feb 19 10:14 emptyFile.txt
root@host:~#  ll /var/lib/docker/volumes/ea0ed5ff271cba03a8b7d35144b58e8da1b2e50b4e05c4cccda7f19b401d7f0b/_data/
..
root@host:~# ll /tmp/dockerfilevolumefromnowhere #YES : from HOST!
.. #it exists... but is not linked to the volume or the path in container in any way.