小编典典

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

all

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

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

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

这就是我问这个问题的主要原因。我今天面临的另一个原因是试图利用来自主机的巨大私人回购,否则我必须git clone使用我的私人 ssh 密钥从
docker 内的私人回购中做,我不知道如何也没有看着呢。

最新更新:

@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

阅读 100

收藏
2022-04-21

共1个答案

小编典典

首先,回答“为什么不起作用VOLUME?” 在 Dockerfile 中定义
aVOLUME时,只能定义目标,不能定义卷的源。在构建期间,您只会从中获得一个匿名卷。该匿名卷将在每个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,或者您之前没有使用 Dockerfile
顶部的语法行启用实验性语法任何其他行,包括注释。请注意,仅当您的 docker install 内置了 buildkit 支持时,切换 buildkit
的变量才有效,这需要 Docker 的 18.09 或更高版本,无论是在客户端还是服务器上。

2022-04-21