# bind mounts

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

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

![](https://3635233293-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MVjOb1Mk_y7lcQcI_LJ%2F-MWZDb4WvW1CbpmQNzrb%2F-MWZDl0hnp9ffXHGVoqg%2F%E6%88%AA%E5%B1%8F2021-03-24%20%E4%B8%8B%E5%8D%888.34.53.png?alt=media\&token=48d91f18-784d-4c68-8ea6-28a312e52894)

### 选择-v还是--mount

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

{% hint style="info" %}
新用户应该使用--mount语法，有经验的用户可能更熟悉-v或--volume语法，但是依然鼓励使用--mount，因为它更容易被使用。
{% endhint %}

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

* 在使用bind mounts时，第一个字段是主机上面的文件或目录路径。
* 第二个字段指定容器中被挂载的文件或路径。
* 第三个字段是可选的，是一个逗号分隔的选项列表，例如`ro, z, Z`。

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

* 挂载的类型`(type)`可以是`bind`，`volume`或者`tmpfs`。
* 挂载的源`(source)`。对于命名卷，它是卷的名称。对于bind mounts，这是Docker主机上的文件或目录路径。可以通过`source`或`src`两种形式来指定源。
* 挂载的目的地`(destination)`使用容器中被挂载的文件或目录的路径作为值。可以通过`destination`，`dst`或`target`三种形式来指定目的地。
* `readonly`选项如果存在，它会将`bind mountbind`以只读的形式挂载到容器中。
* bind-propagation选项，如果存在，它会修改bind propagation。可能是rprivate，private，rshared，shared，rslave，slave中的一个。
* \--mount不支持使用z或Z选项来修改selinux标签。

### &#x20;--v和--mount的区别

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

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

### 通过bind mount启动一个容器

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

{% tabs %}
{% tab title="--mount" %}

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

{% endtab %}

{% tab title="-v" %}

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

{% endtab %}
{% endtabs %}

使用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/中的内容。大部分情况下，这将导致一个非功能化的容器。

{% tabs %}
{% tab title="--mount" %}

```
$ 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".
```

{% endtab %}

{% tab title="-v" %}

```
$ 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".
```

{% endtab %}
{% endtabs %}

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

```
$ docker container rm broken-container
```

### 使用只读bind mount

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

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

{% tabs %}
{% tab title="--mount" %}

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

{% endtab %}

{% tab title="-v" %}

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

{% endtab %}
{% endtabs %}

使用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
```
