如何使用Docker文件中的“ ADD”命令从Docker构建上下文之外包含文件?

来自Docker文档:


路径必须处于构建环境中;您不能添加
../something/something,因为docker构建的第一步是
将上下文目录(和子目录)发送到docker守护程序。


我不想为了适应Docker而重组我的整个项目。我想将所有Docker文件都保存在同一子目录中。

此外,看来Docker尚未(而且可能永远不会)支持符号链接:Dockerfile ADD命令未遵循主机上的符号链接# 1676。

我唯一想到的另一件事是包括一个预构建步骤,以将文件复制到Docker构建上下文中(并配置我的版本控件以忽略那些文件)。有没有比这更好的解决方法了?

评论

这一定是Docker最糟糕的事情。从我的角度来看,没有“ Docker项目”之类的东西。 Docker用于运送项目。它只是一个工具。我不想重建整个项目来容纳docker,添加.dockerignore等。最终,谁知道Docker会持续多久?最好在代码之间(例如,角度项目)与部署它的任何方式(例如,docker)之间进行分隔。毕竟,在所有其他文件旁边放置一个docker文件确实没有任何好处。它只是为了创建图像而将其连接起来:(

是的,这真是令人沮丧。我面临着同样的问题,我有一个较大的二进制文件(已压缩),不想复制到每个Docker构建上下文中。我宁愿从其当前位置(在Docker构建上下文之外)获取它。而且我不想在运行时映射卷,因为我试图在构建时复制/添加文件并解压缩并执行所需的操作,以便将某些二进制文件烘焙到映像中。这样可以快速旋转容器。

我找到了一个很好的结构,并在stackoverflow.com/a/53298446/433814
上进行了详细解释。
docker构建的问题是“上下文”的虚构概念。 Dockerfile不足以定义构建,除非将它们放置在战略目录(即上下文)下,即极端的“ /”下,因此您可以访问任何路径(请注意,在合理的项目中这样做不正确要么...,再加上它使docker的构建速度非常慢,因为docker在启动时会扫描整个上下文。您可以考虑使用所有必需的文件构建docker映像,并使用FROM从那里继续。我不会更改项目结构以适应Docker(或任何构建工具)。

#1 楼

解决此问题的最佳方法是使用-f指定与构建上下文无关的Dockerfile。

例如,此命令将为ADD命令提供对当前目录中任何内容的访问权限。 />
docker build -f docker-files/Dockerfile .


更新:Docker现在允许将Dockerfile置于构建上下文之外(已在18.03.0-ce中修复,https://github.com/docker/cli/pull/ 886)。因此,您也可以执行类似的操作

docker build -f ../Dockerfile .


评论


@Ro。您在撰写文件docs.docker.com/compose/compose-file/#/compose-file-reference的build:部分中使用dockerfile:属性

–艾默生·法鲁吉亚(Emerson Farrugia)
16年7月9日在10:35



我得到“ Dockerfile必须在构建上下文中”-我真的很想拥有一个可以驻留在当前构建上下文之下的Dockerfile。在您的示例中,Dockerfile在当前构建上下文内/下方,这当然可以工作。

–亚历山大·米尔斯(Alexander Mills)
17年2月4日在9:38

这是否解决了OP想要添加上下文目录之外的文件的问题?这就是我要尝试的操作,但是我不认为使用-f可以使外部文件可添加。

– Sridhar Sarnobat
17年9月19日在2:25

无法对此进行足够的投票..在我的docker-compose.yml中,我具有:构建:上下文:..,dockerfile:dir / Dockerfile。现在,我的构建上下文是父目录!

–迈克·格里森(Mike Gleason)和小库图里(Courutier)
18年7月12日在12:49



我正在从包含大量文件的目录中运行此文件,结果是正在查看一条消息,该消息显示将构建上下文发送到Docker守护进程,并且似乎复制了千兆字节的数据。

– ar鱼
19年8月14日在9:40

#2 楼

我经常发现自己将--build-arg选项用于此目的。例如,将以下内容放入Dockerfile:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa


您可以这样做:

docker build -t some-app --build-arg SSH_KEY="$(cat ~/file/outside/build/context/id_rsa)" .


但是请注意Docker文档中的以下警告:

警告:建议不要使用构建时变量来传递诸如github密钥,用户凭证等秘密信息。任何用户都可以看到构建时变量值docker history命令对图像进行处理。

评论


这是不明智的建议,没有发出很大的警告。来自Docker文档:“警告:不建议使用构建时变量来传递诸如github密钥,用户凭据等机密。构建时变量值对于使用docker history命令的映像的任何用户都是可见的。” [1]换句话说,此示例中给出的示例在docker映像中公开了私有SSH密钥。在某些情况下,这可能很好。 docs.docker.com/engine/reference/builder/#arg

–谢尔顿
19年1月16日在16:26

最后,要解决此安全问题,可以使用压扁或多阶段构建等技术:vsupalov.com/build-docker-image-clone-private-repo-ssh-key

– Jojo
19年8月15日在13:35

#3 楼

在Linux上,您可以挂载其他目录,而不用符号链接它们

 mount --bind olddir newdir
 


请参见https:// superuser.com/questions/842642了解更多详细信息。

我不知道其他操作系统是否可以使用类似的软件。
我还尝试使用Samba共享文件夹并将其重新安装到效果很好的Docker上下文。

评论


只有root可以绑定目录

– jjcf89
19年6月13日在15:04

#4 楼

我花了很多时间试图找出一个好的模式,以及如何更好地解释此功能支持所发生的情况。我意识到,解释它的最好方法如下...

Dockerfile:将仅在其相对路径下看到文件
上下文:在“空间”中您放置文件的位置想要共享,您的Dockerfile将被复制到

因此,这就是一个示例,该Dockerfile需要重用一个名为start.sh的文件
,它将始终从其相对路径加载,将其自身的当前目录作为您指定路径的本地引用。
COPY start.sh /runtime/start.sh

文件
考虑到这个想法,我们可以考虑为Dockerfile创建多个副本构建特定的东西,但是它们都需要访问start.sh
./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

考虑到此结构和上面的文件,这是docker-compose.yml
docker-compose.yaml

在此示例中,您的共享上下文目录是运行时目录。

这里的思维模型相同,请认为该目录下的所有文件都移至s类似地,只需指定要复制到该目录的Dockerfile。您可以使用dockerfile进行指定。


主要内容所在的目录是要设置的实际上下文。

docker-compose.yml如下
/>
 version: "3.3"
services:
  
  service-A
    build:
      context: ./all-service
      dockerfile: ./service-A/Dockerfile

  service-B
    build:
      context: ./all-service
      dockerfile: ./service-B/Dockerfile

  service-C
    build:
      context: ./all-service
      dockerfile: ./service-C/Dockerfile
 



all-service被设置为上下文,共享文件start.sh以及Dockerfile也被复制到那里由每个dockerfile指定。
每个都可以以自己的方式构建,共享启动文件!


评论


正如您接受的答案所指出的那样,您对Dockerfile的观点并不完全正确,如果您位于文件夹层次结构a / b / c中,则可以运行docker build。在C中不允许您访问../file-in-b。但是,我认为对此(或至少是我的)普遍的误解是,上下文是由build命令的第一个参数指定的位置而不是Dockerfile的位置定义的。因此,如接受的答案所述:来自a:docker build -f a / b / c / Dockerfile。意味着在Dockerfile中。现在是文件夹

–β.εηοιτ.βε
19年2月21日在10:26

引用Dockerfile文档:文件和目录的路径将被解释为相对于构建上下文源的相对路径。

– Nishant George Agrwal
19/12/18在8:59



#5 楼

如果您阅读问题2745中的讨论,不仅docker可能永远都不支持符号链接,他们可能永远也不支持在您的上下文之外添加文件。似乎是一种设计理念,进入docker build的文件应该明确地是其上下文的一部分,或者也应该来自URL(假定它也以固定版本进行部署),以便该构建可以与众所周知的URL或随Docker一起提供的文件重复进行。泊坞窗容器。


我更喜欢从版本控制的源进行构建-即docker build
-t的东西http://my.git.org/repo-否则,我将从

从根本上说,不....-Docker Inc. SvenDowideit


我的看法是,但我认为您应该重组为分离出代码和docker存储库。这样,容器可以是通用的,并且可以在运行时而不是构建时引入任何版本的代码。

或者,将docker用作您的基本代码部署工件,然后将dockerfile放入代码存储库的根目录中。如果您采用这种方法,那么有一个父Docker容器(用于更一般的系统级详细信息)和一个子容器(用于针对您的代码的设置)可能是有意义的。

评论


那为什么还要使用docker?

–lscoughlin
6月2日12:33

#6 楼

我认为更简单的解决方法是更改​​“上下文”本身。

因此,例如,与其给出:

docker build -t hello-demo-app .


,它将当前目录设置为上下文,假设您想要父目录作为上下文,只需使用:

docker build -t hello-demo-app ..


评论


我认为这打破了.dockerignore:-\

– NullVoxPopuli
18 Mar 9 '18 at 23:25

我放弃了.dockerignore,而是制作了Makefile管理的docker文件夹,该文件夹仅包含构建上下文所需的文件...我只需要调用make build,它会提取所有需要更新的文件,然后调用适当的docker build。 ..我需要做一些额外的工作,但是由于我完全可以控制,它可以完美地工作。

– ah哈哈
19-11-26在9:33



#7 楼

您还可以创建首先需要图像的tarball并将其用作上下文。

https://docs.docker.com/engine/reference/commandline/build/#/tarball-contexts

评论


大提示!我发现您甚至可以将docker build tarball作为上下文输入到stdin:tar zc / dir1 / dir2 | docker build-。这对我来说非常有帮助。

–托尔·奥尔森(Tore Olsen)
5月9日10:54

#8 楼

如此GitHub问题所述,构建实际上发生在/tmp/docker-12345中,因此类似../relative-add/some-file的相对路径是相对于/tmp/docker-12345的。因此,它将搜索/tmp/relative-add/some-file,该错误消息中也显示了该错误。*

不允许包含来自构建目录外部的文件,因此将导致出现“禁止的路径”消息。

#9 楼

使用docker-compose,我通过创建一个安装所需卷的服务并提交容器映像来完成此任务。然后,在后续服务中,我依靠先前提交的映像,该映像将所有数据存储在安装位置。然后,您将不得不将这些文件复制到它们的最终目标,因为在运行docker commit命令时不会挂载主机安装的目录

您不必使用docker-compose来完成此操作,但是它使生活更轻松

 # docker-compose.yml

version: '3'
  services:
    stage:
      image: alpine
      volumes:
        - /host/machine/path:/tmp/container/path
      command: bash -c "cp -r /tmp/container/path /final/container/path"
    setup:
      image: stage
 

 # setup.sh

# Start "stage" service
docker-compose up stage

# Commit changes to an image named "stage"
docker commit $(docker-compose ps -q stage) stage

# Start setup service off of stage image
docker-compose up setup
 


#10 楼

一个简单的解决方法可能是在运行卷时将卷(使用-v或--mount标志)简单地装入容器并以这种方式访问​​文件。

示例:

docker run -v /path/to/file/on/host:/desired/path/to/file/in/container/ image_name


有关更多信息,请参见:https://docs.docker.com/storage/volumes/

评论


请注意,这仅在卷是运行时依赖项时才有效。对于构建时间依赖性,docker run为时已晚。

–user3735633
2月17日23:39

#11 楼

由于HIPAA的原因,我在项目和某些数据文件中遇到了相同的问题,因此无法在回购环境中移动。我最终使用了2个Dockerfile。其中一个构建了主应用程序,而没有我在容器外部需要的东西,并将其发布到内部仓库中。然后,第二个dockerfile提取该映像并添加数据,并创建一个新映像,然后将其部署且从未存储在任何地方。这并不理想,但出于我的目的,将敏感信息排除在存储库之外。

#12 楼

链接的解决方法:
ln path/to/file/outside/context/file_to_copy ./file_to_copy
在Dockerfile上,只需:
COPY file_to_copy /path/to/file

#13 楼

一种快速而肮脏的方法是根据需要将构建上下文设置为多个级别,但这会产生后果。
如果您正在使用如下所示的微服务体系结构:
./Code/Repo1
./Code/Repo2
...

您可以将构建上下文设置到父Code目录,然后访问所有内容,但事实证明,使用大量存储库,这可能导致构建花费很长时间。
示例情况可能是另一个团队在Repo1中维护数据库模式,而您的团队在Repo2中的代码取决于此。您想用自己的一些种子数据来实现这种依赖性,而不必担心架构更改或污染其他团队的存储库(当然,根据更改的内容,您可能仍然不得不更改种子数据脚本)
这种方法很笨拙,但却解决了构建时间过长的问题:
./Code/Repo2中创建sh(或ps1)脚本来复制所需的文件并调用所需的docker命令,例如:
 #!/bin/bash
rm -r ./db/schema
mkdir ./db/schema

cp  -r ../Repo1/db/schema ./db/schema

docker-compose -f docker-compose.yml down
docker container prune -f
docker-compose -f docker-compose.yml up --build
 

在docker-compose文件中,只需将上下文设置为Repo2根,并使用dockerfile中./db/schema目录的内容,而无需担心路径。
请记住,您将冒着将这个目录意外提交到源代码管理的风险,但是脚本清理操作应该足够容易。

#14 楼

就我而言,我的Dockerfile就像一个包含占位符的模板编写,我将使用我的配置文件将其替换为实际值。
因此,我无法直接指定此文件,而是将其通过管道输送到Docker构建中,如下所示:
 sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;
 

但是由于管道的原因,COPY命令不起作用。但是上述方法可以通过-f -(明确地说没有提供文件)来解决。仅执行不带-标志的-f,未提供上下文和Dockerfile,这是一个警告。