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

从Docker文档中:

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

我不想重组我的整个项目,只是为了在这件事上适应Docker。我想将所有Docker文件保存在同一个子目录中。

此外,Docker似乎还不支持符号链接:Dockerfile ADD命令不支持主机#1676上的符号链接。

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


当前回答

我想今年早些时候,buildx中添加了一个功能来实现这一点。

如果你有dockerfile 1.4+和buildx 0.8+,你可以这样做

docker buildx build --build-context othersource= ../something/something .

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

ADD –from=othersource . /stuff

查看此相关帖子https://www.docker.com/blog/dockerfiles-now-support-multiple-build-contexts/

其他回答

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

例如,此命令将赋予ADD命令访问当前目录中任何内容的权限。

docker build -f docker-files/Dockerfile .

更新:Docker现在允许在构建上下文之外使用Dockerfile(已在18.03.0-ce中修复)

docker build -f ../Dockerfile .

使用链接解决问题:

ln路径/to/file/outer/context/file_to_copy/文件_to_copy

在Dockerfile上,只需:

复制file_to_COPY/path/to/file

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

mount --bind olddir newdir

看见https://superuser.com/questions/842642了解更多详情。

我不知道其他操作系统是否也有类似的功能。我还尝试使用Samba共享一个文件夹并将其重新安装到Docker上下文中,这同样有效。

该行为由docker或podman用于向构建过程呈现文件的上下文目录提供。这里有一个很好的技巧,就是在构建指令期间将上下文dir更改为要向守护进程公开的目录的完整路径。例如:

docker build -t imageName:tag -f /path/to/the/Dockerfile /mysrc/path

使用/myrc/path代替。(当前目录),您将使用该目录作为上下文,因此构建过程可以看到该目录下的任何文件。在这个示例中,您将向docker守护进程公开整个/myrc/path树。当使用docker时,触发构建的用户ID必须具有从上下文目录递归读取任何单个目录或文件的权限。

如果您有/home/user/myCoolProject/Dockerfile,但希望将不在同一目录中的文件带到此容器构建上下文中,这可能非常有用。

这里有一个使用上下文dir构建的示例,但这次使用podman而不是docker。

让我们以Dockerfile为例,在Dockerfile中有一个COPY或ADD指令,它从项目外部的目录中复制文件,例如:

FROM myImage:tag
...
...
COPY /opt/externalFile ./
ADD /home/user/AnotherProject/anotherExternalFile ./
...

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

cd /home/user/myCoolProject
podman build -t imageName:tag -f Dockefile /

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

podman build --platform linux/s390x -t myimage:mytag -f ./Dockerfile /tmp/mysrc

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

podman build --platform linux/s390x -t myimage:mytag -f ./Dockerfile ../../

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

FROM myImage:tag
...
...
COPY externalFile ./
ADD  AnotherProject ./
...

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

podman build -t imageName:tag -f ./Dockerfile /opt

在docker中将COPY或ADD与上下文目录一起使用时请注意:docker守护程序将尝试将上下文目录树上可见的所有文件“流式传输”到守护程序,这可能会降低构建速度。并要求用户具有上下文目录的递归权限。特别是在通过API使用构建时,这种行为的成本可能会更高。然而,使用podman,构建是即时进行的,不需要递归权限,这是因为podman不会枚举整个上下文目录,也不会使用客户端/服务器架构。当您使用不同的上下文目录来面对此类问题时,使用podman而不是docker来构建此类案例可能会更有趣。

一些参考文献:

https://docs.docker.com/engine/reference/commandline/build/https://docs.podman.io/en/latest/markdown/podman-build.1.html

我花了很长时间试图找出一个好的模式,以及如何更好地解释这个特性支持的情况。我意识到最好的解释方法如下。。。

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

因此,尽管如此,这里有一个Dockerfile的示例,它需要重用一个名为start.sh的文件

Dockerfile文件

它将始终从其相对路径加载,将其当前目录作为指定路径的本地引用。

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

码头组合.yaml

在本例中,共享上下文目录是运行时目录。这里的思维模式是一样的,认为这个目录下的所有文件都被移到了所谓的上下文中。同样,只需指定要复制到同一目录的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

所有服务都被设置为上下文,共享文件start.sh以及每个Dockerfile指定的Dockerfile都被复制到那里。每个人都可以按照自己的方式构建,共享开始文件!