以下是我的"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文件夹中.
如果这是正确的方法,请告诉我?
1> Martin Ander..:
简而言之:不,你的VOLUME
指示不正确.
Dockerfile VOLUME
指定给定容器端路径的一个或多个卷.但它不允许图像作者指定主机路径.在主机端,在Docker根目录中创建的卷具有非常长的ID类名称.在我的机器上这是/var/lib/docker/volumes
.
注意:因为自动生成的名称非常长并且从人的角度来看没有意义,所以这些卷通常被称为"未命名"或"匿名".
你使用'.'的例子.无论我是否将点作为第一个或第二个参数,角色甚至都不会在我的机器上运行.我收到此错误消息:
docker:来自守护进程的错误响应:oci运行时错误:container_linux.go:265:启动容器进程导致"process_linux.go:368:容器init导致"打开/ dev/ptmx:没有这样的文件或目录\"".
我知道,对于那些试图理解的人而言,对于这一点所说的内容可能并不是很有价值VOLUME
,-v
而且它肯定不能为你想要完成的事情提供解决方案.因此,希望以下示例能够更好地阐述这些问题.
Minitutorial:指定卷
鉴于此Dockerfile:
FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2
(对于这个小型教程的结果,如果我们指定vol1 vol2
或者/vol1 /vol2
- 不要问我原因,它没有任何区别)
建立它:
docker build -t my-openjdk
跑:
docker run --rm -it my-openjdk
在容器内,运行ls
,你会发现存在两个目录; /vol1
和/vol2
.
运行容器还会在主机端创建两个目录或"卷".
让容器运行时,docker volume ls
在主机上执行,你会看到类似的东西(为了简洁起见,我用两个点替换了名称的中间部分):
DRIVER VOLUME NAME
local c984...e4fc
local f670...49f0
回到容器中,执行touch /vol1/weird-ass-file
(在所述位置创建一个空白文件).
此文件现在可在主机上的一个未命名卷lol中使用.我花了两次尝试,因为我第一次尝试了第一个列出的卷,但最终我确实在第二个列出的卷中找到了我的文件,在主机上使用此命令:
sudo ls /var/lib/docker/volumes/f670...49f0/_data
同样,您可以尝试在主机上删除此文件,它也将在容器中删除.
注意:该_data
文件夹也称为"安装点".
退出容器并列出主机上的卷.他们走了.我们--rm
在运行容器时使用了该标志,这个选项不仅有效地消除了退出时的容器,还消除了卷.
运行新容器,但使用-v
以下命令指定卷:
docker run --rm -it -v /vol3 my-openjdk
这增加了第三个卷,整个系统最终有三个未命名的卷.如果我们只指定了命令就会崩溃-v vol3
.参数必须是容器内的绝对路径.在主机端,新的第三卷是匿名的,并与其他两个卷一起驻留./var/lib/docker/volumes/
前面已经说过,Dockerfile
在运行时尝试将文件从主机引入容器时,无法映射到主机路径,这对我们造成了问题.不同的-v
语法解决了这个问题.
想象一下,我的项目目录./src
中有一个子文件夹,我希望/src
在容器内同步.这个命令可以解决问题:
docker run -it -v $(pwd)/src:/src my-openjdk
:
角色的两边都需要绝对的路径.左侧是主机上的绝对路径,右侧是容器内的绝对路径.pwd
是一个"打印当前/工作目录"的命令.将命令放入$()
括号内的命令,在子shell中运行它并返回到项目目录的绝对路径.
把它们放在一起,假设我们./src/Hello.java
在主机上的项目文件夹中有以下内容:
public class Hello {
public static void main(String... ignored) {
System.out.println("Hello, World!");
}
}
我们构建这个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文件,并在第二次运行时为另一个输出修改新消息 - 无需重建图像=)
最后的评论
我对Docker很陌生,前面提到的"教程"反映了我从一个为期3天的命令行黑客马拉松收集到的信息.我几乎感到惭愧我无法提供链接来清除支持我的陈述的英文文档,但老实说,我认为这是由于缺乏文档而不是个人努力.我知道这些示例的工作原理是使用我当前的设置,即"Windows 10 - > Vagrant 2.0.0 - > Docker 17.09.0-ce".
本教程没有解决问题"我们如何在Dockerfile中指定容器的路径,让run命令只指定主机路径".可能有办法,我还没有找到它.
最后,我有一种直觉,认为VOLUME
在Dockerfile 中指定并不常见,但它可能是永远不会使用的最佳做法VOLUME
.有两个原因.我们已经确定的第一个原因:我们无法指定主机路径 - 这是一件好事,因为Dockerfiles应该与主机的细节非常不相关.但第二个原因是人们可能忘记--rm
在运行容器时使用该选项.有人可能记得要移除容器但忘记移除卷.此外,即使拥有最佳的人类记忆,也可能需要确定哪些匿名卷可以安全删除.
@Martin非常感谢你.你的黑客马拉松及其产生的教程非常有用.
"我无法提供清除类似英文文档的链接......老实说,我认为这是由于缺乏文档".我可以证实.这是我发现的最全面,最新的文档,我一直在寻找.
`docker volume prune`可以用于清除未连接到正在运行的容器的剩余卷。并不是说仅凭id就能识别潜在的重要信息很容易...
我们什么时候应该使用未命名/匿名卷?
2> Bukharov Ser..:
官方码头教程告诉:
数据卷是绕过Union文件系统的一个或多个容器中的特殊指定目录.数据卷为持久性或共享数据提供了几个有用的功能:
创建容器时初始化卷.如果容器的基本映像包含指定安装点的
数据,则在卷
初始化时将现有数据复制到新卷中.(请注意,安装主机
目录时不适用.)
可以在容器之间共享和重用数据卷.
直接对数据卷进行更改.
更新映像时,不会包括对数据卷的更改.
即使删除容器本身,数据量仍然存在.
进入Dockerfile
您只能在容器内指定卷的目的地.例如/usr/src/app
.
当您运行容器时,docker run --volume=/opt:/usr/src/app my_image
您可以但不必在主机中指定安装点(/ opt).如果未指定--volume参数,则将自动选择安装点
3> mr haven..:
a中的VOLUME
命令Dockerfile
相当合法,完全是常规的,使用起来绝对好,而且无论如何也不会过时。只需要了解它。
我们使用它来指向容器中应用程序将写入很多目录。我们VOLUME
不仅仅因为我们想像配置文件一样在主机和容器之间共享。
该命令只需要一个参数。相对于WORKDIR
容器(如果已设置)的文件夹路径。然后docker将在其graph(/ var / lib / docker)中创建一个卷并将其安装到容器中的文件夹中。现在,该容器将可以在某处写入高性能文件。如果没有该VOLUME
命令,则写入指定文件夹的速度将非常慢,因为现在容器copy on write
在容器本身中正在使用其策略。该copy on write
策略是存在卷的主要原因。
如果在VOLUME
命令指定的文件夹上挂载,则命令永远不会运行,因为VOLUME
仅在容器启动时才执行,就像ENV
。
基本上,使用VOLUME
命令您无需外部装入任何卷即可获得性能。数据也将保存在整个容器运行中,而无需任何外部安装。然后,准备好后,只需在其上安装一些东西即可。
一些很好的示例用例:
-日志
-临时文件夹
一些不好的用例:
-静态文件
-配置
-代码
4> BMitch..:
VOLUME
在Dockerfile中指定一行会在图像上配置一些元数据,但是如何使用该元数据很重要。
首先,这两行做了什么:
WORKDIR /usr/src/app
VOLUME . /usr/src/app
WORKDIR
如果该目录不存在,该行将创建目录,并更新一些图像元数据以指定所有相对路径,以及命令的当前目录(例如RUN
将位于该位置)。VOLUME
那里的行指定了两个卷,一个是相对路径.
,另一个是/usr/src/app
,这两个卷恰好是同一目录。通常,该VOLUME
行仅包含一个目录,但可以包含多个目录,也可以是json格式的数组。
您无法在Dockerfile中指定卷源:在Dockerfile中指定卷时,常见的混乱原因是试图在映像构建时匹配源和目标的运行时语法,这将不起作用。Dockerfile只能指定卷的目的地。如果有人可以定义卷的源,这将是一个微不足道的安全漏洞,因为他们可以更新docker hub上的公共映像以将根目录安装到容器中,然后在容器内部启动后台进程作为入口点的一部分,将登录名添加到/ etc / passwd,将systemd配置为在下次重新启动时启动比特币矿工,或者在文件系统中搜索信用卡,SSN和私钥以发送到远程站点。
VOLUME系列有什么作用?如前所述,它设置一些图像元数据来表示图像内的目录是一个卷。该元数据如何使用?每次从该映像创建容器时,docker都会将该目录强制为卷。如果您在run命令或撰写文件中未提供卷,则docker的唯一选择是创建一个匿名卷。这是一个本地命名卷,其名称具有唯一的长ID,并且没有其他说明其创建原因或包含哪些数据的信息(匿名卷是数据丢失的原因)。如果覆盖该卷,则指向命名卷或主机卷,则数据将转到该卷。
VOLUME破坏了事情:一旦在Dockerfile中定义了卷,就无法禁用它。更重要的是,RUN
docker中的命令是使用临时容器实现的。这些临时容器将获得一个临时匿名卷。该匿名卷将使用您的图像内容进行初始化。您的RUN
命令在容器内进行的任何写入都将写入该卷。当RUN
命令完成时,更改图像被保存,并以匿名的体积变化被丢弃。因此,强烈建议您不要VOLUME
在Dockerfile 中定义内部。对于希望使用卷位置中的初始数据扩展图像的图像下游用户,这会导致意外的行为。
您应该如何指定音量?要指定要将卷包含在映像中的位置,请提供一个docker-compose.yml
。用户可以对其进行修改以将卷的位置调整到其本地环境,并且它可以捕获其他运行时设置,例如发布端口和网络。
有人应该记录下来!他们有。Docker在其关于Dockerfile的文档中包括有关VOLUME使用情况的警告以及在运行时指定源的建议:
从Dockerfile内更改卷:如果在声明了卷后有任何构建步骤更改了卷内的数据,则这些更改将被丢弃。
...
主机目录在容器运行时声明:主机目录(挂载点)从本质上说是依赖于主机的。这是为了保留图像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您无法从Dockerfile内挂载主机目录。该VOLUME
指令不支持指定host-dir
参数。创建或运行容器时,必须指定安装点。
5> Li-Tian..:
为了更好地理解volume
dockerfile中的指令,让我们学习mysql官方docker文件实现中的典型卷用法。
VOLUME /var/lib/mysql
参考:https :
//github.com/docker-library/mysql/blob/3362baccb4352bcf0022014f67c1ec7e6808b8c5/8.0/Dockerfile
这/var/lib/mysql
是MySQL存储数据文件的默认位置。
如果仅出于测试目的运行测试容器,则不能指定其安装点,例如
docker run mysql:8
然后mysql容器实例将使用volume
dockerfile中的指令指定的默认安装路径。这些卷是在Docker根目录中使用非常长的类似ID的名称创建的,这称为“未命名”或“匿名”卷。在基础主机系统的文件夹/ var / lib / docker / volumes中。
/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4
这对于无需指定安装点的快速测试而言非常方便,但仍可以通过将Volume用于数据存储而不是容器层来获得最佳性能。
对于正式使用,您将需要通过覆盖安装点以使用命名卷来指定安装路径,例如
docker run -v /my/own/datadir:/var/lib/mysql mysql:8
该命令将基础主机系统中的/ my / own / datadir目录作为/ var / lib / mysql装入容器中。数据目录/ my / own / datadir不会被自动删除,即使容器也被删除了。
mysql官方映像的用法:参考:https : //hub.docker.com/_/mysql/