Docker中的挂载

简介

在介绍VOLUME指令之前, 我们来看下如下场景需求:

  1. 容器是基于镜像创建的, 最后的容器文件系统包括镜像的只读层+可写层, 容器中的进程操作的数据持久化都是保存在容器的可写层上. 一旦容器删除后, 这些数据就没了, 除非我们人工备份下来(或者基于容器创建新的镜像). 能否可以让容器进程持久化的数据保存在主机上呢? 这样即使容器删除了, 数据还在.

  2. 当我们在开发一个web应用时, 开发环境是在主机本地, 但运行测试环境是放在docker容器上. 这样的话, 我在主机上修改文件(如html, js等)后, 需要再同步到容器中. 这显然比较麻烦.

  3. 多个容器运行一组相关联的服务, 如果他们要共享一些数据怎么办?

对于这些问题, 我们当然能想到各种解决方案. 而docker本身提供了一种机制, 可以将主机上的某个目录与容器的某个目录(称为挂载点、或者叫卷)关联起来, 容器上的挂载点下的内容就是主机的这个目录下的内容, 这类似linuxmount的机制. 这样的话, 我们修改主机上该目录的内容时, 不需要同步容器, 对容器来说是立即生效的. 挂载点可以让多个容器共享.

挂载机制

容器运行时挂载

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

Mount volume (-v, –read-only)

$ 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 为例

777f7dc92da7ba8c0c54f0f2是已知的容器,可以通过添加:ro:rw来表示只读或者只写模式.

当向容器挂载volume时, 标签系统需要通过标签(label)来判定是否允许容器内的程序访问volume里的内容. 如果没有标签的话, 则会禁止. Docker默认不会修改系统自带的标签.

如果需要修改容器context中内容对应的标签, 可以通过添加:z或者:Z后缀进行标注.z表示两个容器分享volume中的内容,Docker会给这部分内容打上分享内容的标签, 被标注为分享内容的内容可以被所有容器读/写. 而Z表示告诉Docker给内容打上独有的标签, 只有当前容器可以访问这部分内容(还不清楚这部分内容具体操作步骤).

镜像构建时挂载

Dockerfile references for the VOLUME instruction

在容器运行时挂时通过-v创建的挂载点只对创建的容器有效, 而通过dockerfileVOLUME 指令可以在镜像中创建挂载点, 这样只要通过该镜像创建的容器都有了挂载点. 通过这种方式创建的挂载点, 无法指定主机上对应的目录, 是自动生成的.

以下面的dockerfileVOLUME 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 volume (-v, –read-only)

Mount volumes from container (–volumes-from)

docker学习笔记18: Dockerfile 指令 VOLUME 介绍

Dockerfile references for the VOLUME instruction

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据