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

时间:2018-09-29 15:58:12

标签: docker dockerfile

首先,我想澄清一下,我已经在研究此主题时进行了尽职调查。与this SO question密切相关,这并不能真正解决我的困惑。

我了解到,在Dockerfile中指定VOLUME时,这会指示Docker在映射到容器内指定目录的容器期间创建未命名的卷。例如:

# Dockerfile
VOLUME ["/foo"]

这将创建一个卷以包含容器内/foo中存储的所有数据。音量(当通过docker volume ls查看时)将显示为随机的数字。

每次执行docker run时,该卷均不会重复使用。这是造成混乱的关键。对我而言,卷的目标是包含在图像的所有实例(所有容器均从其启动)中持久存在的状态。所以基本上,如果我这样做,没有显式的卷映射

#!/usr/bin/env bash
# Run container for the first time
docker run -t foo

# Kill the container and re-run it again. Note that the previous 
# volume would now contain data because services running in `foo`
# would have written data to that volume.
docker container stop foo
docker container rm foo

# Run container a second time
docker run -t foo

我希望未命名的卷可以在2条run命令之间重用。然而,这种情况并非如此。因为我没有通过-v选项显式映射卷,所以将为每个run创建一个新卷。

这是重要的部分2:由于需要显式指定-vrun命令之间共享持久状态,所以为什么我还要在Dockerfile中指定VOLUME?没有VOLUME,我可以做到这一点(使用前面的示例):

#!/usr/bin/env bash
# Create a volume for state persistence
docker volume create foo_data

# Run container for the first time
docker run -t -v foo_data:/foo foo

# Kill the container and re-run it again. Note that the previous 
# volume would now contain data because services running in `foo`
# would have written data to that volume.
docker container stop foo
docker container rm foo

# Run container a second time
docker run -t -v foo_data:/foo foo

现在,确实,第二个容器将具有安装到先前实例中的/foo的数据。我无需在Dockerfile中使用VOLUME就可以做到这一点。从命令行,我可以将容器内的任何目录转换为主机上绑定目录或Docker中卷的装载。

因此,我的问题是:VOLUME的意义是,无论如何必须通过主机上的命令将命名卷显式映射到容器?我丢失了某些东西,或者这只是令人困惑和混淆。

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

1 个答案:

答案 0 :(得分:5)

VOLUMEEXPOSE之类的指令有些不合时宜。我们今天所知的命名卷是在大约三年前的Docker 1.9中引入的。

在Docker 1.9之前,运行其映像具有一个或多个VOLUME指令(或使用--volume选项)的容器是创建用于数据共享或持久化的卷的唯一方法。实际上,过去的最佳实践是创建仅数据容器,其唯一目的是保存一个或多个卷,然后使用--volumes-from选项与应用程序容器共享这些卷。这是一些描述这种过时模式的文章。

另外,请查看moby/moby#17798 (Data-only containers obsolete with docker 1.9.0?),其中讨论了从仅数据容器到命名卷的更改。

今天,我认为VOLUME是一种高级工具,应经过仔细考虑,仅用于特殊情况。例如,官方的postgres图像声明了VOLUME at /var/lib/postgresql/data。通过将数据库数据保留在分层文件系统之外,这可以提高现成的postgres容器的性能。 Docker不必在容器映像的所有层中搜索/var/lib/postgresql/data上的文件请求。

但是,VOLUME指令确实要付出一定的代价。

  • 用户可能不知道正在创建的未命名卷,并且在删除容器后继续占用其Docker主机上的存储空间。
  • 无法删除在Dockerfile中声明的卷。下游映像无法将数据添加到存在卷的路径中。

后一个问题会导致类似的问题。

对于GitLab问题,有人想使用预先配置的数据来扩展GitLab映像以进行测试,但是由于父映像中的VOLUME at /var/opt/gitlab,因此无法在下游映像中提交该数据。

tl; dr:VOLUME是为Docker 1.9之前的世界设计的。最好只保留它。