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

How to include files outside of Docker's build context?

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

从Docker文档:

The path must be inside the context of the build; you cannot ADD
../something/something, because the first step of a docker build is to
send the context directory (and subdirectories) to the docker daemon.

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

此外,Docker似乎还不支持符号链接(可能永远也不支持):DockerFile添加命令不遵循主机1676上的符号链接。

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


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

例如,此命令将授予添加命令对当前目录中任何内容的访问权。

1
docker build -f docker-files/Dockerfile .

更新:docker现在允许将dockerfile放在构建上下文之外(在18.03.0-ce中修复,https://github.com/docker/cli/pull/886)。所以你也可以做一些像

1
docker build -f ../Dockerfile .


在Linux上,您可以挂载其他目录,而不是将它们符号化。

1
mount --bind olddir newdir

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

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


我经常发现自己为此而使用--build-arg选项。例如,在dockerfile中放入以下内容后:

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

你只需做:

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

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

警告:不建议使用生成时间变量来传递诸如github密钥、用户凭据等机密信息。使用docker history命令的映像的任何用户都可以看到生成时间变量值。


如果你阅读2745期的讨论,不仅Docker可能永远不支持符号链接,他们可能永远不支持在你的上下文之外添加文件。似乎是一种设计理念,即进入Docker构建的文件应该明确地成为其上下文的一部分,或者来自一个URL,在该URL中,它也可能以固定版本部署,这样构建就可以用Docker容器附带的已知URL或文件重复。

I prefer to build from a version controlled source - ie docker build
-t stuff http://my.git.org/repo - otherwise I'm building from some random place with random files.

fundamentally, no.... -- SvenDowideit, Docker Inc

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

或者,使用docker作为基本的代码部署工件,然后将dockerfile放在代码存储库的根目录中。如果您走这条路线,那么有一个父Docker容器来获取更一般的系统级详细信息,有一个子容器用于特定于您的代码的设置,可能是有意义的。


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

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

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

文档文件

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

1
COPY start.sh /runtime/start.sh

文件夹

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

1
2
3
4
5
6
./all-services/
   /start.sh
   /service-X/Dockerfile
   /service-Y/Dockerfile
   /service-Z/Dockerfile
./docker-compose.yaml

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

docker-compose.yaml文档

  • 在本例中,您的sharedcontext dir是runtimedir。
    • 同样的思维模式,认为这个目录下的所有文件都转移到了所谓的context上。
    • 同样,只需指定要复制到同一目录的dockerfile。您可以使用dockerfile指定。
  • 主要内容所在的目录是要设置的实际上下文。

docker-compose.yml如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
docker build -t hello-demo-app .

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

1
docker build -t hello-demo-app ..


你也可以创建一个tarball图像首先需要什么,并使用它作为你的上下文。

https://docs.docker.com/engine/reference/commandline/build//tarball上下文


使用docker compose,我通过创建一个装载我需要的卷的服务和提交容器的映像来完成这项工作。然后,在随后的服务中,我依赖于先前提交的映像,它将所有数据存储在装载位置。然后,您将不得不将这些文件复制到它们的最终目的地,因为在运行docker commit命令时,主机装载的目录不会被提交。

You don't have to use docker-compose to accomplish this, but it makes life a bit easier

1
2
3
4
5
6
7
8
9
10
11
# 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
1
2
3
4
5
6
7
8
9
10
# 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

我对一个项目和一些数据文件也有同样的问题,因为hippa的原因,我不能在repo上下文中移动。我最终使用了两个dockerfiles。其中一个构建主应用程序时没有容器外所需的内容,并将其发布到内部repo。然后,第二个dockerfile将提取该图像并添加数据,然后创建一个新图像,然后将其部署并从不存储在任何位置。不太理想,但我的目的是防止敏感信息被回购。