卷
卷是容器的持久性数据存储,由
Docker。您可以使用该命令显式创建卷,或者 Docker 可以在容器或服务创建期间创建卷。docker volume create
当您创建卷时,它存储在 Docker 上的目录中 主机。当您将卷挂载到容器中时,此目录就是 挂载到容器中。这类似于 bind mounts 的工作方式 不同之处在于卷由 Docker 管理并与核心隔离 主机的功能。
何时使用卷
卷是保存 由 生成和已使用的数据的首选机制 通过 Docker 容器。虽然 bind 挂载依赖于 主机的目录结构和操作系统,卷完全由 Docker。对于以下使用案例,卷是一个不错的选择:
- 卷比绑定挂载更容易备份或迁移。
- 您可以使用 Docker CLI 命令或 Docker API 管理卷。
- 卷适用于 Linux 和 Windows 容器。
- 可以更安全地在多个容器之间共享卷。
- 新卷的内容可以由容器或内部版本预先填充。
- 当您的应用程序需要高性能 I/O 时。
如果您需要从主机访问文件,卷不是一个好的选择,因为 卷完全由 Docker 管理。如果您需要从容器和主机访问文件或目录,请使用 bind mounts。
卷通常是比将数据直接写入容器更好的选择。 因为卷不会增加使用它的容器的大小。使用 音量也更快;写入容器的可写层需要存储驱动程序来管理 文件系统。存储驱动程序使用 Linux 提供联合文件系统 内核。与使用 卷,直接写入主机文件系统。
如果您的容器生成非持久状态数据,请考虑使用 tmpfs 挂载以避免将数据永久存储在任何位置,并且 通过避免写入容器的 writable 层。
卷使用绑定传播,而绑定传播不是
可针对卷进行配置。rprivate
卷的生命周期
卷的内容存在于给定容器的生命周期之外。当 container 被销毁,可写层也随之销毁。使用卷 确保即使删除了使用数据的容器,数据也会被保留。
给定的卷可以同时挂载到多个容器中。当 no
正在运行的容器正在使用卷,则该卷仍可用于 Docker
,并且不会自动删除。您可以使用 删除未使用的卷。docker volume prune
在现有数据上挂载卷
如果您将非空卷挂载到容器中的目录中,其中
文件或目录存在,则预先存在的文件会被挂载遮挡。
这类似于将文件保存到 Linux 主机上,并且
然后将 USB 驱动器挂载到 中。的内容将被遮挡
通过 USB 驱动器的内容,直到 USB 驱动器被卸载。/mnt
/mnt
/mnt
对于容器,没有直接的方法可以移除挂载来显示 再次被遮挡的文件。最好的选择是在 坐骑。
如果将空卷挂载到容器中文件 或目录存在,则这些文件或目录将被传播(复制)到 默认情况下的卷。同样,如果您启动容器并指定卷 ,则会为您创建一个空卷。这是一个 预填充其他容器所需的数据的好方法。
防止 Docker 将容器的预先存在的文件复制到空的
volume 中,请使用选项 --mount 的选项。volume-nocopy
命名卷和匿名卷
卷可以是 named 或 anonymous。匿名卷被赋予一个随机名称
保证在给定的 Docker 主机中是唯一的。就像命名一样
卷中,即使您删除了使用
它们,除非在创建容器时使用标志,其中
如果销毁与容器关联的匿名卷。请参阅删除匿名卷。--rm
如果您连续创建多个容器,每个容器都使用 anonymous volumes 中,每个容器都会创建自己的卷。匿名卷不会重复使用 或在容器之间自动共享。共享匿名卷 在两个或多个容器之间,您必须使用 随机卷 ID。
语法
要使用该命令挂载卷,您可以使用 or 标志。docker run
--mount
--volume
$ docker run --mount type=volume,src=<volume-name>,dst=<mount-path>
$ docker run --volume <volume-name>:<mount-path>
一般来说,是首选。主要区别在于该标志更明确并支持所有可用选项。--mount
--mount
如果您想要,则必须使用:--mount
- 指定卷驱动程序选项
- 挂载 volume 子目录
- 将卷挂载到 Swarm 服务
--mount 的选项
该标志由多个键值对组成,用逗号分隔
每个 Tuples 都由一个 Tuples 组成。键的顺序不是
重要。--mount
<key>=<value>
$ docker run --mount type=volume[,src=<volume-name>],dst=<mount-path>[,<key>=<value>...]
的有效选项包括:--mount type=volume
选择 | 描述 |
---|---|
source ,src | 挂载的源。对于命名卷,这是卷的名称。对于匿名卷,此字段被省略。 |
destination , ,dst target | 文件或目录在容器中挂载的路径。 |
volume-subpath | 卷中要挂载到容器中的子目录的路径。在将卷挂载到容器之前,该子目录必须存在于卷中。请参阅挂载 volume 子目录。 |
readonly ,ro | 如果存在,则会导致卷以只读方式挂载到容器中。 |
volume-nocopy | 如果存在,则如果卷为空,则不会将目标中的数据复制到卷中。默认情况下,如果目标内容为空,则会将目标内容复制到已挂载的卷中。 |
volume-opt | 可以多次指定,采用由选项名称及其值组成的键值对。 |
$ docker run --mount type=volume,src=myvolume,dst=/data,ro,volume-subpath=/foo
--volume 的选项
or 标志由三个字段组成,用冒号分隔
字符 ()。字段的顺序必须正确。--volume
-v
:
$ docker run -v [<volume-name>:]<mount-path>[:opts]
对于命名卷,第一个字段是卷的名称,并且是 在给定主机上唯一。对于匿名卷,第一个字段为 省略。第二个字段是文件或目录的挂载路径 容器。
第三个字段是可选的,是以逗号分隔的选项列表。有效
数据卷的选项包括:--volume
选择 | 描述 |
---|---|
readonly ,ro | 如果存在,则会导致卷以只读方式挂载到容器中。 |
volume-nocopy | 如果存在,则如果卷为空,则不会将目标中的数据复制到卷中。默认情况下,如果目标内容为空,则会将目标内容复制到已挂载的卷中。 |
$ docker run -v myvolume:/data:ro
创建和管理卷
与绑定挂载不同,您可以在任何 容器。
创建卷:
$ docker volume create my-vol
列出卷:
$ docker volume ls
local my-vol
检查卷:
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
删除卷:
$ docker volume rm my-vol
使用卷启动容器
如果您使用尚不存在的卷启动容器,Docker 会创建
适合您的音量。以下示例将卷挂载到容器中。myvol2
/app/
以下和示例产生相同的结果。你不能
除非删除容器和卷,否则请同时运行它们
在运行第一个之后。-v
--mount
devtest
myvol2
$ docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
$ docker run -d \
--name devtest \
-v myvol2:/app \
nginx:latest
用于验证 Docker 是否创建了卷并挂载了该卷
正确。查找该部分:docker inspect devtest
Mounts
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
这表明挂载是一个卷,它显示了正确的源和 destination 的 Destination 和挂载是读写的。
停止容器并删除卷。请注意,卷删除是单独的 步。
$ docker container stop devtest
$ docker container rm devtest
$ docker volume rm myvol2
将卷与 Docker Compose 结合使用
以下示例显示了一个具有卷的 Docker Compose 服务:
services:
frontend:
image: node:lts
volumes:
- myapp:/home/node/app
volumes:
myapp:
首次运行会创建一个卷。Docker 在随后运行命令时重用相同的卷。docker compose up
您可以使用 和
然后在里面引用它,如下所示:docker volume create
compose.yaml
services:
frontend:
image: node:lts
volumes:
- myapp:/home/node/app
volumes:
myapp:
external: true
有关将卷与 Compose 结合使用的更多信息,请参阅 Compose 规范中的卷部分。
使用卷启动服务
当您启动服务并定义卷时,每个服务容器都使用自己的
local volume 的 local volume 来获取。如果您使用卷驱动程序,则任何容器都无法共享此数据。但是,某些卷驱动程序确实支持共享存储。local
以下示例启动一个具有四个副本的服务,每个副本
使用名为 的本地卷。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
4d7oz1j85wwn devtest-service.1 nginx:latest moby Running Running 14 seconds ago
您可以删除该服务以停止正在运行的任务:
$ docker service rm devtest-service
删除服务不会删除该服务创建的任何卷。 卷删除是一个单独的步骤。
使用容器填充卷
如果您启动一个创建新卷的容器,并且容器
在要挂载的目录中有文件或目录,例如 ,
Docker 将目录的内容复制到卷中。然后,容器
挂载并使用该卷,以及使用该卷的其他容器
有权访问预填充的内容。/app/
为了说明这一点,下面的示例启动一个容器,并且
使用容器目录的内容填充新卷。这是 Nginx 存储其默认 HTML 的地方
内容。nginx
nginx-vol
/usr/share/nginx/html
和 examples 具有相同的最终结果。--mount
-v
$ docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html \
nginx:latest
$ 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
使用只读卷
对于某些开发应用程序,容器需要写入 bind
mount,以便将更改传播回 Docker 主机。在其他时候,
容器只需要对数据的读取访问权限。倍数
容器可以挂载相同的卷。您可以同时挂载
单个卷,对于某些容器和其他容器。read-write
read-only
以下示例更改了上一个示例。它将目录挂载为只读
volume 中,将其添加到 (默认为空) 选项列表后,在
挂载点。如果存在多个选项,则可以分隔
他们使用逗号。ro
和 examples 具有相同的结果。--mount
-v
$ docker run -d \
--name=nginxtest \
--mount source=nginx-vol,destination=/usr/share/nginx/html,readonly \
nginx:latest
$ docker run -d \
--name=nginxtest \
-v nginx-vol:/usr/share/nginx/html:ro \
nginx:latest
用于验证 Docker 是否创建了只读挂载
正确。查找该部分:docker inspect nginxtest
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
挂载 volume 子目录
将卷挂载到容器时,您可以指定
volume 要使用的 volume 和标志的参数。这
您指定的子目录必须存在于卷中,然后才能尝试
将其安装到容器中;如果不存在,则挂载失败。volume-subpath
--mount
如果您只想共享特定的
卷的一部分。例如,假设您有多个
容器运行,并且您希望将每个容器中的日志存储在共享的
卷。您可以为共享卷中的每个容器创建一个子目录。
并将子目录挂载到容器中。volume-subpath
以下示例创建一个卷并启动子目录 和 卷中。然后,它启动两个容器并挂载一个
添加到每个容器的卷的子目录中。这个例子
假设容器中的进程将其日志写入 和 。logs
app1
app2
logs
/var/log/app1
/var/log/app2
$ docker volume create logs
$ docker run --rm \
--mount src=logs,dst=/logs \
alpine mkdir -p /logs/app1 /logs/app2
$ docker run -d \
--name=app1 \
--mount src=logs,dst=/var/log/app1/,volume-subpath=app1 \
app1:latest
$ docker run -d \
--name=app2 \
--mount src=logs,dst=/var/log/app2,volume-subpath=app2 \
app2:latest
通过此设置,容器将其日志写入
卷。容器无法访问其他容器的日志。logs
在计算机之间共享数据
在构建容错应用程序时,您可能需要配置多个 同一服务的副本来访问相同的文件。
在开发应用程序时,有几种方法可以实现此目的。 一种是向应用程序添加逻辑以将文件存储在云对象上 存储系统,如 Amazon S3。另一种方法是使用 支持将文件写入外部存储系统,如 NFS 或 Amazon S3。
卷驱动程序允许您从 application logic.例如,如果您的服务使用具有 NFS 的卷 驱动程序,您可以更新服务以使用其他驱动程序。例如,到 将数据存储在云中,而无需更改应用程序逻辑。
使用卷驱动程序
当您使用 创建卷时,或者启动
container 使用尚未创建的卷,您可以指定卷驱动程序。
以下示例首先在创建
独立卷,然后在启动容器时创建一个新的
卷。docker volume create
vieux/sshfs
注意
如果您的卷驱动程序接受逗号分隔的列表作为选项, 您必须从外部 CSV 解析器中转义值。要转义 , 用双引号 () 括起来,并将整个 mount 参数括起来 带单引号 () 。
volume-opt
"
'
例如,驱动程序接受以逗号分隔的挂载选项 list 的 Zip 文件。此示例显示了转义列表的正确方法。
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>
初始设置
以下示例假定您有两个节点,第一个节点是 Docker host 的 URL 中,并且可以使用 SSH 连接到第二个节点。
在 Docker 主机上,安装插件:vieux/sshfs
$ docker plugin install --grant-all-permissions vieux/sshfs
使用卷驱动程序创建卷
此示例指定 SSH 密码,但如果两台主机具有共享密钥
配置后,您可以排除密码。每个卷驱动程序可以有零个或多个
configurable 选项,您可以使用一个标志指定每个选项。-o
$ docker volume create --driver vieux/sshfs \
-o sshcmd=test@node2:/home/test \
-o password=testpassword \
sshvolume
启动一个使用卷驱动程序创建卷的容器
以下示例指定 SSH 密码。但是,如果两台主机具有 共享密钥,则可以排除该密码。 每个卷驱动程序可以有零个或多个可配置选项。
注意
如果卷驱动程序要求您传递任何选项, 您必须使用 flag 来挂载卷,而不是 .
--mount
-v
$ docker run -d \
--name sshfs-container \
--mount type=volume,volume-driver=vieux/sshfs,src=sshvolume,target=/app,volume-opt=sshcmd=test@node2:/home/test,volume-opt=password=testpassword \
nginx:latest
创建用于创建 NFS 卷的服务
以下示例显示了如何在创建服务时创建 NFS 卷。
它用作 NFS 服务器和 NFS 服务器上的导出目录。
请注意,指定的卷驱动程序是 .10.0.0.10
/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=:/var/docker-nfs,"volume-opt=o=addr=10.0.0.10,rw,nfsvers=4,async"' \
nginx:latest
创建 CIFS/Samba 卷
您可以直接在 Docker 中挂载 Samba 共享,而无需在主机上配置挂载点。
$ docker volume create \
--driver local \
--opt type=cifs \
--opt device=//uxxxxx.your-server.de/backup \
--opt o=addr=uxxxxx.your-server.de,username=uxxxxxxx,password=*****,file_mode=0777,dir_mode=0777 \
--name cif-volume
如果您指定主机名而不是 IP,则该选项是必需的。
这允许 Docker 执行主机名查找。addr
块存储设备
您可以将块存储设备(例如外部驱动器或驱动器分区)挂载到容器。 以下示例显示了如何创建文件并将其用作块存储设备。 以及如何将块存储设备挂载为容器卷。
重要
以下过程只是一个示例。 不建议将此处所示的解决方案作为一般做法。 除非您对自己正在做的事情有信心,否则不要尝试这种方法。
挂载块设备的工作原理
在后台,使用 storage driver 的标志会调用
Linux sys调用并转发您传递给它的选项。
Docker 不会在 Linux 内核支持的本机挂载功能之上实现任何其他功能。--mount
local
mount
如果您熟悉 Linux 挂载
命令,
您可以按以下方式将选项视为转发给命令:--mount
mount
$ mount -t <mount.volume-opt.type> <mount.volume-opt.device> <mount.dst> -o <mount.volume-opts.o>
为了进一步解释这一点,请考虑以下命令示例。
此命令将设备挂载到系统上的路径。mount
/dev/loop5
/external-drive
$ mount -t ext4 /dev/loop5 /external-drive
以下命令从正在运行的容器的角度来看,可实现类似的结果。
使用此选项运行容器会以与执行上例中的命令相同的方式设置挂载。docker run
--mount
mount
$ docker run \
--mount='type=volume,dst=/external-drive,volume-driver=local,volume-opt=device=/dev/loop5,volume-opt=type=ext4'
你不能直接在容器内运行命令,
,因为容器无法访问设备。
这就是命令使用该选项的原因。mount
/dev/loop5
docker run
--mount
示例:在容器中挂载块存储设备
以下步骤将创建一个文件系统并将其挂载到容器中。
系统的文件系统支持取决于您使用的 Linux 内核版本。ext4
创建一个文件并为其分配一些空间:
$ fallocate -l 1G disk.raw
在文件上构建文件系统:
disk.raw
$ mkfs.ext4 disk.raw
创建 loop 设备:
$ losetup -f --show disk.raw /dev/loop5
注意
losetup
创建一个临时循环设备,该设备在 系统重启,或使用 手动删除。losetup -d
运行将 loop 设备挂载为卷的容器:
$ docker run -it --rm \ --mount='type=volume,dst=/external-drive,volume-driver=local,volume-opt=device=/dev/loop5,volume-opt=type=ext4' \ ubuntu bash
当容器启动时,该路径将主机文件系统中的文件挂载为块设备。
/external-drive
disk.raw
完成后,设备已从容器中卸载, detach the loop device 以从主机系统中删除设备:
$ losetup -d /dev/loop5
备份、还原或迁移数据卷
卷可用于备份、还原和迁移。
使用该标志创建挂载该卷的新容器。--volumes-from
备份卷
例如,创建一个名为 的新容器 :dbstore
$ docker run -v /dbdata --name dbstore ubuntu /bin/bash
在下一个命令中:
- 启动新容器并从容器中挂载卷
dbstore
- 将本地主机目录挂载为
/backup
- 传递一个命令,将卷的内容 tar 到目录内的一个文件。
dbdata
backup.tar
/backup
$ docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata
当命令完成并且容器停止时,它会创建一个
卷。dbdata
从备份还原卷
使用刚刚创建的备份,您可以将其恢复到同一个容器。 或您在其他位置创建的另一个容器。
例如,创建一个名为 的新容器 :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"
您可以使用这些技术来自动化备份、迁移和还原 使用您喜欢的工具进行测试。
删除卷
删除容器后,Docker 数据卷仍然存在。有两种类型 需要考虑的卷数:
- 命名卷具有来自容器外部的特定源,例如 .
awesome:/bar
- 匿名卷没有特定的来源。因此,在删除容器时,您可以指示 Docker Engine 守护程序将其删除。
删除匿名卷
要自动删除匿名卷,请使用该选项。例如
此命令将创建一个匿名卷。删除容器时,
Docker 引擎会删除卷,但不会删除卷。--rm
/foo
/foo
awesome
$ docker run --rm -v /foo -v awesome:/bar busybox top
注意
如果另一个容器将卷与 绑定,则会复制卷定义,并且 匿名卷在删除第一个容器后也会保留。
--volumes-from
删除所有卷
要删除所有未使用的卷并释放空间:
$ docker volume prune
后续步骤
- 了解 bind mounts。
- 了解 tmpfs 挂载。
- 了解存储驱动程序。
- 了解第三方卷驱动程序插件。