绑定挂载

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

何时使用 bind 挂载

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

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

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

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

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

在现有数据上绑定挂载

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

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

注意事项和约束

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

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

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

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

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

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

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

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

语法

要创建绑定挂载,可以使用 or 标志。--mount--volume

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

一般来说,是首选。主要区别在于该标志更明确并支持所有可用选项。--mount--mount

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

--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 的选项

该标志由多个键值对组成,用逗号分隔 每个 Tuples 都由一个 Tuples 组成。键的顺序不是 重要。--mount<key>=<value>

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

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

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

--volume 的选项

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

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

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

第三个字段是可选的,是以逗号分隔的选项列表。有效 使用 BIND 挂载的选项包括:--volume

选择描述
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 code 中,工件将保存到另一个目录 . 您希望工件可供 的容器使用 ,并且您 希望容器在每次构建源代码时都能访问新的构建 在您的开发主机上。使用以下命令将目录绑定挂载到位于 的容器中。从目录中运行命令。该子命令将扩展为当前正在操作的 目录。 如果您使用的是 Windows,另请参阅 Windows 上的路径转换sourcesource/target//app/target//app/source$(pwd)

以下和示例产生相同的结果。你不能 除非在运行第一个 一。--mount-vdevtest


$ 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 devtestMounts

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

这表明挂载是一个挂载,它显示了正确的源和 destination 的 URL 中,它显示挂载是读写的,并且传播是 设置为 。bindrprivate

停止并移除容器:

$ docker container rm -fv devtest

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

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

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

和 examples 具有相同的最终结果。--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

和 examples 具有相同的结果。--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 devtestMounts

"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--mount-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 或更高版本。

配置绑定传播

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

绑定传播是指是否在给定的 bind-mount 可以传播到该挂载的副本。考虑 挂载点 ,它也挂载在 上。传播设置 控制 上的挂载是否也可以在 上使用 。每 propagation 设置具有递归对位点。在递归的情况下, 请考虑 也挂载为 。传播设置 控制 AND/OR 是否存在。/mnt/tmp/tmp/a/mnt/a/tmp/a/foo/mnt/a/tmp/a

注意

挂载传播不适用于 Docker Desktop。

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

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

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

以下示例将目录挂载到容器中两次, 第二个挂载同时设置 option 和 bind propagation 选择。target/rorslave

和 examples 具有相同的结果。--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,则可以添加 or 选项来修改 SELinux label 中要挂载到容器中的主机文件或目录。这 影响主机本身的文件或目录,并且可以具有 超出 Docker 范围的后果。zZ

  • 该选项表示绑定挂载内容在多个 器皿。z
  • 该选项指示绑定挂载内容是私有且未共享的。Z

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

重要

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

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

不能使用该标志修改 SELinux 标签。--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:

有关将此类卷与 Compose 结合使用的更多信息,请参阅卷上的 Compose 参考。 和 Compose 参考卷配置bind

后续步骤