目录
简介
在介绍VOLUME
指令之前, 我们来看下如下场景需求:
- 容器是基于镜像创建的, 最后的容器文件系统包括镜像的只读层+可写层, 容器中的进程操作的数据持久化都是保存在容器的可写层上. 一旦容器删除后, 这些数据就没了, 除非我们人工备份下来(或者基于容器创建新的镜像). 能否可以让容器进程持久化的数据保存在主机上呢? 这样即使容器删除了, 数据还在.
-
当我们在开发一个
web
应用时, 开发环境是在主机本地, 但运行测试环境是放在docker
容器上. 这样的话, 我在主机上修改文件(如html
,js
等)后, 需要再同步到容器中. 这显然比较麻烦. -
多个容器运行一组相关联的服务, 如果他们要共享一些数据怎么办?
对于这些问题, 我们当然能想到各种解决方案. 而docker
本身提供了一种机制, 可以将主机上的某个目录与容器的某个目录(称为挂载点、或者叫卷)关联起来, 容器上的挂载点下的内容就是主机的这个目录下的内容, 这类似linux
下mount
的机制. 这样的话, 我们修改主机上该目录的内容时, 不需要同步容器, 对容器来说是立即生效的. 挂载点可以让多个容器共享.
挂载机制
容器运行时挂载
docker run
命令中, 和volume
相关的参数有三个
--volume , -v Bind mount a volume
--volume-driver Optional volume driver for the container
--volumes-from Mount volumes from the specified container(s)
其中--volume-driver
没有找到对应的解释.这里暂且忽略.
--volume,-v
以$ docker run -v /doesnt/exist:/foo -w /foo -i -t ubuntu bash
为例.
-v
在容器中设置了挂载点/foo
, 并将主机上的/doesnt/exist
目录中的内容关联到容器中的挂载点/foo
目录下.
如果/doesnt/exist
不存在, 则Docker
会在启动容器之前主动创建.
这样不论是在容器中对/foo
目录下的操作, 还是在主机上对/doesnt/exist
的操作, 都是完全实时同步的, 因为这两个目录实际都是指向主机目录.
其次, -v
选项可以和--read-only
选项组合.
以$ docker run --read-only -v /icanwrite busybox touch /icanwrite/here
为例, 容器ID为36e5717fa2be
-v
只设置了容器的挂载点/icanwrite
, 但没有指定关联的主机目录. 这时Docker
会自动绑定主机上的一个目录, 该目录可以通过docker inspect
查看容器得知具体路径.
>>> docker inspect 36e5717fa2be
...
"Mounts": [
{
"Type": "volume",
"Name": "7f7c2759aa763e7a6a0e5eb944969b869b0f37579de8d128c293e16291cc5211",
"Source": "/var/lib/docker/volumes/7f7c2759aa763e7a6a0e5eb944969b869b0f37579de8d128c293e16291cc5211/_data",
"Destination": "/icanwrite",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
Mounts
记录了容器上所有挂载点的信息, 其中Destination
值是容器的挂载点, Source
值是绑定的主机目录.
可以看出这种方式对应的主机目录是自动创建的, 虽然可以通过inspect
找到对应路径并在主机上直接修改路径下内容, 但其主要目的是为了能够让多个容器共享.
--volumes-from
Mount volumes from container (–volumes-from)
如果多个容器需要共享数据(如持久化数据库、配置文件或者数据文件等), 可以考虑创建一个特定的数据容器, 该容器有1个或多个卷. 其它容器通过--volumes-from
来共享这个数据容器的卷. 因为容器的卷本质上对应主机上的目录, 所以这个数据容器也不需要启动.
以 $ docker run --volumes-from 777f7dc92da7 --volumes-from ba8c0c54f0f2:ro -i -t ubuntu pwd
为例
777f7dc92da7
和ba8c0c54f0f2
是已知的容器,可以通过添加:ro
和:rw
来表示只读或者只写模式.
当向容器挂载volume
时, 标签系统需要通过标签(label
)来判定是否允许容器内的程序访问volume
里的内容. 如果没有标签的话, 则会禁止. Docker
默认不会修改系统自带的标签.
如果需要修改容器context
中内容对应的标签, 可以通过添加:z
或者:Z
后缀进行标注.z
表示两个容器分享volume
中的内容,Docker
会给这部分内容打上分享内容的标签, 被标注为分享内容的内容可以被所有容器读/写. 而Z
表示告诉Docker
给内容打上独有的标签, 只有当前容器可以访问这部分内容(还不清楚这部分内容具体操作步骤).
镜像构建时挂载
Dockerfile references for the VOLUME instruction
在容器运行时挂时通过-v
创建的挂载点只对创建的容器有效, 而通过dockerfile
的 VOLUME
指令可以在镜像中创建挂载点, 这样只要通过该镜像创建的容器都有了挂载点. 通过这种方式创建的挂载点, 无法指定主机上对应的目录, 是自动生成的.
以下面的dockerfile
–VOLUME TEST
为例
# VOLUME TEST
FROM busybox
VOLUME ["/data1","/data2"]
上面的dockfile
文件通过VOLUME
指令指定了两个挂载点 /data1
和 /data2
.
查看通过该dockerfile
创建的镜像生成的容器, 可以看到如下信息:
...
"Mounts": [
{
"Type": "volume",
"Name": "873ac17ac943f3f3b1b166e3eac71cb5fb968953d506a87df66d4035d3f1fc45",
"Source": "/var/lib/docker/volumes/873ac17ac943f3f3b1b166e3eac71cb5fb968953d506a87df66d4035d3f1fc45/_data",
"Destination": "/data1",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "dc8514b9728d1dee39af33f92b4b76b3d54ab1232990ca8bc4b3ff1cb8ca0d39",
"Source": "/var/lib/docker/volumes/dc8514b9728d1dee39af33f92b4b76b3d54ab1232990ca8bc4b3ff1cb8ca0d39/_data",
"Destination": "/data2",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
可以看到两个挂载点的信息.
References
Mount volumes from container (–volumes-from)