Python中国社区  »  docker

深入理解Docker Volume

Py站长 • 1 年前 • 745 次点击  

Docker的难点之一就是Volume的使用,这也是很多人都会问到的问题。所以让我们一起来深入看看Docker Volume是如何工作的。

很多人都对Volume有一个误解,他们认为Volume是为了持久化。如此想法是因为他们觉得容器不能持久化,所以Volume应该是为了满足这个需求而设计的。其实容器会一直存在,除非你删除它们:

这可能来自于容器不是持久的想法,这样确实是不对的。容器是持久的,直到你删除他们,并且你只能这样做:

docker rm my_container

如果你没有执行此命令,那么你的容器会一直存在,依旧可以启动、停止等。如果你找不到你的容器,可以运行此命令:

docker ps -a

docker ps只能显示正在运行的容器,但是容器也会处于停止状态,这种情况下,上面的命令(译者注:-a参数会列出所有的容器)会显示所有的容器,无论它们处于什么状态。docker run ...命令可以有很多的组合(译者注:它提供了Docker容器从创建到启动的所有功能),它会创建一个新的容器,然后启动它。

综上,再次声明:Volume并不是为了持久化。

什么是Volume

Volume可以将容器以及容器产生的数据分离开来,这样,当你使用docker rm my_container删除容器时,不会影响相关的数据。

Volume可以使用以下两种方式创建:

  • 在Dockerfile中指定VOLUME /some/dir
  • 执行docker run -v /some/dir命令来指定

无论哪种方式都是做了同样的事情。它们告诉Docker在主机上创建一个目录(默认情况下是在/var/lib/docker下),然后将其挂载到指定的路径(例子中是:/some/dir)。当删除使用该Volume的容器时,Volume本身不会受到影响,它可以一直存在下去。

如果在容器中不存在指定的路径,那么该目录将会被自动创建。

你可以告诉Docker同时删除容器和其Volume:

docker rm -v my_container

有时候,你想在容器中使用主机上的某个目录,你可以通过其它的参数来指定(译者注:注意冒号前面的和后面的内容):

docker run -v /host/path:/some/path ...

这明确地告诉Docker使用指定的主机路径来代替Docker自己创建的根路径并挂载到容器内指定的路径(以上例子为/some/path)。需要注意的是,这种方式同样支持文件。在Docker术语中,这通常被称为bind-mounts(虽然技术层面上是这样讲的,但是实际的感觉是所有的Volume都是bind-mounts的)。如果主机上的路径不存在,目录将自动在给定的路径中创建。

对待bind-mount Volume和一个“正常”的Volume有一点不同,它不会修改主机上那些非Docker创建的东西:

  • 一个“正常”的Volume,Docker会自动将指定Volume路径(如上面的示例/some/path)上的数据复制到由Docker创建的新的目录下,如果是“bind-mount”,Volume就不会这样做。(译者注:这样做会将主机上的目录复制到容器)
  • 当你执行docker rm -v my_container命令时,“bind-mount” 类型的Volume不会被删除。

容器也可以与其它容器共享Volume。

docker run --name my_container -v /some/path ...
docker run --volumes-from my_container --name my_container2 ...

上面的命令将告诉Docker从第一个容器挂载相同的Volume到第二个容器,它可以在两个容器之间共享数据。

如果你执行 docker rm -v my_container 命令,而上方的第二容器依然存在,那Volume不会被删除,如果你不使用docker rm -v my_container2命令删除第二个容器,那它会一直存在。

Dockerfiles里的VOLUME

正如前面提到的,Dockerfile中的VOLUME指令也可以做同样的事情,类似docker run命令中的-v参数(除了你不能在Dockerfile指定主机路径)。也正因为如此,构建镜像时可以得到惊奇的效果。

在Dockerfile中的每个命令都会创建一个新的用于运行指定命令的容器,并将容器提交到镜像,每一步都是在前一步的基础上构建。因此在Dockerfile中ENV FOO=bar等同于:

cid=$(docker run -e FOO=bar <image>)
docker commit $cid

下面让我们来看看这个Dockerfile的例子发生了什么:

[{{
FROM debian:jessie
VOLUME /foo/bar
RUN touch /foo/bar/baz
}}}

docker build -t my_debian .

我们期待的是Docker创建一个名为my_debian并且Volume是/foo/bar的镜像,以及在/foo/bar/baz下添加了一个空文件,但是让我们看看等同的CLI命令行实际上做了哪些:

cid=$(docker run -v /foo/bar debian:jessie)
image_id=$(docker commit $cid)
cid=$(docker run $image_id touch /foo/bar/baz)
docker commit $(cid) my_debian

真实过程可能并不是这样,但是类似。

在这里,/foo/bar会首先创建,所以我们每次通过这个镜像启动一个容器,都会有一个空的/foo/bar目录。正如前面所说,Dockerfile中每个命令都会创建一个新容器。也就是说,每次都会创建一个新的Volume。由于例子的Dockerfile中是先指定Volume的,所以当执行touch /foo/bar/baz命令的容器创建时,一个Volume会被挂载到/foo/bar,然后baz才能被写入此Volume,而不是实际的容器或镜像的文件系统内。

所以,牢记Dockerfile中VOLUME指令的位置,因为它在你的镜像内创建了不可改变的目录。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/1427
 
745 次点击  
分享到微博
分享
社区所有版块导航
Python
python开源   DjangoApp   pycharm   Django   Python  
DATA
docker   Elasticsearch  
WEB开发
linux   其他Web框架   peewee   web工具   Jquery   zookeeper   MongoDB   tornado   Git   Redis   NoSql   bottle   DATABASE   Bootstrap   IE   NGINX   js   MQ  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广