了解DockerFile中的“VOLUME”指令

下面是我的“Dockerfile”的内容

FROM node:boron # Create app directory RUN mkdir -p /usr/src/app # change working dir to /usr/src/app WORKDIR /usr/src/app VOLUME . /usr/src/app RUN npm install EXPOSE 8080 CMD ["node" , "server" ] 

在这个文件中,我期待“VOLUME。/ usr / src / app”指令将当前工作目录的内容挂载在容器的/ usr / src / app文件夹中的主机上。

请让我知道这是否是正确的方法?

官方docker教程告诉:

数据卷是绕过联盟文件系统的一个或多个容器内的特定目录。 数据卷为持久或共享数据提供了一些有用的function:

  • 当容器被创build时,卷被初始化。 如果容器的基本映像包含指定安装点上的数据,
    现有数据在卷上被复制到新卷中
    初始化。 (请注意,这不适用于安装主机
    目录。)
  • 数据卷可以在容器之间共享和重用。

  • 数据量的变化是直接进行的。

  • 更新图像时,不会包含对数据量的更改。

  • 即使容器本身被删除,数据量仍然存在。

Dockerfile您只能指定容器卷的目的地。 例如/usr/src/app

当你运行容器例如docker run --volume=/opt:/usr/src/app my_image可以但不必在主机中指定挂载点( / opt )。 如果不指定–volume参数,则会自动select安装点

总之:不,你的VOLUME指令是不正确的。

Dockerfile的VOLUME指定一个或多个给定容器端path的卷。 但它不允许图像作者指定主机path。 在主机端,这些卷是在Docker根目录下用一个非常长的类似ID的名字创build的。 在我的机器上是/var/lib/docker/volumes

注意:由于自动生成的名字非常长,并且从人的angular度来看没有意义,所以这些卷通常被称为“未命名”或“匿名”。

你的例子使用'。' 字符甚至不会在我的机器上运行,无论是第一个还是第二个参数。 我得到这个错误消息:

docker:来自守护进程的错误响应:oci运行时错误:container_linux.go:265:启动容器进程导致“process_linux.go:368:容器init导致”打开/ dev / ptmx:没有这样的文件或目录\“”。

我知道,对这个问题所说的话可能对于那些试图理解VOLUME-v人来说并不是很有价值,它当然不能提供你想要完成的解决scheme。 所以,希望下面的例子能够更好地阐明这些问题。

Minitutorial:指定卷

鉴于这个Dockerfile:

 FROM openjdk:8u131-jdk-alpine VOLUME vol1 vol2 

(对于这个minitutorial的结果,如果我们指定vol1 vol2/vol1 /vol2 ,没有任何区别 – 不要问我为什么)

build立它:

 docker build -t my-openjdk 

跑:

 docker run --rm -it my-openjdk 

在容器里面,运行ls ,你会发现有两个目录存在; /vol1/vol2

运行容器还会在主机端创build两个目录或“卷”。

在容器运行的同时,在主机上执行docker volume ls ,你会看到类似的东西(为了简洁,我用两个点replace了名字的中间部分):

 DRIVER VOLUME NAME local c984..e4fc local f670..49f0 

回到容器中 ,执行touch /vol1/weird-ass-file (在所述位置创build一个空白文件)。

这个文件现在可以在主机上,在一个未命名的卷lol中。 我花了两次尝试,因为我第一次尝试了第一个列出的卷,但最终我find了我的文件在第二个列出的卷,在主机上使用此命令:

 sudo ls /var/lib/docker/volumes/f670..49f0/_data 

同样,您可以尝试删除主机上的这个文件,并将其从容器中删除。

注意: _data文件夹也被称为“挂载点”。

退出容器并列出主机上的卷。 他们走了。 我们在运行容器时使用了--rm标志,并且这个选项不仅清除了出口处的容器, --rm除了卷。

运行一个新的容器,但使用-v指定一个卷:

 docker run --rm -it -v /vol3 my-openjdk 

增加了第三卷,整个系统最终有三个未命名的卷。 如果我们只指定-v vol3 ,命令就会崩溃。 参数必须是容器绝对path。 在主机端,新的第三卷是匿名的,并与其他两个卷一起驻留在/var/lib/docker/volumes/

前面说过, Dockerfile不能映射到主机path,在运行时试图将文件从主机传送到容器时,会给我们带来一些问题。 不同的-v语法解决了这个问题。

想象一下,我在我的项目目录./src中有一个子文件夹,我希望在容器内同步到/src 。 这个命令有诀窍:

 docker run -it -v $(pwd)/src:/src my-openjdk 

性格的双方都期待着绝对的道路。 左边是主机上的绝对path,右边是容器内的绝对path。 pwd是“打印当前/工作目录”的命令。 把这个命令放在$()把这个命令放在括号里面,在子shell中运行它,并返回到我们项目目录的绝对path。

把它放在一起,假设我们在主机上的项目文件夹中有./src/Hello.java ,其内容如下:

 public class Hello { public static void main(String... ignored) { System.out.println("Hello, World!"); } } 

我们构build这个Dockerfile:

 FROM openjdk:8u131-jdk-alpine WORKDIR /src ENTRYPOINT javac Hello.java && java Hello 

我们运行这个命令:

 docker run -v $(pwd)/src:/src my-openjdk 

这会打印出“Hello,World!”。

最好的部分是,我们完全可以自由地修改.java文件,并在第二次运行时使用另一个输出的新消息 – 而不必重新构build图像=)

最后的言论

我对Docker来说是相当新颖的,前面提到的“教程”反映了我从一个为期3天的命令行黑客马拉松收集的信息。 我几乎感到羞愧,因为我没有能够提供链接来清楚地expression类似于英文的文件,但是我真的认为这是由于缺less文档而不是个人的努力。 我知道这些例子的工作原理是使用当前的设置“Windows 10 – > Vagrant 2.0.0 – > Docker 17.09.0-ce”。

本教程没有解决“我们如何在Dockerfile中指定容器的path,并让运行命令只指定主机path”。 可能有一种方法,我只是没有find它。

最后,我有一种直觉,认为在Dockerfile中指定VOLUME并不罕见,但最好不要使用VOLUME 。 有两个原因。 我们已经确定的第一个原因:我们不能指定主机path – 这是一件好事,因为Dockerfiles对于主机的细节应该是非常不可知的。 但第二个原因是人们在运行容器时可能会忘记使用--rm选项。 有人可能会记得去除容器,但忘记去除容积。 此外,即使是最好的人类记忆,找出所有匿名卷中哪些可以安全移除也是一项艰巨的任务。