Dockerfile 中的 VOLUME 与 docker run -v

Dockerfile中通过VOLUME /data可以将容器中的data目录,在运行时自动挂载为匿名卷,位置为宿主机的/var/lib/docker/volumes。同时对该目录文件的读取,依然是从容器内,但是写入,会写入到匿名卷中,而不会直接在容器中写入。这样保证了容器层的无状态化。

docker run 命令中的-v mydata:/data,可以将宿主机上的mydata这个目录挂载到容器的data目录,从而替代了默认的匿名卷,并且会覆盖掉容器中相同的目录,同时可以直接往该宿主机目录中读写文件。

如果Dockerfile中的VOLUME /data和docker run 命令中的-v mydata:/data同时使用,会发生什么呢?

下面以一个node工程为例,/cloud目录为该node工程中存放一些拓展模块js文件的目录,我们的目的是在更新(包括新增和删除)拓展模块后,无需重新构建镜像,而只需重启容器即可启用更新后的拓展。

先做三个实验:

  1. Dockerfile中定义VOLUME /cloud,run命令没有-v:可以直接使用拓展,但是更新某个拓展js文件后,restart容器,拓展并不会得到更新。原因:在没有-v时,docker将容器中的/cloud目录挂载为/var/lib/docker/volumes下的匿名卷,并且只有在写的时候才会用到这个匿名卷。而对/cloud目录下的文件的读取,早在构建镜像的时候就固定了,更新的js文件,并没有参与到镜像的构建,所以新的拓展不生效。
  2. Dockerfile中定义VOLUME /cloud,run命令有-v $(pwd)/mycloud:/cloud:可以直接使用拓展,并且更新某个拓展js文件后,restart容器,拓展会得到更新。原因:-v命令将宿主机mycloud挂载到容器的cloud目录,并且覆盖掉了原来的cloud目录,也就是宿主机的/mycloud目录覆盖掉了容器中构建镜像时写入该镜像的/cloud目录,从而在npm start的时候,执行的实际上是宿主机的mycloud目录下的拓展。
  3. Dockerfile中定义VOLUME /cloud,run命令有-v $(pwd)/mycloud:/cloud:可以直接使用拓展,并且删除某个拓展js文件后,restart容器,被删除的拓展会无法使用。原因:-v命令将宿主机mycloud挂载到容器的cloud目录,并且覆盖掉了原来的cloud目录,而且是目录级的覆盖,从而使得文件的删除可以生效。

结论:要实现只需要restart就能使得对拓展的更新(包括新增和删除)生效,而无需重新构建镜像,只需要同时使用Dockerfile中的VOLUME /data和docker run 命令中的-v mydata:/data,这样就能使用宿主机的mydata目录彻底覆盖容器中的data目录。并且对该目录的读写都能直接映射到宿主机。

Share

You may also like...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注