bind mounts

从docker早期开始,bind mounts就一直存在。与卷相比,bind mounts的功能有限。当使用bind mount时,主机上的文件或目录将被挂载到容器中。主机上的文件或目录通过绝对路径被引用。相比之下,当使用卷时,在主机上的 Docker存储目录中创建了一个新的目录,Docker 管理该目录的内容 。

文件或目录不需要已经存在于Docker主机上。如果它们还不存在,则会按需创建。bind mount性能很好,但它们依赖于主机上面具有可用的特定目录结构的文件系统。如果我们正在开发新的Docker应用程序,请考虑使用命名卷。我们无法使用Docker CLI命令直接管理bind mounts。

选择-v还是--mount

通常来说,--mount更加的明确和详细。最大的不同是-v语法将所有的选项组合在一起放入一个字段,而--mount语法则是分开的。

新用户应该使用--mount语法,有经验的用户可能更熟悉-v或--volume语法,但是依然鼓励使用--mount,因为它更容易被使用。

-v--volume: 由三个字段组成,通过:分隔开。字段必须按照正确的顺序组合。

  • 在使用bind mounts时,第一个字段是主机上面的文件或目录路径。

  • 第二个字段指定容器中被挂载的文件或路径。

  • 第三个字段是可选的,是一个逗号分隔的选项列表,例如ro, z, Z

--mount:由多个键值对组成,通过逗号进行分隔,每一个都是一个键值对组合。--mount语法比-v--volume更详细,但是键的顺序没有那么重要,并且它所对应的值更容易被理解。

  • 挂载的类型(type)可以是bindvolume或者tmpfs

  • 挂载的源(source)。对于命名卷,它是卷的名称。对于bind mounts,这是Docker主机上的文件或目录路径。可以通过sourcesrc两种形式来指定源。

  • 挂载的目的地(destination)使用容器中被挂载的文件或目录的路径作为值。可以通过destinationdsttarget三种形式来指定目的地。

  • readonly选项如果存在,它会将bind mountbind以只读的形式挂载到容器中。

  • bind-propagation选项,如果存在,它会修改bind propagation。可能是rprivate,private,rshared,shared,rslave,slave中的一个。

  • --mount不支持使用z或Z选项来修改selinux标签。

--v和--mount的区别

如果使用-v或--volume bind mount一个文件或目录时,如果Docker主机上面不存在该文件或目录,则-v选项总是会为我们以目录的形式创建该文件。

如果使用--mount来bind mount一个文件或目录时,如果Docker主机上面不存在该文件或目录,则--mount不会为我们自动创建这些文件,通常会产生一个错误。

通过bind mount启动一个容器

考虑这样一个使用场景,我们有一个目录source,当我们构建源码时,我们将构建好的代码存入source下面的source/target目录。我们希望target目录下面的代码可以在docker中的/app目录下面被使用。并且希望每次构建代码时,容器都可以访问。我们使用下面的命令:

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app \
  nginx:latest

使用docker inspect devtest来检查bind mount创建是否正确,查看Mounts部分:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "",
        "RW": true,
        "Propagation": "rprivate"
    }
],

上面的信息正确显示了挂载类型为bind挂载,源和目标都是正确的。

停止容器:

$ docker container stop devtest

$ docker container rm devtest

挂载到一个容器中的非空目录

如果bind-mount挂载到了容器中的一个非空目录,目录已经存在的内容会被bind mount遮蔽。这是有好处的,例如你想测试一个新版本的应用而不构建一个新的镜像。

下面的示例使用/tmp目录中的内容替代了/usr/中的内容。大部分情况下,这将导致一个非功能化的容器。

$ docker run -d \
  -it \
  --name broken-container \
  --mount type=bind,source=/tmp,target=/usr \
  nginx:latest

docker: Error response from daemon: oci runtime error: container_linux.go:262:
starting container process caused "exec: \"nginx\": executable file not found in $PATH".

上面的命令创建了容器但是没有成功启动,删除失败的容器:

$ docker container rm broken-container

使用只读bind mount

对于一些应用的开发,容器需要写入到bind mount,但是这个更改会蔓延到docker主机上面。在其他时间,容器只需要只读权限。

下面的示例修改了上面的命令,但是以只读的形式通过bind mount挂载目录。只需要在选项的末尾添加ro选项。选项之间通过逗号分隔。

$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest

使用docker inspect devtest来检查创建的bind mount是否正确。查看Mounts部分:

"Mounts": [
    {
        "Type": "bind",
        "Source": "/tmp/source/target",
        "Destination": "/app",
        "Mode": "ro",
        "RW": false,
        "Propagation": "rprivate"
    }
],

停止容器:

$ docker container stop devtest

$ docker container rm devtest

配置selinux标签

如果想要使用selinux,可以添加z或Z选项来修改被挂载到容器中的主机文件或目录的selinux标签。这会影响主机上面的文件或目录本身并且在Docker的外部作用域依然有效。

  • z选项表示bind mount内容在多个容器中共享。

  • Z选项表示bind mount的内容是是私有和不可共享的。

使用这些选项时要格外小心。将系统目录(如/home或/usr与Z选项绑定)使主机无法操作,我们可能需要手工重新标记主机文件。

下面的示例设置了z选项来指定多个容器可以共享bind mount的内容:

无法使用--mount来修改selinux标签。

$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:z \
  nginx:latest

Last updated