docker - Dockerfile 中 VOLUME 的实际用途是什么?
问题描述
首先,我想明确表示我在研究这个话题时已经做了尽职调查。非常密切相关的是这个 SO question,它并没有真正解决我的困惑。
我知道当VOLUME
在 Dockerfile 中指定时,这指示 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
run
我希望在 2 个命令之间重用未命名的卷。然而,这种情况并非如此。因为我没有通过-v
选项显式映射卷,所以为每个run
.
这是重要的第 2 部分:由于我需要明确指定在命令-v
之间共享持久状态run
,我为什么要VOLUME
在我的 Dockerfile 中指定?如果没有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
一个实例的数据。我可以在没有VOLUME
Dockerfile 的情况下做到这一点。在命令行中,我可以将容器内的任何目录转换为挂载到主机上的绑定目录或 Docker 中的卷。
所以我的问题是:VOLUME
无论如何,你必须通过主机上的命令将命名卷显式映射到容器有什么意义?要么我遗漏了一些东西,要么这只是令人困惑和混淆。
请注意,我在这里的所有断言都是基于我对 docker 行为方式的观察,以及我从文档中收集到的内容。
解决方案
类似VOLUME
和EXPOSE
的指令有点不合时宜。我们今天所知道的命名卷是在大约三年前在Docker 1.9中引入的。
在 Docker 1.9 之前,运行一个容器,其镜像有一个或多个VOLUME
指令(或使用--volume
选项)是创建数据共享或持久性的卷的唯一方法。事实上,过去最好的做法是创建仅用于保存一个或多个卷的纯数据容器,然后使用该--volumes-from
选项与您的应用程序容器共享这些卷。这里有一些文章描述了这种过时的模式。
此外,请查看moby/moby#17798(仅数据容器在 docker 1.9.0 中已过时?),其中讨论了从仅数据容器到命名卷的更改。
今天,我认为该VOLUME
指令是一种高级工具,应仅用于特殊情况,并经过深思熟虑。例如,官方的 postgres 图像声明了VOLUME
at/var/lib/postgresql/data
。这可以通过将数据库数据保留在分层文件系统之外来提高开箱即用的 postgres 容器的性能。Docker 不必在容器镜像的所有层中搜索文件请求/var/lib/postgresql/data
。
但是,该VOLUME
指令确实是有代价的。
- 用户可能不知道正在创建未命名的卷,并且在删除容器后继续占用其 Docker 主机上的存储空间。
- 无法删除 Dockerfile 中声明的卷。下游映像无法将数据添加到存在卷的路径。
后一个问题会导致这样的问题。
对于 GitLab 问题,有人想使用预配置的数据扩展 GitLab 映像以用于测试目的,但由于父映像中 /var/opt/gitlab 的 VOLUME,不可能在下游映像中提交该数据。
tl;dr:VOLUME
是为 Docker 1.9 之前的世界设计的。最好把它排除在外。
推荐阅读
- aws-lambda - 一个或多个参数值无效:键 xyz 的类型不匹配预期:S 实际:M
- python - 在工作日的工作时间内绘制时间序列的百分位数
- reactjs - 使 React AppBar 和 SplitPane 适合没有滚动条
- c++ - 使用 Visual Studio 调试时如何查看 C++ 模板参数?
- html - 不存在导航项时隐藏引导汉堡图标
- python-3.7 - __init__() 在进行迁移时缺少 2 个必需的位置参数:“from_fields”和“to_fields”
- ios - 为静态 UIApplicationShortcutItem 使用 SF Symbols 系统映像
- javascript - 赛普拉斯:处理动态下拉列表以避免“TypeError:无法读取未定义的属性'selectize'”
- javascript - 当我尝试将连接字符串提供给图像 require 函数时,它会在 React Native 中引发错误
- python - Python正则表达式匹配两个字符串,如果另一个字符串不在之间