弄清 Docker 中的 Volume
引用自http://container-solutions.com/understanding-volumes-docker/
为了了解 Docker volume 是什么,首先我们需要清楚 Docker 中的文件系统通常是如何工作的。Docker 镜像保存成一系列的只读层。当我们启动一个容器,Docker 提取某个只读镜像并在其上增加一个读写层。如果这个运行的容器修改了一个已有的文件,这个文件就从下面的只读层拷贝出来并放入最顶层的读写层中来应用这些修改。读写层的版本隐藏了下面的文件,但是没有破坏它,它仍然在下层的镜像中存在。当这个 Docker 容器被删除,重新运行这个镜像将启动一个新的容器,原容器所做的修改将会丢失。Docker 称这些只读层和读写层的组合在一个联合文件系统(Union File System)之上。
为了能够保存和共享容器间的数据,Docker 提出了 volume 的概念。十分简单, volume 是默认的联合文件系统之外的目录或者文件并且像普通目录和文件一样存在在主机文件系统上。
这里有两种方式初始化 volume, 有些细微的差别是需要了解的。我们可以在运行时使用 -v 声明一个 volume:
$ docker run -it --name container-test -h CONTAINER -v /data debian /bin/bash root@CONTAINER:/# ls /data root@CONTAINER:/#
这将在这个容器中联合文件系统之外创建 /data 目录并且可以直接在主机中访问。这个镜像中 /data 目录所有的任何文件都将拷贝到这个 volume 中。我们可以通过在主机上使用 docker inspect 命令找出 volume 在主机中的位置(打开一个新的终端并让前一个容器是在运行中):
$ docker inspect -f {{.Volumes}} container-test
然后你将看到类似下面的内容:
map[/data:/var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f7c0d65b32cecbada822b0db4cc92e79059437a9]
告诉我们 Docker 已经把容器的 /data 挂载在主机 /var/lib/docker 目录下的某处,让我们从主机增加一个文件到这个目录中:
$ sudo touch /var/lib/docker/vfs/dir/cde167197ccc3e138a14f1a4f7c0d65b32cecbada822b0db4cc92e79059437a9/test-file
然后切换回我们的容器并查看一下:
$ root@CONTAINER:/# ls /data test-file
改变立刻反应在容器中,就像这个目录是主机上的某个目录的简单挂载一样。我们可以通过在 Dockerfile 文件中使用 VOLUME 指令达到完全一样的效果:
FROM debian:wheezy
VOLUME /data
但是有一点是在 docker 运行时使用 -v 参数能做到而从 Dockerfile 做不到的,并且它是挂载主机上的一个指定目录到容器。例如:
$ docker run -v /home/adrian/data:/data debian ls /data
将主机上的 /home/adrian/data 目录挂载为容器中的 /data。/home/adrian/data 目录中已有的任何文件
都将能在容器中使用。这非常有利于共享主机和容器的文件,比如挂载用来编译的源码。用作 volume 的主机目录是不能再 Dockerfile 中指定的,为了维护可移植性(主机目录也许不能在所有系统中都有效)。当用 -v 参数这种形式时,镜像该目录下的任何文件都不会拷贝入 volume 中。
共享数据
为了使一个容器访问另一个容器的 volume,我们可以在 docker 运行时简单使用 -volumes-from 参数,例如:
$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash root@NEWCONTAINER:/# ls /data test-file root@NEWCONTAINER:/#
要注意的是 container-test 是否正在运行。如果一个 volume 有一个容器链接,它将永远不能被删掉。
数据容器
使用一个纯数据容器来存放持久化数据,配置文件,数据文件等等是很常见的实践。例如:
$ docker run --name dbdata postgres echo "Data-only container for postgres"
这个命令将创建一个 postgres 镜像,包括 Dockerfile 中定义的 volume,运行 echo 命令后退出。echo 命令能在我们使用 docker ps 时快速的帮我们鉴别这个镜像的目的。我们可以从其他容器中用 -volume-from 参数使用这个 volume:
$ docker run -d --volumes-from dbdata --name db1 postgres
使用运行中德数据容器时有两个要点:
- 不要脱离数据容器运行,这会毫无意义的浪费资源。
- 不要使用一个“最小镜像”如 busybox 或者 scratch 用做数据容器。只使用数据库镜像本身。你已经有这些镜像了,所以不会占用任何额外的空间并且允许 volume 作为镜像的数据。
备份
如果你使用一个数据容器,备份是非常简单的:
$ docker run --rm --volumes-from dbdata -v $(pwd):/backup debian tar cvf /backup/backup.tar /var/lib/postgresql/data
会创建一个 volume 中所有文件的打包工具(官方 postgres Dockerfile 在 /var/lib/postgresql/data 定义了一个 volume )。
许可权和所有权
通常你必须在一个 volume 上设置许可权和拥有权或者通过一些默认数据或配置文件初始化这个 volume。最需要注意的是 Dockerfile 中 VOLUME 后的任何指令都不能改变这个 VOLUME,例如:
FROM debian:wheezy RUN useradd foo VOLUME /data RUN touch /data/x RUN chown -R foo:foo /data
将不会像期望的那样工作。我们要 touch 命令在这个镜像的文件系统中运行,但是它实际上是运行在一个临时容器的 volume 中。下面的才会有效:
FROM debian:wheezy RUN useradd foo RUN mkdir /data && touch /data/x RUN chown -R foo:foo /data VOLUME /data
Docker 能非常聪明的把镜像 volume 挂载点下的所有存在的文件复制到 volume 中并且正确的设置所有权。如果你为这个 volume 指定一个主机目录,这些就不在发生(所以主机文件不能随便重写)。
如果你不能在 RUN 命令中设置许可权和访问权,你将不得不使用一个 CMD 或者 ENTRYPOINT 脚本在容器创建后运行。
删除 volume
有可能大多数人都意想不到,如果你已经使用 docker rm 来删除你的容器,你可能有很多遗弃的 volume 占用空间。
volume只能在如果容器是被 docker rm -v 命令移除(-v 是必须的)或者docker run 时使用 -rm的情形下删除。即使这样,一个 volume 还必须没有其他容器链接它,才能被删除。 volume 链接到用户指定的主机目录将永远不能被 docker 删除。
除非你已经非常小心一直像这样运行你的容器,在 /var/lib/docker/vfs/dir 下你总会有僵尸文件和目录并且没有简单的方法告诉我们它们代表什么。你可以使用 Docker Volume Manager 来帮助跟踪你的 volume 并清理遗弃的 volume。
posted on 2015-10-02 00:55 Sky.Y.Chen 阅读(1602) 评论(0) 收藏 举报