VOLUME在Dockerfile中的实际用途是什么?

12 浏览
0 Comments

VOLUME在Dockerfile中的实际用途是什么?

首先,我想明确一下,我已经在研究这个主题上做了尽职调查。与此密切相关的是这个SO问题,它并没有真正解决我的困惑。

我理解,在Dockerfile中指定VOLUME时,这会指示Docker在容器的持续时间内创建一个未命名的卷,并将其映射到其中指定的目录。例如:

# Dockerfile
VOLUME ["/foo"]

这将在容器内创建一个用于包含存储在/foo中的任何数据的卷。通过docker volume ls查看,该卷将显示为一组随机的数字。

每次运行docker run时,此卷都不会被重用。这是引起困惑的关键点。对我来说,卷的目标是包含跨图像的所有实例(从中启动的所有容器)持久的状态。因此,如果我这样做,而没有明确指定卷映射:

#!/usr/bin/env bash
# 第一次运行容器
docker run -t foo
# 杀死容器,然后再次运行。请注意,前一个
# 卷现在将包含数据,因为在`foo`中运行的服务
# 会将数据写入该卷。
docker container stop foo
docker container rm foo
# 第二次运行容器
docker run -t foo

我希望未命名的卷在两个run命令之间被重用。然而,事实并非如此。因为我没有通过-v选项显式映射卷,所以每个run都会创建一个新的卷。

这是重要的第二部分:由于我需要显式指定-v来共享run命令之间的持久状态,那么为什么我要在Dockerfile中指定VOLUME呢?没有VOLUME,我可以这样做(使用之前的例子):

#!/usr/bin/env bash
# 创建一个用于持久状态的卷
docker volume create foo_data
# 第一次运行容器
docker run -t -v foo_data:/foo foo
# 杀死容器,然后再次运行。请注意,前一个
# 卷现在将包含数据,因为在`foo`中运行的服务
# 会将数据写入该卷。
docker container stop foo
docker container rm foo
# 第二次运行容器
docker run -t -v foo_data:/foo foo

现在,第二个容器将挂载到/foo的数据是来自上一个实例的。我可以在没有VOLUME的情况下这样做。从命令行,我可以将容器内的任何目录转换为主机上的绑定目录或Docker中的卷。

所以我的问题是:当您必须通过主机上的命令显式映射命名卷到容器时,VOLUME的意义是什么?要么是我遗漏了什么,要么这只是令人困惑和费解的。

请注意,我这里所有的断言都是基于我对Docker行为的观察以及我从文档中收集到的信息。

0
0 Comments

Dockerfile中的VOLUME指令的实际目的是什么?

VOLUME和EXPOSE等指令有点过时。像我们今天所知的命名卷在近三年前的Docker 1.9中引入。在Docker 1.9之前,运行一个镜像中有一个或多个VOLUME指令(或使用--volume选项)的容器是创建用于数据共享或持久性卷的唯一方法。事实上,以前创建数据容器是一种最佳实践,其唯一目的是保存一个或多个卷,然后使用--volumes-from选项将这些卷与应用容器共享。下面是一些描述这种过时模式的文章。

- Docker Data Containers

- Why Docker Data Containers (Volumes!) are Good

此外,还可以参考moby/moby#17798(Data-only containers obsolete with docker 1.9.0?)中讨论的从数据容器到命名卷的变化。

今天,我认为VOLUME指令是一种高级工具,只应在特殊情况下经过慎重考虑后使用。例如,官方的postgres镜像在/var/lib/postgresql/data处声明了一个VOLUME。通过将数据库数据保留在分层文件系统之外,这可以默认情况下改善postgres容器的性能。Docker不必在容器镜像的所有层中搜索位于/var/lib/postgresql/data处的文件请求。

然而,VOLUME指令确实存在一些问题。

- 用户可能不知道正在创建的未命名卷,并且在删除容器后继续占用Docker主机上的存储空间。

- 没有办法删除在Dockerfile中声明的卷。下游镜像无法向卷存在的路径添加数据。

后一个问题导致了以下问题。

- How to “undeclare” volumes in docker image?

- GitLab on Docker: how to persist user data between deployments?

对于GitLab的问题,有人想要使用预配置的数据扩展GitLab镜像进行测试,但由于父镜像中的VOLUME在/var/opt/gitlab处,所以无法在下游镜像中提交该数据。

VOLUME是在Docker 1.9之前设计的。最好不要使用它。

这基本上是答案,也就是“历史”。以前不存在命名卷,--volumes-from占主导地位。还有一个小的补充是文档,例如EXPOSE(从技术上讲不需要,因为可以根据需要进行任何操作,它告诉使用者应该是卷/持久数据的位置)。

是的,EXPOSE在容器链接(--link选项)中起到了更大的作用。我告诉开发人员今天只把它当作文档来处理。

这解释了为什么要防止某些镜像创建多余的卷,我必须将这些卷挂载为tmpfs。我能理解这些问题。如果docker废弃这些命令将会很好,例如如果在较新版本的Docker中使用这些命令,docker build将会发出警告诊断。所以,我现在知道以后要避免使用VOLUME和EXPOSE。那么,还剩一个问题:如何描述镜像的“接口”(应该映射到主机环境或其他容器中的卷/端口/等相关内容)?

我上一个问题的基础是,如果有的话,VOLUME和EXPOSE确实可以作为一种文档。您可以轻松地发现容器环境中哪些方面对外部世界是相关的。

我建议避免使用VOLUME,但EXPOSE指令对于文档很有用。与VOLUME不同,EXPOSE指令没有任何不需要的副作用。

自从我发布了这个问题后,我意识到VOLUME仍然具有功能性目的:它允许您使用镜像内部的数据初始化卷。如果我在Dockerfile中不使用VOLUME,但在docker run时仍然使用-v选项,那么当构建镜像时,我的卷是否会以容器中当前目录的内容初始化?

0