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

时间:2014-09-26 02:00:44

标签: share docker host mount

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

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

对我而言,背后的原因是在Docker中构建东西时,我不希望将这些(apt-get install)缓存锁定在单个docker中,而是共享/重用它们。这是我问这个问题的主要原因。

最新更新:

在docker v18.09之前,正确的答案应该是以:

开头的答案
  

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

然而,这是一个声明不清,有组织和支持的答案。当我重新安装我的docker包含时,我碰巧偶然发现了以下文章:

对apt-cacher-ng服务进行Docker化
https://docs.docker.com/engine/examples/apt-cacher-ng/

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

另一种方法是 新接受的答案 ,例如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"]

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

10 个答案:

答案 0 :(得分:107)

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

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

答案 1 :(得分:61)

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

好消息,现在有办法 -

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

John Yani said“IMO,它解决了Dockerfile的所有弱点,使其适合开发。”

摇臂

https://github.com/grammarly/rocker

  

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

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

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

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

  

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

答案 2 :(得分:15)

首先,回答“ 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启用构建工具。

答案 3 :(得分:12)

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

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

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

更新:

例如,

CONTAINER_ID=$(docker run -dit ubuntu:16.04)
docker cp build.sh $CONTAINER_ID:/build.sh
docker exec -t $CONTAINER_ID /bin/sh -c '/bin/sh /build.sh'
docker commit $CONTAINER_ID $REPO:$TAG
docker stop $CONTAINER_ID

答案 4 :(得分:5)

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

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

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

docker run -v /export:/export data

所以你可以使用容器内的hosts文件夹。

答案 5 :(得分:4)

我认为您可以通过docker命令运行构建来执行您想要做的事情,该命令本身在docker容器中运行。见Docker can now run within Docker | Docker Blog。例如,在探索如何Create the smallest possible Docker container | Xebia Blog时,使用了这样的技术,但实际上使用容器访问了外部的docker。

另一篇相关文章是Optimizing Docker Images | CenturyLink Labs,它解释了如果你最终在构建过程中下载了东西,你可以通过下载,构建和删除下载来避免在最终图像中浪费空间跑步。

答案 6 :(得分:2)

这很难看,但我的表情如下:

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将它们复制回图像以进行下一次构建。

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

答案 7 :(得分:2)

正如许多人已经回答的那样,无法在构建期间挂载主机卷。我只是想添加insert()的方式,我认为它会很好,主要用于开发/测试用途

Dockerfile

docker-compose

docker-compose.yml

FROM node:10
WORKDIR /app
COPY . .
RUN npm ci
CMD sleep 999999999

然后按version: '3' services: test-service: image: test/image build: context: . dockerfile: Dockerfile container_name: test volumes: - ./export:/app/export - ./build:/app/build

运行容器

答案 8 :(得分:0)

这里是使用构建和提交的两步方法的简化版本,没有shell脚本。它涉及:

  1. 无卷
  2. ,部分构建图像
  3. 运行具有卷的容器 ,进行更改,然后提交结果,替换原始图像名称。

进行相对较小的更改后,附加步骤仅会使构建时间增加几秒钟。

基本上:

docker build -t image-name . # your normal docker build

# Now run a command in a throwaway container that uses volumes and makes changes:
docker run -v /some:/volume --name temp-container image-name /some/post-configure/command

# Replace the original image with the result:
# (reverting CMD to whatever it was, otherwise it will be set to /some/post-configure/command)   
docker commit --change="CMD bash" temp-container image-name 

# Delete the temporary container:
docker rm temp-container

在我的用例中,我想预生成一个maven toolchains.xml文件,但是我的许多JDK安装都在一个卷上,直到运行时才可用。我的某些映像与所有JDKS都不兼容,因此我需要在构建时测试兼容性并有条件地填充toolchains.xml。请注意,我不需要该图像可移植,也不会将其发布到Docker Hub。

答案 9 :(得分:0)

如果您正在寻找一种“挂载”文件的方法,例如将PIDFile的{​​{1}}用于systemctl daemon-reload systemctl start teamviewerd systemctl status teamviewerd

-v

在Dockerfile中,您现在可以访问此秘密

docker run

More in-depth information about --secret available on Docker Docs