以下是我的“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文件夹中。
如果这是正确的方法,请告诉我?
官方码头工程教程说:
数据卷是绕过Union文件系统的一个或多个容器中的特殊指定目录。数据卷为持久性或共享数据提供了几个有用的功能:
- 创建容器时初始化卷。如果容器的基本映像包含指定安装点的数据, 现有数据按卷复制到新卷中 初始化。 (请注意,安装主机时不适用 目录。)
- 可以在容器之间共享和重用数据卷。
- 直接对数据卷进行更改。
- 更新映像时,不会包括对数据卷的更改。
- 即使删除容器本身,数据量仍然存在。
在Dockerfile
中,您只能指定容器内卷的目标。例如/usr/src/app
。
当你运行你的容器时, docker run --volume=/opt:/usr/src/app my_image
您可以但不必在主机上指定其安装点(/ opt)。如果未指定--volume
参数,则将自动选择安装点。
简而言之:不,你的VOLUME
指令不正确。
Dockerfile的VOLUME
指定给定容器端路径的一个或多个卷。但它不允许图像作者指定主机路径。在主机端,在Docker根目录中创建的卷具有非常长的ID类名称。在我的机器上这是/var/lib/docker/volumes
。
注意:因为自动生成的名称非常长并且从人的角度来看没有意义,所以这些卷通常被称为“未命名”或“匿名”。
你使用'。'的例子。无论我是否将点作为第一个或第二个参数,角色甚至都不会在我的机器上运行。我收到此错误消息:
docker:来自守护进程的错误响应:oci运行时错误:container_linux.go:265:启动容器进程导致“process_linux.go:368:容器init导致”打开/ dev / ptmx:没有这样的文件或目录\“”。
我知道,对于那些试图理解VOLUME
和-v
的人而言,对于这一点所说的内容可能并不是很有价值,但它肯定不能为你想要完成的事情提供解决方案。因此,希望以下示例能够更好地阐述这些问题。
鉴于此Dockerfile:
FROM openjdk:8u131-jdk-alpine
VOLUME vol1 vol2
(对于这个minitutorial的结果,如果我们指定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命令只指定主机路径”。可能有办法,我还没有找到它。
最后,我有一种直觉,在Dockerfile中指定VOLUME
不仅不常见,但它绝不是使用VOLUME
的最佳做法。有两个原因。我们已经确定的第一个原因:我们无法指定主机路径 - 这是一件好事,因为Dockerfiles应该与主机的细节非常不相关。但第二个原因是人们可能忘记在运行容器时使用--rm
选项。有人可能记得要移除容器但忘记移除卷。此外,即使拥有最好的人类记忆,也可能需要弄清楚哪些匿名卷可以安全删除。
在Dockerfile中指定VOLUME
行会在图像上配置一些元数据,但这些元数据的使用方式非常重要。
首先,这两行做了什么:
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都会强制该目录为卷。如果未在运行命令或撰写文件中提供卷,则docker的唯一选项是创建匿名卷。这是一个本地命名卷,其名称具有长唯一ID,并且没有其他指示为什么创建它或它包含哪些数据(匿名卷是数据丢失)。如果覆盖卷,指向命名卷或主机卷,则数据将转到那里。
VOLUME打破了一些事情:您无法在Dockerfile中定义后禁用卷。更重要的是,docker中的RUN
命令是使用临时容器实现的。这些临时容器将获得临时匿名卷。该匿名卷将使用您的图像内容进行初始化。从RUN
命令对容器内的任何写入都将对该卷进行。当RUN
命令完成时,将保存对图像的更改,并且将丢弃对匿名卷的更改。因此,我强烈建议不要在Dockerfile中定义VOLUME
。对于希望使用卷位置中的初始数据扩展图像的图像的下游用户,会导致意外行为。
你应该如何指定音量?要指定要在图像中包含卷的位置,请提供docker-compose.yml
。用户可以修改它以将卷位置调整到其本地环境,并捕获其他运行时设置,如发布端口和网络。
有人应该记录下来!他们有。 Docker包含有关其documentation on the Dockerfile中VOLUME用法的警告以及在运行时指定源的建议:
- 从Dockerfile中更改卷:如果任何构建步骤在声明后更改卷内的数据,那么这些更改将被丢弃。
...
- 主机目录在容器运行时声明:主机目录(mountpoint)本质上是依赖于主机的。这是为了保持图像的可移植性,因为不能保证给定的主机目录在所有主机上都可用。因此,您无法从Dockerfile中安装主机目录。
VOLUME
指令不支持指定host-dir
参数。您必须在创建或运行容器时指定安装点。
VOLUME
中的Dockerfile
命令是非常合法的,完全是传统的,绝对可以使用,并且它不会被弃用。只需要了解它。
我们使用它指向容器中的应用程序将写入很多的任何目录。我们不使用VOLUME
只是因为我们想要像配置文件一样在主机和容器之间共享。
命令只需要一个参数;从容器内部到文件夹的路径,相对于WORKDIR
(如果已设置)。然后,docker将在其图形(/ var / lib / docker)中创建一个卷,并将其挂载到容器中的文件夹中。现在容器将具有高性能的写入位置。没有VOLUME
命令,指定文件夹的写入速度将非常慢,因为现在容器正在容器本身使用它的copy on write
策略。 copy on write
策略是卷存在的主要原因。
如果挂载在VOLUME
命令指定的文件夹上,则命令永远不会运行,因为VOLUME
仅在容器启动时执行,有点像ENV
。
基本上使用VOLUME
命令可以在不外部安装任何卷的情况下获得性能。数据将在容器运行中保存,无需任何外部安装。然后当准备就绪时,只需在它上面装一些
一些很好的示例用例: - 日志 - 临时文件夹
一些不好的用例: - 静态文件 - 配置 - 代码
为了更好地理解dockerfile中的volume
指令,让我们学习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容器实例将使用默认的mount路径,该路径由dockerfile中的volume
指令指定。在Docker根目录中创建的卷具有一个非常长的ID名称,这称为“未命名”或“匿名”卷。在底层主机系统/ var / lib / docker / volumes的文件夹中。
/var/lib/docker/volumes/320752e0e70d1590e905b02d484c22689e69adcbd764a69e39b17bc330b984e4
这对于快速测试非常方便,无需指定安装点,但仍然可以通过使用Volume for data store而不是容器层来获得最佳性能。
对于正式用途,您需要通过覆盖安装点来指定安装路径以使用命名卷,例如
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/