Docker入门系列 9. Docker 容器的数据管理 数据卷 绑定挂载 临时挂载


因为容器技术,重新构建并运行应用变得容易且快速,容器化应用基本是无需进一步配置的(已经打包好所有依赖并且已配置),只需PULL,然后Deploy(也许还需要一份docker-compose.yml)就好。我们无需太担心硬件,OS,库以及应用本身的潜在Bug导致应用挂掉,挂掉就挂掉吧,销毁然后重新运行一个新的就好,而且马上就可Ready。我们需要重点关心的是如何保存,备份,恢复应用产生的数据.

默认情况下,在容器内创建的所有文件都存储在可写容器层上。这意味着:

  • 当该容器不再存在时,数据将不会持久保存,并且如果另一个进程需要它,则可能很难从容器中取出数据。
  • 容器的可写层与运行容器的主机紧密耦合。您不能轻易地将数据移动到其他地方。
  • 写入容器的可写层需要 存储驱动程序来管理文件系统。存储驱动程序使用Linux内核提供联合文件系统。与使用直接写入主机文件系统的数据卷相比,这种额外的抽象降低了性能 。

Docker为容器提供了两个选项来将文件存储在主机中,以便即使容器停止后文件也可以持久存储:数据卷绑定挂载。如果您在Linux上运行Docker,则还可以使用tmpfs挂载。

1. Docker 数据挂载类型

无论您选择使用哪种类型的挂载,容器中的数据看起来都是相同的。它在容器的文件系统中显示为目录或单个文件。

区分数据卷,绑定挂载和tmpfs 挂载之间差异的一种简单方法就是考虑数据在Docker主机上实际存储的位置。

type of mounts

  • 数据卷(data volumes): 存储在主机文件系统的一部分中,该文件系统由Docker管理(/var/lib/docker/volumes/在Linux上)。非Docker进程不应修改文件系统的这一部分。卷是在Docker中持久保存数据的最佳方法。

  • 绑定挂载(bind mounts): 可以存储在主机系统上的任何位置。它们甚至可能是重要的系统文件或目录。Docker主机或Docker容器上的非Docker进程可以随时对其进行修改。

  • 临时挂载 (tmpfs mounts 仅存储在主机系统的内存中,并且永远不会写入主机系统的文件系统中。

1.1 数据卷(data volumes)

数据卷 由Docker创建和管理。您可以使用docker volume create命令显式创建卷,或者Docker可以在容器或服务创建期间创建卷。

创建卷时,它存储在Docker主机上的目录中。将卷装入容器时,此目录就是装入容器的目录。这类似于绑定安装的工作方式,除了卷由Docker管理并且与主机的核心功能隔离。

已创建的数据卷可以同时安装到多个容器中。当没有正在运行的容器使用卷时,该卷仍可用于Docker,并且不会自动删除。您可以使用删除未使用的卷docker volume prune

挂载卷时,它可以命名匿名的。匿名卷首次安装到容器中时,不会为其指定明确的名称,因此Docker为它们提供一个随机名称,该名称在给定的Docker主机中保证是唯一的。除了名称之外,命名卷和匿名卷的行为也相同。

卷还支持使用卷驱动程序,该驱动程序使您可以将数据存储在远程主机或云提供商上,以及其他可能性。

1.1.1 数据卷 应用场景

  • 数据卷是将数据持久保存在Docker容器和服务中的首选方法。卷的一些用例包括:

  • 在多个运行中的容器之间共享数据。如果未显式创建卷,则在首次将卷安装到容器中时将创建该卷。当该容器停止或卸下时,该卷仍然存在。多个容器可以同时装载相同的卷(可读写或只读)。仅在显式删除卷时才将它们删除。

  • 不保证Docker主机具有给定的目录或文件结构时。卷可帮助您将Docker主机的配置与容器运行时解耦。

  • 当您要将容器的数据存储在远程主机或云提供商上时,而不是在本地。

  • 当您需要将数据从一个Docker主机备份,还原或迁移到另一个Docker主机时,卷是一个更好的选择。您可以停止使用该卷的容器,然后备份该卷的目录(例如/var/lib/docker/volumes/)。

1.2 绑定挂载(bind mounts)

绑定挂载 自Docker早期以来可用。与卷相比,绑定安装的功能有限。使用绑定安装时,主机上的文件或目录将安装到容器中。文件或目录由主机上的完整路径引用。该文件或目录不需要在Docker主机上已经存在。如果尚不存在,则按需创建。绑定挂载性能非常好,但是它们依赖于具有特定目录结构的主机文件系统。如果要开发新的Docker应用程序,请考虑使用命名卷。您不能使用Docker CLI命令直接管理绑定安装。

绑定安装允许访问敏感文件

使用绑定挂载的一个副作用是,您可以通过 容器中 运行的进程来更改 主机 文件系统 ,包括创建,修改或删除重要的系统文件或目录。这是一项强大的功能,可能会带来安全隐患,包括影响主机系统上的非Docker进程。

1.2.1 绑定挂载 应用场景

通常情况下,应尽可能使用数据卷。绑定挂载适用于以下类型的应用场景:

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

  • 在Docker主机上的开发环境和容器之间共享源代码或构建工件。例如,您可以将Maven target/ 目录安装到容器中,并且每次在Docker主机上构建Maven项目时,容器都可以访问重建的工件。

    如果您以这种方式使用Docker进行开发,那么您的生产Dockerfile会将生产就绪的工件直接复制到映像中,而不是依赖于绑定安装。

  • 当确保Docker主机的文件或目录结构与容器所需的绑定挂载一致时。

1.3 临时挂载(tmpfs mounts

临时挂载 不会持久化在磁盘上,无论是在Docker主机上还是在容器内。容器在其生存期内可以使用它来存储非持久状态或敏感信息。例如,在内部,群集服务使用tmpfs安装将机密安装到服务的容器中。

1.3.1 临时挂载 应用场景

当您不希望数据在主机上或容器内持久存在时,最好使用mount。当您的应用程序需要写入大量非持久状态数据时,这可能是出于安全原因或为了保护容器的性能。


绑定挂载和数据卷都可以使用-v--volume标志挂载到容器中,但是两者的语法略有不同。对于tmpfs 挂载,您可以使用该--tmpfs标志。但是,在Docker 17.06及更高版本中,建议将--mount标志用于容器和服务,用于绑定挂载数据卷tmpfs挂载,因为语法更清晰。

如果使用绑定挂载或数据卷,请牢记以下几点:

  • 如果将空卷装载到存在文件或目录的容器中的目录中,则这些文件或目录将传播(复制)到该卷中。同样,如果启动一个容器并指定一个尚不存在的卷,则会为您创建一个空卷。这是预填充另一个容器所需数据的好方法。

  • 如果将绑定挂载或非空卷挂载到存在某些文件或目录的容器中的目录中,则这些文件或目录会被挂载遮盖,就像您将文件保存到Linux主机上的/mnt目,然后再挂载USB驱动器到/mnt。/mnt的内容将由USB驱动器遮盖,直至USB驱动器的内容被卸载。被遮盖的文件不会被删除或更改,但是在挂载绑定挂载或数据卷时将无法访问。

2. 使用数据卷

数据卷是用于持久化由Docker容器生成和使用的数据的首选机制。尽管绑定挂载取决于主机的目录结构,但是卷完全由Docker管理。与绑定挂载相比,卷具有几个优点:

  • 与绑定挂载相比,卷更易于备份或迁移。
  • 您可以使用Docker CLI命令或Docker API管理卷。
  • 卷在Linux和Windows容器上均可工作。
  • 可以在多个容器之间更安全地共享卷。
  • 卷驱动程序使您可以将卷存储在远程主机或云提供商上,以加密卷内容或添加其他功能。
  • 新卷的内容可以由容器预先填充。

此外,与将数据持久保存在容器的可写层中相比,卷通常是更好的选择,因为卷不会增加使用卷的容器的大小,并且卷的内容存在于给定容器的生命周期之外。

type of mounts volume

如果您的容器生成非持久状态数据,请考虑使用 tmpfs挂载以避免将数据永久存储在任何地方,并通过避免写入容器的可写层来提高容器的性能。

卷使用rprivate绑定传播,并且绑定传播无法为卷配置。

2.1 选择 -v 或 –mount 标志

最初,-vor --volume标志用于独立容器,而--mount标志用于集群服务。但是,从Docker 17.06开始,您还可以使用--mount独立容器。总的来说, --mount是更明确和冗长的。最大的区别是该-v 语法将所有选项合并在一个字段中,而--mount 语法将它们分开。这是每个标志的语法比较。

新用户应该尝试--mount--volume语法更简单的语法。

如果需要指定数据卷驱动程序选项,则必须使用--mount

  • -v--volume:由三个字段组成,以冒号(:)分隔。这些字段必须以正确的顺序排列,并且每个字段的含义不是立即显而易见的。

    • 对于命名卷,第一个字段是卷的名称,在给定的主机上是唯一的。对于匿名卷,将省略第一个字段。
    • 第二个字段是文件或目录在容器中的挂载路径。
    • 第三个字段是可选的,并且是逗号分隔的选项列表,例如ro。这些选项将在下面讨论。
  • --mount:包含多个键值对,以逗号分隔,每个键值对都由一个<key>=<value>元组组成。该--mount语法比-v--volume更详细,但关键字的顺序并不重要,并且键值对的值更容易理解。

    • 该type挂载,其可以是bind,volume,或 tmpfs。本主题讨论卷,因此类型始终为 volume。
    • 该source的挂载。对于命名卷,这是卷的名称。对于匿名卷,将省略此字段。可以指定为source 或src。
    • 该destination作为其值,其中的文件或目录被挂载在容器的路径。可以指定为destination,dst或target。
    • 该readonly选项(如果存在)会使绑定挂载以只读方式安装到容器中。
    • 该volume-opt选项 可以多次指定的选项,采用键值对,该键值对由选项名称及其值组成。

从外部CSV解析器转义值

如果您的卷驱动程序接受逗号分隔的列表作为选项,则必须从外部CSV解析器中转义该值。要对volume-opt进行转义,请用双引号(")包围,并用单引号(')包围整个mount参数。

例如,local驱动程序接受挂载选项作为o参数中逗号分隔的列表。此示例显示了转义列表的正确方法。

$ docker service create \
     --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'
    --name myservice \
    <IMAGE>

下面的示例在可能的情况下同时显示--mount-v语法,并 --mount首先展示。

-v--mount之间的差异

与绑定装载相反,所有数据卷选项都可用于 –mount和-v标志。

将数据卷与服务一起使用时,仅–mount支持。

2.2 创建和管理卷

与绑定装载不同,您可以在任何容器范围之外创建和管理卷。

2.2.1 创建一个卷:

docker volume create my-vol

运行结果如下

➜ docker volume create my-vol
my-vol

2.2.2 查询卷:

docker volume ls

运行结果如下

➜ docker volume ls
DRIVER              VOLUME NAME
local               my-vol

2.2.3 检查卷:

docker volume inspect my-vol

运行结果如下

➜ docker volume inspect my-vol
[
    {
        "CreatedAt": "2020-05-15T14:31:20Z",
        "Driver": "local",
        "Labels": {},
        "Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
        "Name": "my-vol",
        "Options": {},
        "Scope": "local"
    }
]

2.2.4 删除卷:

docker volume rm my-vol

运行结果如下

➜ docker volume rm my-vol
my-vol

2.3 启动有数据卷的容器

如果您使用尚不存在的卷启动容器,则Docker将为您创建该卷。以下示例将卷myvol2装入 /app/容器中。

下面的-v和–mount示例产生相同的结果。您不能同时运行它们,除非在运行第一个devtest容器和myvol2卷之后将其删除。

--mount

docker run -d \
  --name devtest \
  --mount source=myvol2,target=/app \
  nginx:latest

-v

docker run -d \
  --name devtest \
  -v myvol2:/app \
  nginx:latest

使用docker inspect devtest验证创建卷并安装正确。查找Mounts部分:

"Mounts": [
            {
                "Type": "volume",
                "Name": "myvol2",
                "Source": "/var/lib/docker/volumes/myvol2/_data",
                "Destination": "/app",
                "Driver": "local",
                "Mode": "z",
                "RW": true,
                "Propagation": ""
            }
        ],

这表明安装是一个卷,它显示了正确的源和目标,并且该安装是可读写的。

停止容器并移除数据卷。数据卷清除是一个单独的步骤。

$ docker container stop devtest

$ docker container rm devtest

$ docker volume rm myvol2

2.4 启动有数据卷的服务

启动服务并定义卷时,每个服务容器都使用其自己的本地卷。如果您使用local 卷驱动程序,则没有一个容器可以共享此数据,但是某些卷驱动程序确实支持共享存储。适用于AWS的Docker和适用于Azure的Docker均使用Cloudstor插件支持持久存储。

以下示例nginx以四个副本启动服务,每个副本使用一个称为的本地卷myvol2。

docker service create -d \
  --replicas=4 \
  --name devtest-service \
  --mount source=myvol2,target=/app \
  nginx:latest

使用docker service ps devtest-service验证服务正在运行:

➜ docker service ps devtest-service
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
wmvbs25isoi9        devtest-service.1   nginx:latest        docker-desktop      Running             Running 6 seconds ago
6nai1zccabj2        devtest-service.2   nginx:latest        docker-desktop      Running             Running 6 seconds ago
j1czn1oz2uk6        devtest-service.3   nginx:latest        docker-desktop      Running             Running 6 seconds ago
ullg50sx5g8p        devtest-service.4   nginx:latest        docker-desktop      Running             Running 6 seconds ago

删除该服务,这将停止其所有任务:

docker service rm devtest-service

删除服务不会删除该服务创建的任何卷。卷删除是一个单独的步骤。

服务的语法差异

该docker service create命令不支持-vor --volume标志。将卷装入服务的容器时,必须使用该--mount 标志。

2.5 使用容器填充数据卷

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

为了说明这一点,此示例启动一个nginx容器,并nginx-vol使用容器/usr/share/nginx/html目录的内容填充新卷,Nginx 在该 目录中存储其默认HTML内容。

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

--mount

docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html \
  nginx:latest

-v

docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html \
  nginx:latest

运行这些示例中的任何一个之后,请运行以下命令以清理容器和卷。数据卷清除是一个单独的步骤。

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

2.6 使用只读数据卷

对于某些开发应用程序,容器需要写入绑定安装,以便将更改传播回Docker主机。在其他时候,容器仅需要对数据的读取访问权限。请记住,多个容器可以挂载相同的卷,并且可以同时对其中一些容器进行读写安装,而对其他容器则同时进行只读安装。

此示例修改了上面的示例,但通过ro在容器中的安装点之后添加到(默认为空)选项列表中,将目录作为只读卷安装。如果存在多个选项,请用逗号分隔。

--mount-v实例有同样的结果。

--mount

docker run -d \
  --name=nginxtest \
  --mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
  nginx:latest

-v

docker run -d \
  --name=nginxtest \
  -v nginx-vol:/usr/share/nginx/html:ro \
  nginx:latest

使用docker inspect nginxtest验证只读mount正确创建。查找Mounts部分:

"Mounts": [
    {
        "Type": "volume",
        "Name": "nginx-vol",
        "Source": "/var/lib/docker/volumes/nginx-vol/_data",
        "Destination": "/usr/share/nginx/html",
        "Driver": "local",
        "Mode": "",
        "RW": false,
        "Propagation": ""
    }
],

停止并移除容器,然后移除数据卷。卷删除是一个单独的步骤。

$ docker container stop nginxtest

$ docker container rm nginxtest

$ docker volume rm nginx-vol

2.7 在机器之间共享数据

在构建容错应用程序时,您可能需要配置同一服务的多个副本才能访问相同的文件。

volumes-shared-storage

开发应用程序时,有几种方法可以实现此目的。一种是向您的应用程序添加逻辑,以将文件存储在像Amazon S3这样的云对象存储系统上。另一个方法是使用支持将文件写入外部存储系统(例如NFS或Amazon S3)的驱动程序来创建卷。

卷驱动程序使您可以从应用程序逻辑中抽象底层存储系统。例如,如果您的服务使用带有NFS驱动程序的卷,则可以更新服务以使用其他驱动程序(例如,将数据存储在云中),而无需更改应用程序逻辑。

2.8 使用数据卷驱动器

当使用创建卷时docker volume create,或者启动使用尚未创建的卷的容器时,可以指定卷驱动器。以下示例vieux/sshfs首先在创建独立卷时使用卷驱动程序,然后在启动创建新卷的容器时使用卷驱动程序。

2.8.1 初始化设置

本示例假定您有两个节点,其中第一个是Docker主机,并且可以使用SSH连接到第二个。

在Docker主机上,安装vieux/sshfs插件:

docker plugin install --grant-all-permissions vieux/sshfs

2.8.2 使用卷驱动程序创建卷

此示例指定了SSH密码,但是如果两个主机都配置了共享密钥,则可以省略该密码。每个卷驱动程序可能具有零个或多个可配置选项,每个选项都使用一个-o标志指定。

docker volume create --driver vieux/sshfs \
  -o sshcmd=test@node2:/home/test \
  -o password=testpassword \
  sshvolume

2.8.3 启动一个使用卷驱动程序创建卷的容器

此示例指定了SSH密码,但是如果两个主机都配置了共享密钥,则可以省略该密码。每个卷驱动程序可能具有零个或多个可配置选项。如果卷驱动程序要求您传递选项,则必须使用该–mount标志而不是来安装卷-v

docker run -d \
  --name sshfs-container \
  --volume-driver vieux/sshfs \
  --mount src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
  nginx:latest

2.8.4 创建一个创建NFS卷的服务

本示例说明了创建服务时如何创建NFS卷。本示例10.0.0.10用作NFS服务器和NFS服务器上/var/docker-nfs的导出目录。请注意,指定的音量驱动程序为local

NFSV3

docker service create -d \
  --name nfs-service \
  --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/var/docker-nfs,volume-opt=o=addr=10.0.0.10' \
  nginx:latest

NFSV4

docker service create -d \
    --name nfs-service \
    --mount 'type=volume,source=nfsvolume,target=/app,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/,"volume-opt=o=10.0.0.10,rw,nfsvers=4,async"' \
    nginx:latest

2.9 备份,还原或迁移数据卷

卷对于备份,还原和迁移很有用。使用该 –volumes-from标志创建一个安装该卷的新容器。

2.9.1 备份容器

例如,在下一个命令中,我们:

  • 启动一个新容器并从该dbstore容器装入卷
  • 将本地主机目录挂载为 /backup
  • 将命令将dbdata卷的内容放到目录中的backup.tar文件中/backup。
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

当命令完成并且容器停止时,我们将获得dbdata卷的备份。

2.9.2 从备份还原容器

使用刚刚创建的备份,您可以将其还原到同一容器或在其他位置创建的另一个容器。

例如,创建一个名为的新容器dbstore2:

docker run -v /dbdata --name dbstore2 ubuntu /bin/bash

然后将备份文件解压缩到新容器的数据卷中:

docker run --rm --volumes-from dbstore2 -v $(pwd):/backup ubuntu bash -c "cd /dbdata && tar xvf /backup/backup.tar --strip 1"

您可以使用首选工具使用上述技术来自动执行备份,迁移和还原测试。

2.10 删除卷

删除容器后,Docker数据卷仍然存在。有两种类型的卷需要考虑:

  • 命名卷 具有来自容器外部的特定来源,例如awesome:/bar
  • 匿名卷 没有特定来源,因此在删除容器时,请指示Docker Engine守护程序将其删除。

2.10.1 删除匿名卷

要自动删除匿名卷,请使用该--rm选项。例如,此命令创建一个匿名/foo卷。删除容器后,Docker Engine会删除该/foo卷,但不会删除该awesome卷。

docker run --rm -v /foo -v awesome:/bar busybox top

2.10.2 删除所有卷

要删除所有未使用的卷并释放空间:

docker volume prune

3. 使用绑定挂载

自Docker诞生以来,绑定挂载就已经存在。与相比,绑定安装的功能有限。使用绑定安装时,主机上的文件或目录将安装到容器中。该文件或目录由主机上的完整或相对路径引用。相比之下,当您使用卷时,将在主机上的Docker的存储目录中创建一个新目录,并且Docker管理该目录的内容。

该文件或目录不需要在Docker主机上已经存在。如果尚不存在,则按需创建。绑定挂载性能非常好,但是它们依赖于具有特定目录结构的主机文件系统。如果要开发新的Docker应用程序,请考虑使用命名卷。您不能使用Docker CLI命令直接管理绑定安装。

types-of-mounts-bind

3.1 选择-v或–mount标志

最初,-vor –volume标志用于独立容器,而–mount标志用于集群服务。但是,从Docker 17.06开始,您还可以使用–mount独立容器。总的来说, –mount是更明确和冗长的。最大的区别是该-v 语法将所有选项合并在一个字段中,而–mount 语法将它们分开。这是每个标志的语法比较。

提示:新用户应使用–mount语法。有经验的用户可能更熟悉-v或–volume语法,但建议使用–mount,因为研究表明它更易于使用。

  • -v或–volume:由三个字段组成,以冒号(:)分隔。这些字段必须以正确的顺序排列,并且每个字段的含义不是立即显而易见的。
    • 对于绑定挂载,第一个字段是主机上文件或目录的路径。
    • 第二个字段是文件或目录在容器中的安装路径。
    • 第三个字段是可选的,并且是用逗号分隔的选项,诸如列表ro,consistent,delegated,cached,z,和Z。这些选项将在下面讨论。
  • –mount:包含多个键值对,以逗号分隔,每个键值对都由一个=元组组成。该–mount语法是更详细的比-v或–volume,但按键的顺序并不显著,并且标志的价值更容易理解。
    • 该类型挂载,其可以是bind,volume,或tmpfs。本次讨论绑定挂载,因此类型始终为bind。
    • 该source的挂载。对于绑定挂载,这是Docker守护程序主机上文件或目录的路径。可以指定为source或 src。
    • 该destination作为其值,其中的文件或目录被挂载在容器的路径。可以指定为destination,dst或target。
    • 该readonly选项(如果存在)会使绑定安装以只读方式安装到容器中。
    • 该bind-propagation选项(如果存在)将更改 绑定传播。可以是一个rprivate, private,rshared,shared,rslave,slave。
    • 的consistency选项,如果存在,可以是一种consistent,delegated或cached。此设置仅适用于Mac的Docker桌面,在所有其他平台上将被忽略。
    • 该–mount标志不支持z或没有Z用于修改selinux标签的选项。

下面的示例在可能的情况下同时显示–mount和-v语法,并 –mount首先展示。

-v--mount行为之间的差异

由于-vand --volume标志已经很长时间成为Docker的一部分,因此它们的行为无法更改。这意味着和之间存在一种不同的行为。-v–mount

如果您使用-v--volume绑定安装Docker主机上尚不存在的文件或目录,请-v为您创建端点。始终将其创建为目录

如果您使用–mount绑定贴装尚不泊坞窗主机上存在的文件或目录,码头工人也不会自动为您创建它,但会产生一个错误。

3.2 使用绑定挂载启动容器

考虑以下情况:您有一个目录source,并且在构建源代码时,工件会保存到另一个目录中source/target/。您希望这些工件可用于的容器/app/,并且希望每次在开发主机上构建源代码时,容器都可以访问新的构建。使用以下命令将target/ 目录绑定安装到您的容器中,位于/app/。从source目录中运行命令 。该$(pwd)子命令将扩展到Linux或macOS主机上的当前工作目录。

下面的–mount和-v示例产生相同的结果。您不能同时运行它们,除非devtest在运行第一个容器后删除容器。

--mount

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

-v

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

使用docker inspect devtest验证绑定安装正确创建。查找Mounts部分:

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

这表明该坐骑是一个bind坐骑,它显示了正确的源和目标,表明该坐骑是可读写的,并且传播设置为rprivate。

停止容器:

$ docker container stop devtest

$ docker container rm devtest

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

如果将绑定安装到容器上的非空目录中,则该目录的现有内容将被绑定安装遮盖。这可能是有益的,例如,当您要测试应用程序的新版本而不构建新映像时。但是,这也可能令人惊讶,并且此行为不同于docker volume的行为。

该示例被认为是极端的,但是用主机上的/usr/目录替换了容器目录的内容/tmp/。在大多数情况下,这将导致容器无法正常工作。

在–mount和-v实施例具有相同的最终结果。

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

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

容器已创建但未启动。去掉它:

$ docker container rm broken-container

3.3 使用只读绑定挂载

对于某些开发应用程序,容器需要写入绑定安装,因此更改将传播回Docker主机。在其他时间,容器仅需要读取访问权限。

此示例修改了上面的示例,但ro通过在容器中的安装点之后添加到(默认为空)选项列表中,将目录作为只读绑定安装进行安装。如果存在多个选项,请用逗号分隔。

在–mount和-v实例有同样的结果。

--mount

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

-v

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

使用docker inspect devtest验证绑定安装正确创建。查找Mounts部分:

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

停止容器:

$ docker container stop devtest

$ docker container rm devtest

3.4 配置绑定传播

rprivate对于绑定安装和卷,绑定传播默认为。它仅可用于绑定安装,并且只能在Linux主机上配置。绑定传播是一个高级主题,许多用户不需要配置它。

绑定传播是指是否可以将在给定绑定安装或命名卷中创建的安装传播到该安装的副本。考虑一个安装点/mnt,它也安装在上/tmp。传播设置控制是否/tmp/a可以在上安装底座/mnt/a。每个传播设置都有一个递归对点。在递归的情况下,请考虑将/tmp/a其也安装为/foo。传播设置控制是否/mnt/a和/或/tmp/a将存在。


传播设置描述
shared原始安装的子安装暴露于副本安装,副本安装的子安装也传播到原始安装。
slave与共享安装类似,但仅在一个方向上。如果原始安装公开了子安装,则副本安装可以看到它。但是,如果副本安装公开了子安装,则原始安装看不到它。
private坐骑是私人的。其中的子安装不暴露于副本安装,副本安装的子安装也不暴露于原始安装。
rshared与共享相同,但传播也扩展到嵌套在任何原始或副本安装点中的安装点以及从这些安装点扩展。
rslave与从属服务器相同,但是传播也扩展到嵌套在任何原始或副本安装点中的安装点以及从这些安装点扩展。
rprivate默认值。与专用相同,这意味着原始或副本安装点内的任何安装点都不会向任一方向传播。


在可以在安装点上设置绑定传播之前,主机文件系统需要已经支持绑定传播。

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

以下示例将target/目录两次安装到容器中,第二次安装同时设置了ro选项和rslave绑定传播选项。

在–mount和-v实例有同样的结果。

--mount

$ 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

-v

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

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

3.5 配置selinux标签

如果使用selinux,则可以添加z或Z选项来修改要装入容器的主机文件或目录的selinux标签。这会影响主机本身上的文件或目录,并可能导致超出Docker范围的后果。

  • 该z选项指示绑定安装内容在多个容器之间共享。
  • 该Z选项指示绑定安装内容是私有的且未共享。

这些选项请格外小心。绑定安装系统目录(例如/home或/usr带有该Z选项)会使主机无法运行,并且您可能需要手动重新标记主机文件。

重要说明:将绑定安装与服务一起使用时,selinux标签(:Z:z)以及将:ro被忽略。有关详细信息,请参见 moby / moby#32579

本示例设置z选项以指定多个容器可以共享绑定安装的内容:

无法使用该–mount标志修改selinux标签。

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

3.6 配置macOS的挂载一致性

用于Mac的Docker桌面用于osxfs将从macOS共享的目录和文件传播到Linux VM。这种传播使这些目录和文件可用于在Mac上运行Docker Desktop的Docker容器。

默认情况下,这些共享是完全一致的,这意味着每次在macOS主机上或通过容器中的挂载进行写操作时,所做的更改都会刷新到磁盘上,以便共享中的所有参与者都具有完全一致的视图。在某些情况下,完全一致性可能会严重影响性能。Docker 17.05及更高版本引入了一些选项,可以针对每个容器,每个容器来调整一致性设置。提供以下选项:

  • consistentdefault:如上所述,具有完全一致性的默认设置。

  • delegated:容器运行时的挂载视图具有权威性。在主机上看到容器中所做的更新之前可能会有所延迟。

  • cached:macOS主机的挂载视图具有权威性。在容器中可以看到主机上所做的更新,这可能会有所延迟。

这些选项在除macOS之外的所有主机操作系统上均被完全忽略。

在–mount和-v实例有同样的结果。

--mount

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

-v

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

3. 使用tmpfs 挂载

数据卷和绑定挂载使您可以在主机和容器之间共享文件,以便即使容器停止后也可以保留数据。

如果您在Linux上运行Docker,则还有第三种选择:tmpfs挂载。当创建带有tmpfs安装架的容器时,该容器可以在该容器的可写层之外创建文件。

与卷和绑定挂载相反,tmpfs挂载是临时的,并且仅保留在主机内存中。当容器停止时,tmpfs挂载将被删除,并且在其中写入的文件将不会保留。

types-of-mounts-tmpfs

这对于在主机或容器可写层中临时存储您不想持久保存的敏感文件很有用。

3.1 tmpfs安装的局限性

  • 与卷和绑定安装不同,您不能tmpfs在容器之间共享安装。

  • 仅当您在Linux上运行Docker时,此功能才可用。

3.2 选择–tmpfs或–mount标志

最初,该–tmpfs标志用于独立容器,而该–mount标志用于集群服务。但是,从Docker 17.06开始,您还可以使用–mount独立容器。总的来说, –mount是更明确和冗长的。最大的区别是该 –tmpfs标志不支持任何可配置的选项。

  • –tmpfs:挂载tmpfs挂载而不允许您指定任何可配置的选项,并且只能与独立容器一起使用。

  • –mount:包含多个键值对,以逗号分隔,每个键值对都由一个=元组组成。该–mount语法比更详细–tmpfs:

    • 该type安装件,其可以是bind,volume,或 tmpfs。本主题讨论tmpfs,因此类型始终为 tmpfs。
    • 的destination作为其值,其中的路径tmpfs安装被安装在容器中。可以指定为destination,dst或target。
    • 在tmpfs-type和tmpfs-mode选项。请参阅 tmpfs选项。

下面的示例在可能的情况下同时显示–mount和–tmpfs语法,并–mount首先展示。

–tmpfs和–mount行为之间的差异

  • 该–tmpfs标志不允许您指定任何可配置的选项。
  • 该–tmpfs标志不能与群集服务一起使用。您必须使用–mount。

3.3 在容器中使用tmpfs挂载

要tmpfs在容器中使用安装座,请使用–tmpfs标志,或将 –mount标志与type=tmpfs和destination选项一起使用。没有 source了tmpfs坐骑。以下示例在Nginx容器中的上创建一个tmpfs安装 /app。第一个示例使用–mount标志,第二个示例使用–tmpfs标志。

–mount

$ docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app \
  nginx:latest

–tmpfs

$ docker run -d \
  -it \
  --name tmptest \
  --tmpfs /app \
  nginx:latest

tmpfs通过运行docker container inspect tmptest并查找以下Mounts部分来验证安装是否为安装:

"Tmpfs": {
    "/app": ""
},

卸下容器:

$ docker container stop tmptest

$ docker container rm tmptest

指定tmpfs选项

tmpfs安装允许两个配置选项,都不是必需的。如果需要指定这些选项,则必须使用该–mount标志,因为该–tmpfs标志不支持它们。

选项描述
tmpfs-sizetmpfs安装的大小(以字节为单位)。默认情况下不受限制。
tmpfs-modetmpfs的文件模式(八进制)。例如700或0770。默认为1777或世界可写。

以下示例将设置tmpfs-mode为1770,以使其在容器内不被世界范围内读取。

docker run -d \
  -it \
  --name tmptest \
  --mount type=tmpfs,destination=/app,tmpfs-mode=1770 \
  nginx:latest

文章作者: Baymax
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Baymax !
评论
 上一篇
Docker入门系列  10. Docker 镜像的制作 优化 精简 Docker入门系列 10. Docker 镜像的制作 优化 精简
1. Docker镜像制作前面几篇文章已经给大家介绍了 Docker 的基本概念,相信大家也会使用 Docker 运行自己想要的容器了。但是只有学会制作镜像,才能将 Docker 应用到我们的项目中去。 docker镜像是除了docker
2020-05-26
下一篇 
Docker入门系列  8. Docker 容器网络的概念及网络配置 macvlan Docker入门系列 8. Docker 容器网络的概念及网络配置 macvlan
1. 使用 macvlan 网络有些应用程序,特别是监视网络流量的遗留应用程序或应用程序,希望直接连接到物理网络。 在这种情况下,你可以使用 macvlan 网络驱动程序为每个容器的虚拟网络接口分配一个 MAC 地址,使其看起来像一个直接连
2020-05-25
  目录