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

ben*_*kly 367 docker

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

从Docker文档:

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

我不想重构我的整个项目只是为了适应Docker这个问题.我想将所有Docker文件保存在同一个子目录中.

此外,似乎Docker还没有(也可能不会)支持符号链接:Dockerfile ADD命令不遵循主机#1676上的符号链接.

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

Cyb*_*wiz 340

解决此问题的最佳方法是使用-f独立于构建上下文指定Dockerfile.

例如,此命令将使ADD命令可以访问当前目录中的任何内容.

docker build -f docker-files/Dockerfile .
Run Code Online (Sandbox Code Playgroud)

更新:Docker现在允许在构建上下文之外使用Dockerfile(修复于18.03.0-ce,https://github.com/docker/cli/pull/886).所以你也可以这样做

docker build -f ../Dockerfile .
Run Code Online (Sandbox Code Playgroud)

  • 这是否解决了OP想要"添加"上下文目录之外的文件的问题?这就是我想要做的但我不认为使用`-f`使外部文件可添加. (37认同)
  • 不能upvote足够..在我的docker-compose.yml我有:`build:context:..,dockerfile:dir/Dockerfile`.现在我的构建上下文是父目录! (12认同)
  • @Ro.你在Compose文件中的`build:`部分使用`dockerfile:`属性https://docs.docker.com/compose/compose-file/#/compose-file-reference (8认同)
  • 我从一个包含大量文件的目录运行它,结果是我看到一条消息,上面写着“将构建上下文发送到 Docker deamon”,它似乎复制了千兆字节的数据。 (7认同)
  • 是的我只想要一个共享的Dockerfile,它对应多个子目录,所有子目录都是"构建上下文" (3认同)
  • 但问题仍然存在,即在上下文之外导入文件时,Dockerfile 是不够的。例如,许多 IDE 和构建工具只是获取 Dockerfile 并执行它。更好的解决方案是使用`CONTEXT` 键指向一个共同的父文件夹,尽管Docker 真的很难处理包含太多文件的上下文文件夹(例如,参见https://github.com/docker/toolbox/issues/613)。更好的解决方案是将独立/不相关的目录添加到上下文中,全部来自 Dockerfile。 (3认同)
  • @oarfish 那么你真的需要从该目录运行它吗?您可以使用 .dockerignore 文件来指定不需要的文件/子目录。 (3认同)
  • 我得到"Doc​​kerfile必须在构建上下文中" - 我真的希望有一个Dockerfile可以驻留在当前构建上下文之下.在您的示例中,您在当前构建上下文中/下方具有Dockerfile,这当然可行. (2认同)
  • @AlexanderMills我看到了你的问题.我想你有理由想在构建上下文之外使用Dockerfile吗?是否有一些你不想包含在构建上下文中的东西?也许.dockerignore(https://docs.docker.com/engine/reference/builder/#/dockerignore-file)可以提供帮助. (2认同)
  • 这只是不起作用.ADD失败:构建上下文之外的禁止路径:../webservice/npmkeys()`使用`docker build -f files/Dockerfile` (2认同)
  • @loretoparisi构建上下文包括当前目录,而不是父目录.只需在构建之前上传一个文件夹,然后在没有".."的情况下添加webservice/npmkeys (2认同)
  • 如果您尝试从Docker构建上下文之外的完全不同的文件中获取文件,那么此解决方案确实无用.即假设您的文件位于/src/my_large_file.zip下,并且您的Docker构建上下文位于/ home/user1/mydocker_project下.我不想把文件复制到Docker构建上下文,因为它很大,我想把它的一些内容烘焙到图像中,这样启动容器不是一个缓慢的过程. (2认同)
  • @oarfish我认为Docker复制构建上下文中的所有内容并将其发送到构建守护进程 - 还有构建不需要的文件,请参阅:https://github.com/moby/moby/issues/2745#issuecomment-35318051 *” ...一个非常长且资源密集的上传上下文”阶段,这似乎(非常不必要)将所有项目目录复制到临时位置“*,并且回复:*”简短的回答是 docker 客户端不会解析 Dockerfile。它 tgz 是上下文(当前目录和所有子目录),将其全部传递到服务器“* (2认同)
  • 这是一个非常基本的事情,可能会帮助其他初学者:使用此解决方案时,Dockerfile 的上下文是在哪个目录“docker build ...”启动,而不是保存 Dockerfile。您还可以使用“RUN dir -a ./”来检查上下文所在的位置。 (2认同)
  • Dockerfile 中提到的文件的路径必须位于构建命令中传递的上下文内并相对于该上下文。所以,它实际上并没有解决“包含 Docker 构建上下文之外的文件”的问题。 (2认同)

Gün*_*uer 43

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

mount --bind olddir newdir
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅https://superuser.com/questions/842642.

我不知道其他操作系统是否有类似的东西可用.我还尝试使用Samba共享一个文件夹并将其重新安装到Docker上下文中,该上下文也可以使用.

  • 只有 root 可以绑定目录 (4认同)

use*_*308 41

我经常发现自己--build-arg为此目的使用了这个选项.例如,在Dockerfile中放入以下内容之后:

ARG SSH_KEY
RUN echo "$SSH_KEY" > /root/.ssh/id_rsa
Run Code Online (Sandbox Code Playgroud)

你可以这样做:

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

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

警告:建议不要使用构建时变量来传递github密钥,用户凭据等秘密.使用docker history命令可以使映像的任何用户都可以看到构建时变量值.

  • 没有大的警告,这是一个糟糕的建议.从Docker文档:"警告:不建议使用构建时变量来传递github密钥,用户凭证等秘密.使用docker history命令,任何图像用户都可以看到构建时变量值." [1]换句话说,此示例中给出的示例公开了泊坞窗图像中的私有SSH密钥.在某些情况下,这可能没问题.https://docs.docker.com/engine/reference/builder/#arg (4认同)
  • 最后,要克服此安全问题,可以使用压扁或多阶段构建等技术:https://vsupalov.com/build-docker-image-clone-private-repo-ssh-key/ (3认同)

Mar*_*les 19

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

  • Dockerfile:只会在自己的相对路径下看到文件
  • 上下文:"空间"中的一个位置,您要共享的文件和Dockerfile将被复制到该位置

所以,说到这里,这是一个需要重用被调用文件的Dockerfile的例子 start.sh

Dockerfile

它将ALWAYS从其相对路径加载,将其自身的当前目录作为对local指定路径的引用.

COPY start.sh /runtime/start.sh
Run Code Online (Sandbox Code Playgroud)

考虑到这个想法,我们可以考虑为Dockerfiles构建特定内容的多个副本,但它们都需要访问start.sh.

./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml
Run Code Online (Sandbox Code Playgroud)

考虑到这个结构和上面的文件,这里是一个docker-compose.yml

泊坞窗,compose.yaml

  • 在此示例中,您的shared上下文目录是runtimedir.
    • 这里的心理模型相同,认为这个目录下的所有文件都被移到了所谓的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
Run Code Online (Sandbox Code Playgroud)
  • all-service设置为上下文,共享文件start.sh以及每个文件指定的Dockerfile dockerfile.
  • 每个都以自己的方式构建,共享起始文件!

干杯!

  • 您对 Dockerfile 的观点并不完全正确,正如已接受的答案所指出的那样,如果您位于文件夹层次结构“a/b/c”中,那么在“c”中运行“docker build”将不允许您访问`../b 中的文件`。但是,我认为这里的普遍误解(或者至少是我的)是上下文是由构建命令的第一个参数指定的位置定义的,而不是由 Dockerfile 的位置定义的。正如已接受的答案中所述:来自 `a`: `docker build -fa/b/c/Dockerfile 。` 表示 Dockerfile 中的 `.` 现在是文件夹 `a` (9认同)
  • 引用 Dockerfile 文档:文件和目录的路径将被解释为相对于构建上下文的源。 (2认同)

Usm*_*ail 18

如果您阅读问题2745中的讨论,不仅docker可能永远不会支持符号链接,他们可能永远不会支持在您的上下文之外添加文件.似乎是一种设计理念,即进入docker构建的文件应明确地成为其上下文的一部分,或者来自URL,其中也可能使用固定版本进行部署,以便使用随附的众所周知的URL或文件重复构建.码头工人集装箱.

我更喜欢使用版本控制源构建 - 即docker build -t stuff http://my.git.org/repo - 否则我将使用随机文件从一些随机位置构建.

从根本上说,没有.... - SvenDowideit,Docker Inc

只是我的意见,但我认为你应该重组以分离代码和docker存储库.这样,容器可以是通用的,并在运行时提取任何版本的代码而不是构建时间.

或者,使用docker作为基本代码部署工件,然后将dockerfile放在代码存储库的根目录中.如果你去这条路线可能有意义的是有一个父Docker容器用于更一般的系统级细节和一个子容器用于特定于你的代码的设置.

  • 那么为什么还要使用 docker 呢? (4认同)

isc*_*sca 15

docker此行为由或podman用于将文件呈现给构建过程的上下文目录给出。
这里的一个不错的技巧是在构建指令期间将上下文目录更改为要向守护程序公开的目录的完整路径。例如:

docker build -t imageName:tag -f /path/to/the/Dockerfile /mysrc/path
Run Code Online (Sandbox Code Playgroud)

使用/mysrc/path而不是.(当前目录),您将使用该目录作为上下文,因此构建过程可以看到该目录下的任何文件。
在这个示例中,您将把整个/mysrc/path树暴露给 docker 守护进程。
当将此与docker触发构建的用户 ID 一起使用时,必须对上下文目录中的任何单个目录或文件具有递归读取权限。

如果您拥有/home/user/myCoolProject/Dockerfile 但想要将不在同一目录中的文件引入此容器构建上下文,这可能会很有用。

这是使用 context dir 构建的示例,但这次使用podman而不是docker.

Dockerfile让我们举个例子,在你的aCOPY或指令中ADD,它从项目外部的目录复制文件,例如:

FROM myImage:tag
...
...
COPY /opt/externalFile ./
ADD /home/user/AnotherProject/anotherExternalFile ./
...
Run Code Online (Sandbox Code Playgroud)

为了构建这个,使用位于 中的容器文件/home/user/myCoolProject/Dockerfile,只需执行以下操作:

cd /home/user/myCoolProject
podman build -t imageName:tag -f Dockefile /
Run Code Online (Sandbox Code Playgroud)

一些已知的更改上下文目录的用例是使用容器作为构建源代码的工具链。
例如:

FROM myImage:tag
...
...
COPY /opt/externalFile ./
ADD /home/user/AnotherProject/anotherExternalFile ./
...
Run Code Online (Sandbox Code Playgroud)

或者它可以是相对路径,例如:

cd /home/user/myCoolProject
podman build -t imageName:tag -f Dockefile /
Run Code Online (Sandbox Code Playgroud)

这次使用全局路径的另一个示例:

FROM myImage:tag
...
...
COPY externalFile ./
ADD  AnotherProject ./
...
Run Code Online (Sandbox Code Playgroud)

请注意,现在Dockerfile 命令层中省略了COPY和的完整全局路径。ADD
在这种情况下,contex dir必须相对于文件所在的位置,如果 和externalFileAnotherProject/opt目录中,则context dir构建它的位置必须是:

podman build -t imageName:tag -f ./Dockerfile /opt
Run Code Online (Sandbox Code Playgroud)

使用COPYADD与 context dir indocker时请注意:
守护docker进程将尝试将上下文目录树上可见的所有文件“流”到守护进程,这可能会减慢构建速度。并要求用户具有上下文目录的递归权限。这种行为的成本可能会更高,特别是在通过 API 使用构建时。然而,podman构建是即时发生的,不需要递归权限,这是因为它podman没有枚举整个上下文目录,也没有使用client/server架构。当您使用不同的上下文目录遇到此类问题时,针对此类情况
的构建可能会更有趣,而不是使用。podmandocker

一些参考:

  • 这是危险的,也是不可取的。Docker 构建上下文将是您的整个机器。其一,将整个上下文发送到守护进程将花费很长时间。其次,构建过程本身可以做任何它想做的事情。恶意 Dockerfile 可以连接到具有完整文件系统读取权限的远程服务器。最后,您的 Dockerfile 指令(例如“ADD”)与您的机器紧密耦合,需要所有内容的完整路径(也称为绝对路径)。它们将不再是便携式的。 (2认同)

小智 13

我认为今年早些时候,buildx 中添加了一个功能来做到这一点。

\n

如果您有 dockerfile 1.4+ 和 buildx 0.8+,您可以执行以下操作:

\n
docker buildx build --build-context othersource= ../something/something .\n
Run Code Online (Sandbox Code Playgroud)\n

然后在你的 docker 文件中你可以使用 from 命令添加上下文

\n
ADD \xe2\x80\x93-from=othersource . /stuff\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅此相关帖子

\n


Lau*_*uet 8

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

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

  • 很棒的提示!我发现你甚至可以将 docker build 的 tarball 作为 stdin 上的上下文提供: `tar zc /dir1 /dir2 |docker build -`。这对我的情况非常有帮助。 (3认同)

Ans*_*ral 8

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

因此,例如,与其给出:

docker build -t hello-demo-app .
Run Code Online (Sandbox Code Playgroud)

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

docker build -t hello-demo-app ..
Run Code Online (Sandbox Code Playgroud)

  • 我认为这打破了.dockerignore:-\ (3认同)

小智 5

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


Roc*_*n17 5

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

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


Tre*_*ith 5

创建一个包装器 docker 构建 shell 脚本来获取文件,然后调用docker build然后删除文件。

我的快速浏览中没有提到一个简单的解决方案:

  • 有一个名为的包装脚本docker_build.sh
  • 让它创建 tarball,将大文件复制到当前工作目录
  • 称呼docker build
  • 清理 tarball、大文件等

这个解决方案很好,因为 (1.) 它没有复制 SSH 私钥带来的安全漏洞 (2.) 另一个解决方案使用了sudo bind另一个安全漏洞,因为它需要 root 权限才能执行bind