绑定挂载

使用绑定挂载时,将挂载主机上的文件或目录 从主机到容器中。相比之下,当您使用卷时,新的 目录在主机上的 Docker 存储目录中创建,并且 Docker 管理该目录的内容。

何时使用 bind 挂载

绑定挂载适用于以下类型的用例:

  • 在开发环境之间共享源代码或构建工件 Docker 主机和容器。

  • 当您想要在容器中创建或生成文件并持久化 文件复制到主机的文件系统上。

  • 将配置文件从主机共享到容器。就是这样 默认情况下,Docker 通过挂载/etc/resolv.conf从主机到每个容器。

绑定挂载也可用于构建:您可以从 将主机放入 Build 容器中,以测试、lint 或编译项目。

在现有数据上绑定挂载

如果你将挂载文件或目录绑定到容器中的目录中,其中 文件或目录存在,则预先存在的文件会被挂载遮挡。 这类似于您要将文件保存到/mnt在 Linux 主机上,以及 然后将 USB 驱动器挂载到/mnt.的内容/mnt会被遮挡 通过 USB 驱动器的内容,直到 USB 驱动器被卸载。

对于容器,没有直接的方法可以移除挂载来显示 再次被遮挡的文件。最好的选择是在 坐骑。

注意事项和约束

  • 默认情况下,绑定挂载对主机上的文件具有写入访问权限。

    使用 bind 挂载的一个副作用是您可以更改主机 filesystem,包括创建、 修改或删除重要的系统文件或目录。此功能 可能会产生安全隐患。例如,它可能会影响非 Docker 主机系统上的进程。

    您可以使用readonlyro选项来防止容器 写入挂载。

  • 绑定挂载是到 Docker 守护程序主机创建的,而不是客户端。

    如果您使用的是远程 Docker 守护程序,则无法创建 绑定到 访问容器中客户端计算机上的文件。

    对于 Docker Desktop,守护程序在 Linux VM 中运行,而不是直接在 native host 的Docker Desktop 具有透明处理 bind 挂载,允许您与 在虚拟机中运行的容器。

  • 具有绑定挂载的容器与主机紧密绑定。

    绑定挂载依赖于具有特定目录的主机文件系统 结构可用。这种依赖性意味着具有 bind 挂载的容器可能会 如果在没有相同目录结构的其他主机上运行,则失败。

语法

要创建绑定挂载,您可以使用--mount--volume旗。

$ docker run --mount type=bind,src=<host-path>,dst=<container-path>
$ docker run --volume <host-path>:<container-path>

通常--mount是首选。主要区别在于--mountflag 更明确,并支持所有可用选项。

如果您使用--volume绑定挂载尚未 存在于 Docker 主机上,Docker 会自动在 Host 为你服务。它始终创建为目录。

--mount如果指定了 mount PATH 上不存在。相反,它会产生一个错误:

$ docker run --mount type=bind,src=/dev/noexist,dst=/mnt/foo alpine
docker: Error response from daemon: invalid mount config for type "bind": bind source path does not exist: /dev/noexist.

--mount 的选项

--mountflag 由多个键值对组成,以逗号分隔 每个 API 都由<key>=<value>元。键的顺序不是 重要。

$ docker run --mount type=bind,src=<host-path>,dst=<container-path>[,<key>=<value>...]

的有效选项--mount type=bind包括:

选择描述
source,src文件或目录在主机上的位置。这可以是绝对路径或相对路径。
destination,dst,target文件或目录在容器中挂载的路径。必须是绝对路径。
readonly,ro如果存在,则会导致将绑定挂载作为只读挂载到容器中
bind-propagation如果存在,则更改 bind 传播
$ docker run --mount type=bind,src=.,dst=/project,ro,bind-propagation=rshared

--volume 的选项

--volume-vflag 由三个字段组成,用冒号分隔 字符 (:).字段的顺序必须正确。

$ docker run -v <host-path>:<container-path>[:opts]

第一个字段是主机上要将 mount 绑定到容器的路径。这 second 字段是文件或目录在 容器。

第三个字段是可选的,是以逗号分隔的选项列表。有效 的选项--volumewith a bind mount 包括:

选择描述
readonly,ro如果存在,则会导致将绑定挂载作为只读挂载到容器中
z,Z配置 SELinux 标签。请参阅配置 SELinux 标签
rprivate(默认)将 bind propagation 设置为rprivate对于这个坐骑。请参阅配置绑定传播
private将 bind propagation 设置为private对于这个坐骑。请参阅配置绑定传播
rshared将 bind propagation 设置为rshared对于这个坐骑。请参阅配置绑定传播
shared将 bind propagation 设置为shared对于这个坐骑。请参阅配置绑定传播
rslave将 bind propagation 设置为rslave对于这个坐骑。请参阅配置绑定传播
slave将 bind propagation 设置为slave对于这个坐骑。请参阅配置绑定传播
$ docker run -v .:/project:ro,rshared

使用绑定挂载启动容器

请考虑您有一个目录source当您构建 source code 的 build 中,工件将保存到另一个目录source/target/. 您希望工件对容器可用/app/还有你 希望容器在每次构建源代码时都能访问新的构建 在您的开发主机上。使用以下命令绑定挂载target/目录中的/app/.从source目录。这$(pwd)子命令扩展为当前正在作的 目录。 如果您使用的是 Windows,另请参阅 Windows 上的路径转换

以下内容--mount-v示例会产生相同的结果。你不能 运行它们,除非您删除devtest容器 一。


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

docker inspect devtest验证是否已创建 Bind Mount 正确。查找Mounts部分:

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

这表明挂载是一个bindmount,则会显示正确的源,而 destination 的 URL 中,它显示挂载是读写的,并且传播是 设置为rprivate.

停止并移除容器:

$ docker container rm -fv devtest

挂载到容器上的非空目录中

如果将目录绑定挂载到容器上的非空目录中,则 目录的现有内容被 bind 挂载遮挡。这可以是 有益的,例如当您想要测试应用程序的新版本时 而无需构建新镜像。然而,它也可能令人惊讶,而且这 行为与的行为不同。

这个例子是人为的极端,但替换了 容器的/usr/目录中的/tmp/目录中。在 大多数情况下,这会导致容器无法正常工作。

--mount-v示例具有相同的最终结果。


$ 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 run -d \
  -it \
  --name broken-container \
  -v /tmp:/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

使用只读绑定挂载

对于某些开发应用程序,容器需要 写入绑定挂载,以便将更改传播回 Docker 主机。在其他时候,容器只需要读取访问权限。

此示例修改了前一个示例,但将目录挂载为只读 bind mount,通过添加ro到 (默认情况下为空) 选项列表中,在 挂载点。如果存在多个选项,则单独 他们用逗号表示。

--mount-v示例具有相同的结果。


$ docker run -d \
  -it \
  --name devtest \
  --mount type=bind,source="$(pwd)"/target,target=/app,readonly \
  nginx:latest
$ docker run -d \
  -it \
  --name devtest \
  -v "$(pwd)"/target:/app:ro \
  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 rm -fv devtest

递归挂载

当你绑定 mount 一个本身包含 mounts 的路径时,这些 submount 是 默认情况下也包含在 bind 挂载中。此行为是可配置的, 使用bind-recursive选项--mount.仅支持此选项 使用--mountflag 的 Token,而不是-v--volume.

如果绑定挂载是只读的,则 Docker 引擎会尽力而为 将 submounts 也设为只读。这称为递归 只读挂载。递归只读挂载需要 Linux 内核版本 5.12 或之后。如果你运行的是较旧的内核版本,则 submounts 是 默认情况下,自动挂载为读写。尝试将 submounts 设置为 在 5.12 之前的内核版本上为只读,使用bind-recursive=readonly选项会导致错误。

支持的bind-recursive选项包括:

价值描述
enabled(默认)如果内核为 v5.12 或更高版本,则只读挂载将递归为只读。否则,子挂载是读写的。
disabled子挂载将被忽略(不包括在 bind 挂载中)。
writableSubmounts 是读写的。
readonlySubmounts 是只读的。需要内核 v5.12 或更高版本。

配置绑定传播

Bind propagation 默认为rprivate用于 Bind 挂载和 Volume。是的 仅可配置用于绑定挂载,并且仅在 Linux 主机上配置。捆 propagation 是一个高级主题,许多用户从不需要配置它。

绑定传播是指是否在给定的 bind-mount 可以传播到该挂载的副本。考虑 挂载点/mnt,它也安装在/tmp.传播设置 控制是否在/tmp/a也将在/mnt/a.每 propagation 设置具有递归对位点。在递归的情况下, 想想看/tmp/a也挂载为/foo.传播设置 控制是否/mnt/a和/或/tmp/a会存在。

注意

挂载传播不适用于 Docker Desktop。

传播设置描述
shared原始挂载的子挂载会暴露给副本挂载,而副本挂载的子挂载也会传播到原始挂载。
slave类似于共享挂载,但仅在一个方向上。如果原始挂载公开了子挂载,则副本挂载可以看到它。但是,如果副本挂载公开了子挂载,则原始挂载无法看到它。
private挂载是私有的。其中的子挂载不会暴露给副本挂载,而副本挂载的子挂载也不会暴露给原始挂载。
rshared与 shared 相同,但传播也扩展到嵌套在任何原始或副本挂载点中的挂载点或从这些挂载点扩展。
rslave与 slave 相同,但传播也扩展到嵌套在任何原始或复制副本挂载点中的挂载点。
rprivate默认值。与 private 相同,这意味着原始挂载点或副本挂载点中的任何位置都不会沿任一方向传播。

在挂载点上设置 bind 传播之前,主机文件系统需要 已支持 Bind 传播。

有关绑定传播的更多信息,请参阅共享子树的 Linux 内核文档

以下示例将target/目录放入容器中两次, 第二个挂载将ro选项和rslave绑定传播 选择。

--mount-v示例具有相同的结果。


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

现在,如果您创建/app/foo/,/app2/foo/也存在。

配置 SELinux 标签

如果您使用 SELinux,则可以添加zZ修改 SELinux 的选项 label 中要挂载到容器中的主机文件或目录。这 影响主机本身的文件或目录,并且可以具有 超出 Docker 范围的后果。

  • z选项表示绑定挂载内容在多个 器皿。
  • Z选项表示绑定挂载内容为 private 且未共享。

使用这些选项时要格外小心。绑定挂载系统目录 如/home/usr使用Z选项渲染您的主机 无法作,并且可能需要手动重新标记主机文件。

重要

当将绑定挂载与服务一起使用时,SELinux 会标记 (:Z:z) 以及:ro被忽略。有关详细信息,请参阅 moby/moby #32579

此示例将z选项以指定多个容器可以共享 绑定挂载的内容:

不能使用--mount旗。

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

将绑定挂载与 Docker Compose 结合使用

具有绑定挂载的单个 Docker Compose 服务如下所示:

services:
  frontend:
    image: node:lts
    volumes:
      - type: bind
        source: ./static
        target: /opt/app/static
volumes:
  myapp:

有关使用bind键入,请参阅卷上的 Compose 参考。 和 Compose 参考卷配置

后续步骤