Docker提供了三种不同的方式用于将宿主的数据挂载到容器中:volumes,bind mounts,tmpfs volumes。当你不知道该选择哪种方式时,记住,volumes总是正确的选择。
无论选择哪种挂载方式,在容器内部看来,数据就是数据,并没有什么不同。主机的数据在容器的文件系统中总被显示为目录或文件。如何简单的理解这三种挂载方式的不同之处呢,我们可以理解为在这三种方式下,容器内的数据在宿主机中存放的位置不同,见下图:
- 如果你使用volumes的方式挂载了一个空的volume到某个容器的一个非空目录中,则这个非空目录中已存在的内容会被拷贝到这个volume中。类似的,如果在启动容器时指定了一个不存在的volume,一个空的volume会被自动创建;
- 如果使用bind mounts的方式挂载,或者用volumes的方式挂载了一个非空的volume到容器的一个非空目录中,则容器中这个非空目录下的内容将暂时被挂载过来的volume中的内容所覆盖(并未被删除),当取消挂载后,容器中那个非空目录中的文件仍然存在。就像在linux下的/mnt目录下如果存在一些文件,在把USB挂载到/mnt时,在/mnt下就只能看到USB中的内容而看不见原先的文件,当取消USB挂载后,再进入/mnt就可以看见原先的文件了。
选择使用 -v还是--mount
起初,-v或者--volume用于独立容器,--mount用于swarm services。然而,从Docker 17.06开始,也可是使用--mount用于独立容器。—mount命令更精准详细。-v将选项进行了合并。使用--mount。
如果你需要制定volume驱动选项,你必须使用 --mount。
- -v或者--volume:由3部分参数组成,使用“:”间隔。顺序不能颠倒。
- 第一个部分是volumes名字,在宿主机上具有唯一性。匿名卷名字系统给出。
- 第二部分是挂载到容器里的文件或文件夹路径。
- 第三部分是可选项列表分隔符,例如“or”,这些可选项在下面会讨论。
- —mount:由多个键值对组成,<key>=<value>。—mount要比-v或者--volume命令更长,但是更容易理解。
- type,可以是bind,volume或者tmpfs。这篇文章主要讨论volumes,所以type一直使用volume.
- source,volumes的名字,匿名volume可以省略。source可缩写为src.
- destination,挂载到容器中的文件或目录路径。可也缩写为dst或者使用target。
- readonly,指定挂载在容器中为只读。
- volume-opt,可选属性,可以多次使用。
-v和--mount的不同行为
与bind mounts不同,对于—mount和-v所有的选项都可以使用。
当使用volumes服务时,只支持--mount.
创建和管理volumes
不像bind mount,你可以在容器外创建和管理volumes。
创建一个volume:
$ docker volume create my-vol
显示所有volumes
$ docker volume ls
local my-vol
查看volumes
$ docker volume inspect my-vol
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/my-vol/_data",
"Name": "my-vol",
"Options": {},
"Scope": "local"
}
]
删除一个volume:
$ docker volume rm my-vol
启动一个带volume的容器
如果你启动一个带有volume容器,volume还没有创建,Docker会为你创建。下面的例子挂载myvol2到容器中的/app/下。
下面的例子-v和—mount结果是一样的。
--mount:
$ docker run -d \
--name devtest \
--mount source=myvol2,target=/app \
nginx:latest
-v:
$ docker run -d \
--name devtest \
-v myvol2:/app \
nginx:latest
使用inspect查看挂载是否正确,查看Mounts部分:
"Mounts": [
{
"Type": "volume",
"Name": "myvol2",
"Source": "/var/lib/docker/volumes/myvol2/_data",
"Destination": "/app",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
可以看出挂载正确,并且是可读写的。
停止容器然后删除volume
$ docker container stop devtest
$ docker container rm devtest
$ docker volume rm myvol2
启动一个带有volumes服务
当你启动服务定义一个volume,每个服务可以使用自己本地人volume.如果你使用local volume,容器不能分享数据,但是一些volume驱动支持分享存储。Docker for AWS and Docker for Azure使用Cloudstor插件都支持持久化存储。
下面的例子启动4份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 service create 命令不支持-v或者—volume。必须使用—mount。
使用容器加载一个volume
和上面一样,如果你启动一个容器创建一个新的volume,在容器被挂载的目录(/app/)中有文件或者文件夹,这个目录中的内容会被拷贝到volume中。然后容器挂载使用volume,其他容器使用这个volume也可以访问预加载内容。
为了说明这个,这个例子启动一个nginx容器并且加载一个新volume nginx-vol,里面包括容器中/usr/share/nginx/html 目录中的内容,里面存储的是nginx默认的HTML内容。
--mount and -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
使用只读volume
对于一些开发应用,容器需要回写数据到Docker主机。但有时容器只需要读数据。请记住多个容器可以挂载相同volume,一个挂载读写容器,也可以挂载只读容器,还可以两种同时挂载。
这个例子修改上面的例子,但是挂载的是只读容器,使用’or’分隔符处理选项列表,
--mount and -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 命令查看是否挂载正确,查看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
机器间共享数据
当构建高可用应用程序,你需要配置多个相同的服务访问相同文件。
有几种方法可以达到这种效果。一种是在你的应用中添加对云存储文件的访问,如Amazon S3。另一种是使用支持外服存储驱动(NFS, Amazon S3)的volume。
Volume驱动允许你在应用中抽象下层的存储系统。例如,如果你的服务使用NFS驱动volume,你可以使用不同的驱动更新服务,就像存储在云中的数据,不需要修改应用逻辑。
使用volume驱动
当你使用docker volume create创建一个volume,或者当你启动一个带有没创建volume的容器,你可以指定volume驱动。下面例子使用vieux/sshfs volume驱动 ,首先创建一个独立的volume,然后启动一个创建新volume的容器。
初始化设置
这个例子假设你有两个节点,第一个是Docker主机而且可以连接到第二个的ssh.
在Docker主机中安装vieux/sshfs插件:
$ docker plugin install --grant-all-permissions vieux/sshfs
使用volume驱动创建volume
这个样例指定一个SSH密码,但是如果两个主机共享keys配置,你可以省略密码。每个volume驱动可以没有或者更多配置选项,可以使用-o标识。
$ docker volume create --driver vieux/sshfs \
-o sshcmd=test@node2:/home/test \
-o password=testpassword \
sshvolume
test@node2:/home/test 为远程主机挂载点
启动一个带有使用volume驱动创建volume的容器
这个样例指定一个SSH密码,但是如果两个主机共享keys配置,你可以省略密码。每个volume驱动可以没有或者更多配置选项。如果volume驱动要穿可选参数,你必须使用—mount。
$ 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