原始问题:如何在Dockerfile中使用VOLUME指令?
Revised: 从下面的答案更新,所以我想解决的实际问题是 - 如何在构建期间将主机卷装入Dockerfile中的docker容器,即在 docker build
期间具有 docker run -v /export:/export
功能 .
Latest Update: 请参阅新接受的答案,例如,v18.09中的Buildkit .
Was: 有一个解决方案 - 摇杆,不是来自Docker,但现在摇杆被停止了,我再次将答案恢复到"Not possible" .
Update: 所以答案是"Not possible" . 我可以接受它作为答案,因为我知道这个问题已在https://github.com/docker/docker/issues/3156进行了广泛讨论 . 我可以理解,可移植性对于docker开发者来说是一个至关重要的问题;但作为码头用户,我不得不说我对这个缺失的功能感到非常失望 . 让我用上述讨论的引用结束我的观点:“我想使用Gentoo作为基本图像,但绝对不要因为在安装过程中必须出现在图像中的巨型portage树 . ”是的,我可以使用wget或curl来下载我需要的任何东西,但是现在仅仅是一个可移植性考虑因此迫使我每次构建Gentoo基本映像时下载> 1GB的Portage树既不高效也不方便用户 . 此外,软件包存储库将始终位于/ usr / portage下,因此在Gentoo下始终是PORTABLE . 我再一次尊重这个决定,但请允许我在同一时间表达我的失望 . 谢谢 .
原始问题详情:
从
Share Directories via Volumes
http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/
它表示数据卷功能“自Docker Remote API版本1以来一直可用” . 我的docker是版本1.2.0,但我发现上面文章中给出的示例不起作用:
# BUILD-USING: docker build -t data .
# RUN-USING: docker run -name DATA data
FROM busybox
VOLUME ["/var/volume1", "/var/volume2"]
CMD ["/usr/bin/true"]
Dockerfile中通过VOLUME命令将主机挂载的卷挂载到docker容器中的正确方法是什么?
$ apt-cache policy lxc-docker
lxc-docker:
Installed: 1.2.0
Candidate: 1.2.0
Version table:
*** 1.2.0 0
500 https://get.docker.io/ubuntu/ docker/main amd64 Packages
100 /var/lib/dpkg/status
$ cat Dockerfile
FROM debian:sid
VOLUME ["/export"]
RUN ls -l /export
CMD ls -l /export
$ docker build -t data .
Sending build context to Docker daemon 2.56 kB
Sending build context to Docker daemon
Step 0 : FROM debian:sid
---> 77e97a48ce6a
Step 1 : VOLUME ["/export"]
---> Using cache
---> 59b69b65a074
Step 2 : RUN ls -l /export
---> Running in df43c78d74be
total 0
---> 9d29a6eb263f
Removing intermediate container df43c78d74be
Step 3 : CMD ls -l /export
---> Running in 8e4916d3e390
---> d6e7e1c52551
Removing intermediate container 8e4916d3e390
Successfully built d6e7e1c52551
$ docker run data
total 0
$ ls -l /export | wc
20 162 1131
$ docker -v
Docker version 1.2.0, build fa7b24f
7 回答
首先,回答“为什么不
VOLUME
工作?”在Dockerfile中定义VOLUME
时,只能定义目标,而不能定义卷的源 . 在构建期间,您只能从此获得匿名卷 . 该匿名卷将在每个RUN
命令处安装,预填充图像的内容,然后在RUN
命令的末尾被丢弃 . 仅保存对容器的更改,而不保存对卷的更改 .由于已经提出了这个问题,因此发布了一些可能有用的功能 . 首先是多阶段构建,允许您在第一阶段构建低效的磁盘空间,并将所需的输出复制到您发布的最终阶段 . 第二个功能是Buildkit,它可以显着改变图像的构建方式,并将新功能添加到构建中 .
对于多阶段构建,您将拥有多个
FROM
行,每个行开始创建单独的图像 . 默认情况下,仅标记最后一个图像,但您可以复制前一个阶段的文件 . 标准用途是具有构建二进制或其他应用程序工件的编译器环境,以及作为复制该工件的第二阶段的运行时环境 . 你可以有:这将导致构建仅包含生成的二进制文件,而不是完整/导出目录 .
Buildkit于18.09年开始实验 . 它是构建过程的完全重新设计,包括更改前端解析器的能力 . 其中一个解析器更改已实现了
RUN --mount
选项,该选项允许您为运行命令安装缓存目录 . 例如 . 这是一个安装一些debian目录的程序(重新配置debian映像,这可以加速重新安装包):您可以为您拥有的任何应用程序缓存调整缓存目录,例如maven为$ HOME / .m2,golang为/root/.cache .
TL;DR: Answer is here: 使用
RUN --mount
语法,您还可以绑定构建上下文中的mount只读目录 . 该文件夹必须存在于构建上下文中,并且不会映射回主机或构建客户端:请注意,因为目录是从上下文安装的,所以它需要18.09或更高版本的安装并启用带有
export DOCKER_BUILDKIT=1
的构建工具包 .无法使用
VOLUME
指令告知docker what 已挂载 . 这将严重破坏可移植性 . 该指令告诉docker这些目录中的内容不会进入图像,并且可以使用--volumes-from
命令行参数从其他容器访问 . 您必须使用-v /path/on/host:/path/in/container
运行容器才能从主机访问目录 .无法在构建期间安装主机卷 . 没有特权构建,安装主机也会严重降低可移植性 . 您可能希望尝试使用wget或curl下载构建所需的任何内容并将其放置到位 .
更新:有人不会接受没有答案,我非常喜欢,特别是对于这个特殊的问题 .
好消息,现在有办法 -
解决方案是Rocker:https://github.com/grammarly/rocker
John Yani said,"IMO, it solves all the weak points of Dockerfile, making it suitable for development."
摇杆
https://github.com/grammarly/rocker
有一种方法可以在构建期间安装卷,但它不涉及Dockerfiles .
该技术将来自您想要使用的任何基础create a container(使用
-v
选项将容器装入容器中),运行shell脚本来完成图像构建工作,然后commit the container作为完成时的图像 .这不仅会遗漏你不想要的多余文件(这对于安全文件也很好,比如SSH文件),它也会创建一个单独的图像 . 它有缺点:commit命令不支持所有Dockerfile指令,如果你需要编辑你的构建脚本,它不会让你在中断时接听 .
在运行容器时,会创建主机上的目录并将其装入容器中 . 你可以找到它的目录
如果要从容器中的主机安装目录,则必须使用
-v
参数并指定目录 . 在你的情况下,这将是:因此,您将使用容器内的hosts文件夹 .
我认为您可以通过docker命令运行构建来执行您想要做的事情,该命令本身在docker容器中运行 . 见Docker can now run within Docker | Docker Blog . 这样的技术,但是实际上使用容器访问外部的docker,例如,在探索如何Create the smallest possible Docker container | Xebia Blog时使用 .
另一篇相关文章是Optimizing Docker Images | CenturyLink Labs,它解释了如果你最终在构建期间下载了东西,你可以通过在一个RUN步骤中下载,构建和删除下载来避免在最终图像中浪费空间 .
这很丑陋,但我的表情如下:
Dockerfile:
imageBuild.sh:
我有一个java版本,可以将Universe下载到/root/.m2中,并且每次都这样做 .
imageBuild.sh
在构建后将该文件夹的内容复制到主机上,并将Dockerfile
复制回映像以进行下一次构建 .这就像卷如何工作(即它在构建之间持续存在) .