首页 文章

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

提问于
浏览
166

原始问题:如何在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 回答

  • 3

    首先,回答“为什么不 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"]
    

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


    Buildkit于18.09年开始实验 . 它是构建过程的完全重新设计,包括更改前端解析器的能力 . 其中一个解析器更改已实现了 RUN --mount 选项,该选项允许您为运行命令安装缓存目录 . 例如 . 这是一个安装一些debian目录的程序(重新配置debian映像,这可以加速重新安装包):

    # syntax = tonistiigi/dockerfile:runmount20180607
    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
    

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


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

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

    请注意,因为目录是从上下文安装的,所以它需要18.09或更高版本的安装并启用带有 export DOCKER_BUILDKIT=1 的构建工具包 .

  • 5

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

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

  • 63

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

    好消息,现在有办法 -

    解决方案是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

    通过引入新命令,Rocker旨在解决以下用例,这对于简单的Docker来说很痛苦:在构建阶段安装可重用卷,因此依赖管理工具可以在构建之间使用缓存 . 与构建共享ssh密钥(用于提取私有存储库等),而不将它们留在生成的图像中 . 在不同的映像中构建和运行应用程序,能够轻松地将工件从一个映像传递到另一个映像,理想情况下在单个Dockerfile中具有此逻辑 . 从Dockerfiles直接标记/推送图像 . 从shell build命令传递变量,以便将它们替换为Dockerfile . 和更多 . 这些是阻碍我们在Grammarly采用Docker的最关键问题 .

  • 9

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

    该技术将来自您想要使用的任何基础create a container(使用 -v 选项将容器装入容器中),运行shell脚本来完成图像构建工作,然后commit the container作为完成时的图像 .

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

  • 4

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

    $ docker inspect --format "{{ .Volumes }}" <ID>
    map[/export:/var/lib/docker/vfs/dir/<VOLUME ID...>]
    

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

    docker run -v /export:/export data
    

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

  • 102

    我认为您可以通过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步骤中下载,构建和删除下载来避免在最终图像中浪费空间 .

  • 6

    这很丑陋,但我的表情如下:

    Dockerfile:

    FROM foo
    COPY ./m2/ /root/.m2
    RUN stuff
    

    imageBuild.sh:

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

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

    这就像卷如何工作(即它在构建之间持续存在) .

相关问题