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

时间:2014-11-21 19:11:28

标签: docker

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

来自Docker文档:

  

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

我不想重构我的整个项目只是为了容纳Docker。我想将所有Docker文件保存在同一个子目录中。

此外,似乎Docker还没有(也可能不会)支持符号链接:Dockerfile ADD command does not follow symlinks on host #1676.

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

15 个答案:

答案 0 :(得分:40)

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

mount --bind olddir newdir

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

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

答案 1 :(得分:37)

为此,我经常发现自己使用--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 documentation的以下警告:

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

答案 2 :(得分:18)

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

  

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

     

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

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

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

答案 3 :(得分:11)

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

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

因此,这是一个需要重用名为start.sh

的文件的Dockerfile示例。

Dockerfile

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被复制到那里。
  • 每个人都可以用自己的方式构建,共享开始文件!

干杯!

答案 4 :(得分:6)

我认为更简单的解决方法是改变背景'本身。

所以,例如,而不是给予:

docker build -t hello-demo-app .

将当前目录设置为上下文,让我们说您希望将父目录作为上下文,只需使用:

docker build -t hello-demo-app ..

答案 5 :(得分:4)

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

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

答案 6 :(得分:2)

您还可以先创建图像所需内容的tarball,然后将其用作上下文。

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

答案 7 :(得分:2)

使用链接的解决方法:

ln path/to/file/outside/context/file_to_copy ./file_to_copy

在 Dockerfile 上,只需:

COPY file_to_copy /path/to/file

答案 8 :(得分: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/

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

答案 11 :(得分:0)

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

./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目录的内容,而无需担心路径。 请记住,您将冒着将该目录意外提交到源代码管理的风险,但是脚本清理操作应该足够容易。

答案 12 :(得分:0)

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

因此我无法直接指定此文件,而是将其通过以下方式通过管道传递到docker构建中:

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

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

答案 13 :(得分:0)

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

我的快速浏览中未提及的简单解决方案:

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

这个解决方案很好,因为 (1.) 它没有复制您的 SSH 私钥所带来的安全漏洞 (2.) 另一个解决方案使用 sudo bind 所以那里有另一个安全漏洞,因为它需要 root bind 的权限。

答案 14 :(得分:0)

如何在两个 Dockerfile 之间共享 typescript 代码

我遇到了同样的问题,但在两个 typescript 项目之间共享文件。其他一些答案对我不起作用,因为我需要保留共享代码之间的相对导入路径。我通过像这样组织我的代码来解决它:

api/
  Dockerfile
  src/
    models/
      index.ts

frontend/
  Dockerfile
  src/
    models/
      index.ts

shared/
  model1.ts
  model2.ts
  index.ts

.dockerignore

注意:将共享代码提取到该顶级文件夹后,我避免了需要更新导入路径,因为我更新了 api/models/index.tsfrontend/models/index.ts 以从共享导出:(例如export * from '../../../shared)

由于构建上下文现在是上一级目录,我不得不进行一些额外的更改:

  1. 更新构建命令以使用新的上下文:

    docker build -f Dockerfile ..(两个点而不是一个)

  2. 顶级使用单个 .dockerignore 来排除所有 node_modules。 (例如**/node_modules/**

  3. Dockerfile COPY 命令前加上 api/frontend/

  4. 复制 shared(除了 api/srcfrontend/src

    WORKDIR /usr/src/app
    
    COPY api/package*.json ./     <---- Prefix with api/
    RUN npm ci
    
    COPY api/src api/ts*.json ./  <---- Prefix with api/
    COPY shared usr/src/shared    <---- ADDED
    RUN npm run build
    

这是我可以将所有内容发送到 docker 的最简单方法,同时保留两个项目中的相对导入路径。棘手(烦人)的部分是由构建上下文向上一级目录引起的所有更改/后果。