如何在Docker的构建上下文之外包含文件?


461

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

从Docker文档中:

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

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

此外,似乎Docker尚未(可能从未)支持符号链接:Dockerfile ADD命令未遵循主机#1676上的符号链接。

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


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

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

我发现了一个很好的结构和细节,在我解释stackoverflow.com/a/53298446/433814
马塞罗日销售

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

Answers:


412

解决此问题的最佳方法是使用-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 .

8
@Ro。您可以使用撰写文件docs.docker.com/compose/compose-file/#/compose-file-referencedockerfile:build:部分中的属性
Emerson Farrugia

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

3
是的,我只想要一个共享的Dockerfile,它对应于多个子目录,所有这些子目录都是“构建上下文”
Alexander Mills

50
这是否可以解决OP想要到ADD上下文目录之外的文件的问题?那就是我想要做的,但是我不认为使用-f使外部文件可添加。
Sridhar Sarnobat

18
不能给予好评这还不够。在我的搬运工,compose.yml我:build: context: .., dockerfile: dir/Dockerfile。现在,我的构建上下文是父目录!
Mike Gleason jr Couturier

50

我经常发现自己--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命令的映像的任何用户都是可见的。


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

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

46

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

mount --bind olddir newdir

有关更多详细信息,请参见/superuser/842642

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


2
只有root可以绑定目录
jjcf89

28

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

  • Dockerfile:只会看到其自身相对路径下的文件
  • 上下文:“空间”中您要共享的文件和Dockerfile将复制到的位置

如此说来,这是Dockerfile的一个示例,该示例需要重用一个名为 start.sh

Docker文件

ALWAYS从其相对路径加载,并以其自身的当前目录作为local您指定路径的参考。

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

  • 在此示例中, shared上下文目录是runtime目录。
    • 这里的思维模式相同,认为该目录下的所有文件都移到了所谓的 context
    • 同样,只需指定要复制到相同目录的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
  • 每个人都有自己的构建方式,共享启动文件!

干杯!


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

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

18

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

我更喜欢从版本控制的源进行构建-即docker build -t stuff http://my.git.org/repo否则,我将从某个具有随机文件的随机位置进行构建。

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

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

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


那为什么还要使用docker?
lscoughlin

11

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

因此,例如,与其给出:

docker build -t hello-demo-app .

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

docker build -t hello-demo-app ..

6
我认为这会打破.dockerignore:-\
NullVoxPopuli,

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


3

使用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

1

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


1

一个简单的解决方法是在运行卷时简单地将卷挂载(使用-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

1

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

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


0

一种快速而肮脏的方法是根据需要设置构建上下文的级别,但这可能会导致后果。如果您正在使用如下所示的微服务架构:

./Code/Repo1
./Code/Repo2
...

您可以将构建上下文设置为父级 Code目录,然后访问所有内容,但是事实证明,由于存在大量存储库,这可能导致构建花费很长时间。

一个示例情况可能是另一个团队在其中维护数据库架构,Repo1而您团队的代码在Repo2依赖于此。您想用自己的一些种子数据来实现这种依赖关系,而不必担心架构更改或污染其他团队的存储库(当然,取决于所做的更改,您可能仍需要更改种子数据脚本)。第二种方法很棘手,但解决长构建的问题:

在其中创建sh(或ps1)脚本./Code/Repo2以复制所需的文件并调用所需的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文件中,只需将上下文设置为Repo2root并使用./db/schemadockerfile中目录的内容,而无需担心路径。请记住,您将冒着意外将该目录提交给源代码管理的风险,但是脚本清理操作应该足够容易。


0

就我而言,我的Dockerfile就像模板一样包含占位符,我将使用配置文件将其替换为实际值。

所以我无法直接指定此文件,而是将其通过管道传送到docker build中,如下所示:

sed "s/%email_address%/$EMAIL_ADDRESS/;" ./Dockerfile | docker build -t katzda/bookings:latest . -f -;

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


0

诀窍是要认识到,如果您指定了Docker路径,则可以在build命令中指定上下文以包含来自父目录的文件,我将Dockerfile更改如下:

...
COPY ./ /dest/
...

然后,我的构建命令如下所示:

docker built -t TAG -f DOCKER_FILE_PATH CONTEXT

从项目目录

docker built -t username/project[:tag] -f ./docker/Dockerfile .

从项目/泊坞窗

docker built -t username/project[:tag] -f ./docker/Dockerfile ..
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.