如何在构建期间将主机卷装入Dockerfile中的docker容器中

xpt*_*xpt 214 share mount host docker

原始问题:如何在Dockerfile中使用VOLUME指令?

修订:从下面的答案更新,所以我想解决的实际问题是 - 如何在构建期间将主机卷装入Dockerfile中的docker容器,即具有该docker run -v /export:/export功能docker build.

最新更新:请参阅新接受的答案,例如,v18.09中的Buildkit.
是的:有一个解决方案 - 摇杆,不是来自Docker,但现在摇杆已经停止,我将答案还原为"不可能".

更新:所以答案是"不可能".我可以接受它作为答案,因为我知道这个问题已在https://github.com/docker/docker/issues/3156上进行了广泛讨论.我可以理解,可移植性对于docker开发者来说是一个至关重要的问题; 但作为码头用户,我不得不说我对这个缺失的功能感到非常失望.让我通过前面讨论的引用来结束我的论点:" 我想使用Gentoo作为基本图像,但是一旦构建了图像,绝对不希望> 1GB的Portage树数据存在于任何层中.如果不是因为巨大的portage树必须在安装过程中出现在图像中,那么它可能会有一些不错的紧凑型容器. "是的,我可以使用wget或curl来下载我需要的任何内容,但事实上只是一个可移植性的考虑因素现在强迫我每次构建Gentoo基本图像时下载> 1GB的Portage树既不高效也不用户友好.此外,软件包存储库将始终位于/ usr/portage下,因此在Gentoo下始终是PORTABLE.我再一次尊重这个决定,但请允许我在同一时间表达我的失望.谢谢.

原始问题详情:

通过卷共享目录
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"]
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

And*_*fan 110

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

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

  • 我可以理解可移植性的"潜在"不良副作用,但也有一个有效的用例来拥有此选项.在我的情况下,我希望能够告诉用户"移动到目录并运行'docker run'命令"将$(PWD)挂载到某个容器目录.$(PWD)确保可维护性.虽然这*可能是一个极端情况,但它将极大地帮助我为用户提供的脚本分配运行时环境. (3认同)
  • 谢谢。问题已修改。我要解决的实际问题是——如何在构建期间将主机卷挂载到 Dockerfile 中的 docker 容器中。谢谢。 (2认同)
  • 不可能.见修改后的答案. (2认同)

xpt*_*xpt 63

更新:有人不会不接受答案,我非常喜欢它,特别是对于这个特殊的问题.

好消息,现在有办法 -

解决方案是Rocker:https://github.com/grammarly/rocker

John Yani : "IMO,它解决了Dockerfile的所有弱点,使其适合开发."

摇臂

https://github.com/grammarly/rocker

通过引入新命令,Rocker旨在解决以下用例,这对于简单的Docker来说很痛苦:

  1. 在构建阶段挂载可重用卷,因此依赖关系管理工具可以在构建之间使用缓存.
  2. 与构建共享ssh密钥(用于提取私有存储库等),而不将它们留在生成的图像中.
  3. 在不同的映像中构建和运行应用程序,能够轻松地将工件从一个映像传递到另一个映像,理想情况下在单个Dockerfile中具有此逻辑.
  4. 直接从Dockerfiles标记/推送图像.
  5. 从shell构建命令传递变量,以便可以将它们替换为Dockerfile.

和更多.这些是阻碍我们在Grammarly采用Docker的最关键问题.

更新:根据Github上的官方项目回购,Rocker已经停产

截至2018年初,集装箱生态系统比三年前该项目启动时要成熟得多.现在,摇杆的一些关键和突出功能可以通过码头构建或其他支持良好的工具轻松实现,尽管某些功能仍然是摇杆独有的.有关详细信息,请参阅https://github.com/grammarly/rocker/issues/199.

  • 现在摇杆已经停产了,我再次将答案恢复为“不可能”。请参阅 OP 和所选答案。 (2认同)

BMi*_*tch 23

首先,回答"为什么不起作用VOLUME?" VOLUME在Dockerfile中定义a 时,只能定义目标,而不能定义卷的源.在构建期间,您只能从中获取匿名卷.将在每个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"]
Run Code Online (Sandbox Code Playgroud)

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


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
Run Code Online (Sandbox Code Playgroud)

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


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

# syntax = docker/dockerfile:experimental
FROM debian:latest
RUN --mount=target=/export,type=bind,source=export \
    process export directory here...
Run Code Online (Sandbox Code Playgroud)

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

  • 不幸的是,18.09 版尚不支持 Windows Buildkit (2认同)
  • 我在OSX上收到“来自守护程序的错误响应:Dockerfile分析错误行xx:未知标志:安装” (2认同)
  • 尚不支持 docker-compose,但您不需要 compose 来构建镜像。要跟踪的问题:https://github.com/moby/buildkit/issues/685 (2认同)
  • 有关此文档:https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md (2认同)

Kei*_*son 14

有一种方法可以在构建期间安装卷,但它不涉及Dockerfiles.

该技术将从您想要使用的任何基础创建容器(使用-v选项将容器装入容器中),运行shell脚本来执行映像构建工作,然后在完成后将容器作为映像提交.

这不仅会遗漏你不想要的多余文件(这对于安全文件也很好,比如SSH文件),它也会创建一个单独的图像.它有缺点:commit命令不支持所有Dockerfile指令,如果你需要编辑你的构建脚本,它就不会让你在中断时接听.

  • +1请你详细说明第2段的说明.例如,如果基础是`debian:wheezy`而shell脚本是`build.sh`,那么会使用哪些具体指令? (6认同)

Mat*_*ice 6

这很丑陋,但我实现了这样的外观:

Dockerfile:

FROM foo
COPY ./m2/ /root/.m2
RUN stuff
Run Code Online (Sandbox Code Playgroud)

图像构建.sh:

docker build . -t barImage
container="$(docker run -d barImage)"
rm -rf ./m2
docker cp "$container:/root/.m2" ./m2
docker rm -f "$container"
Run Code Online (Sandbox Code Playgroud)

我有一个 Java 版本,可以将 Universe 下载到 /root/.m2 中,并且每次都这样做。 imageBuild.sh构建后将该文件夹的内容复制到主机上,并将Dockerfile它们复制回映像以供下一次构建。

这类似于卷的工作方式(即它在构建之间持续存在)。

  • 此解决方案会为您留下一张包含 ./m2/ 中所有文件的图像 - 您需要的文件和您不需要的文件 - 这可能会导致巨大的生产图像,这是不希望的!通过安装到外部依赖项目录,只有所需的文件才会被复制到映像中。 (3认同)

Beh*_*ehe 5

在运行容器时,会创建主机上的目录并将其装入容器中.你可以找到它的目录

$ docker inspect --format "{{ .Volumes }}" <ID>
map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]
Run Code Online (Sandbox Code Playgroud)

如果要从容器中的主机安装目录,则必须使用该-v参数并指定目录.在你的情况下,这将是:

docker run -v /export:/export data
Run Code Online (Sandbox Code Playgroud)

因此,您将使用容器内的hosts文件夹.

  • **原始问题**:*如何使用Dockerfile中的VOLUME指令?*即使截至今天,它仍处于问题的最开始.你的回答是*运行时*,我的问题一直是关于*build time*,这是Dockerfile的用途. (9认同)
  • 哦,你说得对.我错过了上下文. (2认同)

Yeg*_*mba 5

正如许多人已经回答的那样,在构建期间安装主机卷是不可能的。我只是想添加docker-compose一种方式,我认为有它会很好,主要用于开发/测试用途

Dockerfile

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999
Run Code Online (Sandbox Code Playgroud)

docker-compose.yml

version: '3'
services:
  test-service:
    image: test/image
    build:
      context: .
      dockerfile: Dockerfile
    container_name: test
    volumes:
      - ./export:/app/export
      - ./build:/app/build
Run Code Online (Sandbox Code Playgroud)

并通过以下方式运行你的容器docker-compose up -d --build