了解 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 文件夹的指令。

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

原文由 refactor 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 912
2 个回答

官方 docker 教程说:

数据卷是一个或多个容器中的一个特别指定的目录,它绕过了联合文件系统。数据卷为持久或共享数据提供了几个有用的特性:

  • 创建容器时会初始化卷。如果容器的基础镜像包含指定挂载点的数据,

将现有数据逐卷复制到新卷中

初始化。 (请注意,这在安装主机时不适用

目录。)

  • 数据卷可以在容器之间共享和重用。

  • 直接对数据卷进行更改。

  • 更新映像时不会包括对数据卷的更改。

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

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

当您运行容器时,例如 docker run --volume=/opt:/usr/src/app my_image ,您 可以 但不必在主机上指定其安装点( /opt )。如果您未指定 --volume 参数,则将自动选择安装点,通常在 /var/lib/docker/volumes/ 下。

原文由 Bukharov Sergey 发布,翻译遵循 CC BY-SA 4.0 许可协议

简而言之:不,您的 VOLUME 指令不正确。

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

注意:由于自动生成的名称非常长,从人类的角度来看毫无意义,因此这些卷通常被称为“未命名”或“匿名”。

您使用“。”的示例无论我将点作为第一个参数还是第二个参数,字符甚至都不会在我的机器上运行。我收到此错误消息:

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

我知道到目前为止所说的对于试图理解 VOLUME-v 的人来说可能不是很有价值,而且它当然不能为您尝试完成的工作提供解决方案。因此,希望以下示例能够进一步阐明这些问题。

Minitutorial:指定卷

鉴于此 Dockerfile:

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

(对于这个小教程的结果,如果我们指定 vol1 vol2/vol1 /vol2 没有区别——这是因为 Dockerfile 中的默认工作目录是 /

构建它:

 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 (在所述位置创建一个空白文件)。

该文件现在在主机上可用,位于未命名的卷之一中。我尝试了两次,因为我首先尝试了第一个列出的卷,但最终我在主机上使用以下命令在第二个列出的卷中找到了我的文件:

 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 。有两个原因。我们已经确定的第一个原因:我们不能指定主机路径——这是一件好事,因为 Dockerfile 应该与主机的细节非常无关。但第二个原因是人们在运行容器时可能会忘记使用 --rm 选项。人们可能记得删除容器但忘记删除卷。另外,即使人类记忆力最好,找出所有匿名卷中的哪一个可以安全删除也可能是一项艰巨的任务。

原文由 Martin Andersson 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题