如何在构建期间将主机卷挂载到 Dockerfile 中的 docker 容器中

新手上路,请多包涵

_自 2014 年提出这个问题以来,发生了许多情况,许多事情发生了变化。我今天再次重温这个话题,我正在第 12 次编辑这个问题以反映最新的变化_。这个问题可能看起来很长,但它是按时间倒序排列的,所以最新的变化在顶部,随时停止阅读。

我想解决的问题是——如何在构建过程中将主机卷挂载到 Dockerfile 中的 docker 容器中,即在 — 期间具有 docker build docker run -v /export:/export 能力。

对我来说,背后的一个原因是,在 Docker 中构建东西时,我不希望那些( apt-get install )缓存锁定在单个 docker 中,而是共享/重用它们。

这就是我问这个问题的主要原因。我今天面临的另一个原因是试图利用来自主机的巨大私人回购,否则我必须使用我的私人 ssh 密钥从 docker 内的私人回购中执行 git clone 我不这样做’不知道怎么做,还没有调查。

最近更新:

@BMitch 回答中的 Buildkit

使用 RUN --mount 语法,您还可以从构建上下文绑定挂载只读目录…

它现在已经内置在 docker 中(我认为它是一个第三方工具),只要你的超过 18.09。我的现在是 20.10.7 - https://docs.docker.com/develop/develop-images/build_enhancements/

启用 BuildKit 构建

全新安装 docker 最简单的方法是在调用 docker build 命令时设置 DOCKER_BUILDKIT=1 环境变量,例如:

$ DOCKER_BUILDKIT=1 docker build .

否则,您将获得:

the --mount option requires BuildKit. Refer to https://docs.docker.com/go/buildkit/ to learn how to build images with BuildKit enabled

因此,如上所述,这将是我的第二个用例的完美解决方案。

截至 2019 年 5 月 7 日的更新:

在 docker v18.09 之前,正确答案应该是以下开头的那个:

有一种方法可以在构建期间挂载卷,但它不涉及 Dockerfile。

然而,这是一个表述不佳、组织有序且得到支持的答案。当我重新安装我的 docker contains 时,我偶然发现了以下文章:

Dockerize 一个 apt-cacher-ng 服务

https://docs.docker.com/engine/examples/apt-cacher-ng/

这是码头工人对这个/我的问题的解决方案,不是直接而是间接。这是 docker 建议我们做的正统方式。我承认这比我在这里试图问的要好。

另一种方式是 _新接受的答案_,例如 v18.09 中的 Buildkit。

选择适合您的。


是: 曾经有一个解决方案——rocker,它不是来自 Docker,但是现在这个 rocker 已经停产了,我再次将答案恢复为 “不可能”


旧更新: 所以答案是“不可能”。我可以接受它作为答案,因为我知道该问题已在 https://github.com/docker/docker/issues/3156 进行了广泛讨论。我可以理解,可移植性对于 docker 开发人员来说是一个最重要的问题;但作为 docker 用户,我不得不说我对这个缺失的功能感到非常失望。让我引用上述讨论中的一句话来结束我的论点:“ 我想使用 Gentoo 作为基础镜像,但绝对不希望在构建镜像后的任何层中有超过 1GB 的 Portage 树数据。你如果不是因为安装过程中必须出现在映像中的巨大的 portage 树,可能会有一些不错的紧凑容器。 “是的,我可以使用 wget 或 curl 下载我需要的任何东西,但事实上这仅仅是一个可移植性考虑现在每次构建 Gentoo 基础映像时都强制我下载 > 1GB 的 Portage 树,既不高效也不友好。此外,软件包存储库总是在 /usr/portage 下,因此在 Gentoo 下总是可移植的。再次,我尊重这个决定,但同时请允许我表达我的失望。谢谢。


详细的 原始问题

通过卷共享目录

http://docker.readthedocs.org/en/v0.7.3/use/working_with_volumes/

它说数据卷功能“从 Docker 远程 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

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

阅读 1.4k
2 个回答

首先,回答“为什么 VOLUME 不起作用?”当你在 Dockerfile 中定义一个 VOLUME 时,你只能定义目标,不能定义卷的源。在构建期间,您只会从中获得一个匿名卷。该匿名卷将在每个 RUN 命令中安装,预先填充映像的内容,然后在 RUN 命令结束时丢弃。只保存对容器的更改,而不是对卷的更改。


自从提出这个问题以来,已经发布了一些可能会有所帮助的功能。首先是多阶段构建,允许您构建磁盘空间低效的第一阶段,并将所需的输出复制到您交付的最后阶段。第二个功能是 Buildkit,它极大地改变了图像的构建方式,并且正在向构建中添加新功能。

对于多阶段构建,您将有多个 FROM 行,每行都开始创建单独的图像。默认情况下仅标记最后一个图像,但您可以从以前的阶段复制文件。标准用途是拥有一个编译器环境来构建二进制或其他应用程序工件,以及一个运行时环境作为复制该工件的第二阶段。你可以有:

 FROM debian:sid as builder
COPY export /export
RUN compile command here >/result.bin

FROM debian:sid
COPY --from=builder /result.bin /result.bin
CMD ["/result.bin"]

这将导致构建只包含生成的二进制文件,而不是完整的 /export 目录。


Buildkit 将在 18.09 结束实验。这是对构建过程的完全重新设计,包括更改前端解析器的能力。其中一项解析器更改已实现 RUN --mount 选项,该选项可让您为运行命令安装缓存目录。例如,这是一个挂载一些 debian 目录的目录(通过重新配置 debian 映像,这可以加快软件包的重新安装速度):

 # syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/var/lib/apt/lists,type=cache \
    --mount=target=/var/cache/apt,type=cache \
    apt-get update \
 && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      git

您可以针对您拥有的任何应用程序缓存调整缓存目录,例如 $HOME/.m2 用于 maven,或 /root/.cache 用于 golang。


TL;DR:答案在这里: 使用 RUN --mount 语法,您还可以从构建上下文绑定安装只读目录。该文件夹必须存在于构建上下文中,并且不会映射回主机或构建客户端:

 # syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...

请注意,因为目录是从上下文挂载的,所以它也是只读挂载的,您无法将更改推送回主机或客户端。构建时,您需要安装 18.09 或更新版本并使用 export DOCKER_BUILDKIT=1 启用 buildkit。

如果您收到不支持挂载标志的错误,则表明您没有使用上述变量启用 buildkit,或者您之前没有使用 Dockerfile 顶部的语法行启用实验语法任何其他行,包括注释。请注意,仅当您的 docker install 内置了 buildkit 支持时,切换 buildkit 的变量才有效,这需要 Docker 的 18.09 或更高版本,无论是在客户端还是服务器上。

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

不能使用 VOLUME 指令告诉 docker 挂载 什么。这将严重破坏便携性。该指令告诉 docker,这些目录中的内容不会进入图像,并且可以使用 --volumes-from 命令行参数从其他容器访问。您必须使用 -v /path/on/host:/path/in/container 运行容器才能从主机访问目录。

无法在构建期间挂载主机卷。没有特权构建和安装主机也会严重降低便携性。您可能想尝试使用 wget 或 curl 下载构建所需的任何内容并将其放置到位。

原文由 Andreas Steffan 发布,翻译遵循 CC BY-SA 3.0 许可协议

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