Dockerのビルドコンテキストの外にファイルを含める方法は?


462

Dockerファイルの「ADD」コマンドを使用して、Dockerのビルドコンテキスト外のファイルを含めるにはどうすればよいですか?

Dockerドキュメントから:

パスはビルドのコンテキスト内にある必要があります。../something/somethingを追加することはできません。Dockerビルドの最初のステップは、コンテキストディレクトリ(およびサブディレクトリ)をdockerデーモンに送信することです。

この問題でDockerに対応するためだけにプロジェクト全体を再構築したくありません。すべてのDockerファイルを同じサブディレクトリに保持したい。

また、Dockerはまだシンボリックリンクをサポートしていないようです(今後もサポートしない可能性があります)。DockerfileADDコマンドは、ホスト#1676のシンボリックリンクをたどりません。

他に考えられる唯一のことは、ファイルをDockerビルドコンテキストにコピーするための事前ビルドステップを含めることです(そして、それらのファイルを無視するようにバージョンコントロールを構成します)。それよりも良い回避策はありますか?


94
これはDockerにとって最悪のことです。私の見解では、「Dockerプロジェクト」というものはありません。Dockerは出荷プロジェクト用です。その単なるツールです。プロジェクト全体を再構築して.dockerignoreなどを追加してdockerに対応する必要はありません。結局のところ、Dockerの持続時間を知っているのは誰ですか。コード(つまり、角度のあるプロジェクト)と、それを展開するためのあらゆる手段(つまり、Docker)の間に分離があると便利です。結局のところ、Dockerファイルを他のすべてのファイルの横に配置しても、実際にはメリットはありません。画像を作成するために配線するだけです:(
TigerBear

3
ええ、これは大きなダウナーです。私は同じ問題に直面しており、各Dockerビルドコンテキストにコピーしたくない、より大きなサイズのバイナリファイル(すでに圧縮されています)があります。現在の場所(Dockerビルドコンテキストの外)からソースを取得したいと思います。また、実行時にボリュームをマップしたくないので、ビルド時にファイルをコピー/追加して解凍し、特定のバイナリをイメージに焼き付けるために必要なことを実行しようとしています。このようにして、コンテナーのスピンアップは迅速です。
ジャージー豆

私は適切な構造を見つけ、stackoverflow.com
Marcello de Sales

1
Dockerビルドの問題は、「コンテキスト」の構成概念です。Dockerfileは、戦略的なディレクトリ(別名:コンテキスト)、つまり「/」に配置されていない限り、ビルドを定義するのに十分ではないため、任意のパスにアクセスできます(正しいプロジェクトでは適切ではありません)どちらか...、さらに、Dockerは開始時にコンテキスト全体をスキャンするため、Dockerのビルドが非常に遅くなります)。必要なすべてのファイルを含むDockerイメージを作成し、そこFROMから続行することを検討できます。Docker(または任意のビルドツール)に対応するためにプロジェクト構造を変更しません。
Devis L.

回答:


411

これを回避する最善の方法は、-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。あなたは使用dockerfile:してプロパティをbuild:作曲、ファイル内のセクションdocs.docker.com/compose/compose-file/#/compose-file-reference
エマーソンFarrugia

3
「Dockerfileはビルドコンテキスト内にある必要があります」というメッセージが表示されます-現在のビルドコンテキストの下に常駐できる1つのDockerfileが本当に必要です。あなたの例では、Dockerfileが現在のビルドコンテキスト内/下にあり、もちろん動作します。
Alexander Mills

3
はい、複数のサブディレクトリに対応する共有Dockerfileが必要です。これらはすべて「ビルドコンテキスト」です
Alexander Mills

51
これADDは、コンテキストディレクトリの外にあるファイルを必要とするというOPの問題を解決しますか?それが私がやろうとしていることですが、使用-fすると外部ファイルが追加可能になるとは思いません。
Sridhar Sarnobat 2017

18
私は私のドッキングウィンドウ-compose.ymlに。これは十分にupvoteすることはできません:build: context: .., dockerfile: dir/Dockerfile。これで、ビルドコンテキストが親ディレクトリになります。
Mike Gleason jr Couturier 2018

51

--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
最後に、このセキュリティ問題を克服するために、スカッシュ
Jojo

46

Linuxでは、他のディレクトリをシンボリックリンクする代わりにマウントできます

mount --bind olddir newdir

詳細については、https://superuser.com/questions/842642を参照してください。

他のOSで同様のものが利用できるかどうかはわかりません。また、Sambaを使用してフォルダーを共有し、それをDockerコンテキストに再マウントしてみたところ、同様に機能しました。


2
ルートだけがディレクトリをバインドできます
jjcf89

28

私は良いパターンを見つけ、この機能のサポートで何が起こっているかをよりよく説明する方法を見つけるために良い時間を費やしました。それを説明する最良の方法は次のとおりであることがわかりました...

  • Dockerfile:独自の相対パスにあるファイルのみを表示します
  • コンテキスト:共有するファイルとDockerfileがコピーされる「スペース」内の場所

ということで、これがDockerfileの例です。 start.sh

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コンテキストdirはruntimedirです。
    • ここでも同じメンタルモデルです。このdirの下にあるすべてのファイルが、いわゆるに移動されると考えてください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場合、yesで実行docker build .cてもアクセスできません../file-in-b。しかし、これ(または少なくとも私の場合はそうでした)の一般的な誤解は、コンテキストがDockerfileの場所ではなく、ビルドコマンドの最初の引数で指定された場所によって定義されることです。だから、受け入れ答えで述べたように:からadocker build -f a/b/c/Dockerfile . 手段Dockerfileでいること.になりましフォルダですa
β.εηοιτ.βε

1
Dockerfile docsからの引用:ファイルとディレクトリのパスは、ビルドのコンテキストのソースに対する相対パスとして解釈されます。
Nishant George Agrwal

18

問題2745の説明を読むと、Dockerがシンボリックリンクをサポートできないだけでなく、コンテキスト外でのファイルの追加をサポートできない可能性があります。Dockerビルドに入るファイルは明示的にそのコンテキストの一部であるか、おそらく固定バージョンでもデプロイされているURLからのものでなければならないという設計哲学のようですDockerコンテナ。

私はバージョン管理されたソース(つまりdocker build -t stuff http://my.git.org/repo)からビルドすることを好みます。それ以外の場合は、ランダムファイルのあるランダムな場所からビルドします。

基本的には違います...-SvenDowideit、Docker Inc

私の意見だけですが、コードとドッカーのリポジトリを分離するために再構築する必要があると思います。そうすることで、コンテナーを汎用的にして、ビルド時ではなく実行時にコードの任意のバージョンを取り込むことができます。

または、基本的なコードデプロイメントアーティファクトとして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ビルドを呼び出します...追加の作業が必要ですが、完全に制御できるため、問題なく動作します。
Sahsahae

5

画像が最初に必要とするもののtarballを作成し、それをコンテキストとして使用することもできます。

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


すばらしいヒントです。Dockerにフィードして、tarballをstdinのコンテキストとしてビルドできることもわかりましたtar zc /dir1 /dir2 |docker build -。これは私の場合非常に役に立ちました。
Tore Olsen

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を使用することになりました。1つは、コンテナーの外で必要なものなしでメインアプリケーションをビルドし、それを内部リポジトリーに公開します。次に、2番目の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

すばやく簡単な方法の1つは、ビルドコンテキストを必要なだけ多くのレベルに設定することですが、これにより結果が生じる可能性があります。次のようなマイクロサービスアーキテクチャで作業している場合:

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

ビルドコンテキストを親Codeディレクトリに設定してすべてにアクセスできますが、多数のリポジトリがあると、ビルドに時間がかかることがわかります。

状況の例として、別のチームがデータベーススキーマを保持しRepo1ていて、チームのコードがRepo2これに依存している場合があります。スキーマの変更を心配したり、他のチームのリポジトリを汚染したりせずに、この依存関係を独自のシードデータの一部とドッキングしたい場合(変更内容によっては、シードデータスクリプトを変更する必要がある場合もあります)2番目のアプローチはハッキーですがロングビルドの問題を回避します:

で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ファイルで、コンテキストをRepo2ルートとして設定し./db/schema、パスを気にせずにdockerfile のディレクトリのコンテンツを使用します。このディレクトリを誤ってソース管理にコミットするリスクを負うことになりますが、クリーンアップアクションのスクリプトは十分簡単である必要があります。


0

私の場合、私のDockerfileは、構成ファイルを使用して実際の値に置き換えるプレースホルダーを含むテンプレートのように記述されています。

したがって、このファイルを直接指定することはできませんでしたが、次のようにそれをdockerビルドにパイプします。

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

しかし、パイプが原因で、COPYコマンドは機能しませんでした。しかし、上記の方法はそれを解決し-f -ます(明示的にファイルが提供されていないと言っています)。フラグ-なしで行うだけ-fで、コンテキストとDockerfileは提供されません。これは警告です。


0

トリックは、Dockerパスを指定した場合、ビルドコマンドでコンテキストを指定して親ディレクトリのファイルを含めることができることを認識することです。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 ..
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.