了解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
选项。 有人可能会记得去除容器,但忘记去除容积。 此外,即使是最好的人类记忆,找出所有匿名卷中哪些可以安全移除也是一项艰巨的任务。