【Docker使えるようになりたい】【#9 ビルドコンテキスト & COPY】ビルドコンテキストとCOPY命令について

DockerとDocker Daemonとビルドコンテキスト

Docker image build .は、「Dockerfile」と「ビルドコンテキスト」から、イメージファイルを生成するコマンドであると言えます。

.でDockerfileのあるディレクトリを指定していると以前の記事で解説しましたが、実際には、Docker Daemonと呼ばれるDockerの処理を行う裏で実行されているプロセスに対して、指定したディレクトリ以下のファイル、フォルダを全てコピーして送信しています。

なぜ、こんなことをするかというと、Dockerの設計思想が深く関わっています。

Dockerはそのコンテナを動作させるデバイスが「ローカルでもリモートでもいいよう」に、ホストクラアント方式を採用しています。

その都合上、buildコマンドを実行すると、実際に処理をするDocker Daemonに対して、指定したビルドコンテキストディレクトリ以下のフォルダ、ファイルをコピーして送信しています。

後述するCOPY命令などでは実際にファイルをイメージに入れる必要があり、リモートにある可能性のあるDocker Daemonにコピーを渡しておかないとファイルを見つけることができないからです。

COPYを使ってホストOS上のファイルをイメージにコピーする : COPY

コンテナ内で必ず使うようなファイルをイメージに事前にコピーすることができます。
(ホストOS上のソースファイルをイメージ内でコンパイルしたい時など)

当然イメージにコピーするのでそこから生成されるコンテナは全ては、build時にコピーしたファイルが含まれることになります。

残したくない場合は、build時に対象のファイルが使い終わったら削除すればいい

ホストOS上のファイルをイメージにコピーするには以下の命令を使用します。

COPY {追加元} {追加先}

追加元 : ローカルのパス
追加先 : コンテナ内のパス

また、以下の特徴もあります。

  • ファイルはもちろん、ディレクトリごとのコピーも可能です。
  • 追加元のディレクトリのパスの指定は、ビルドコンテキストとして指定したフォルダからの相対パスになる
  • コピー先のコンテナ内に存在しないディレクトリを指定した場合でも、自動で生成される

例 : ホストOS上にあるtest.txtファイをイメージにコピーする

以下のディレクトリ構成を想定します。

DockerTest
├── project/  <---カレントディレクトリ
   └── Dockerfile
└── test.txt

test.txtDockerTest/にあり、DockerfileはDockerTest/project/にあります。Dockerfileの内容は次のとおりです。

FROM ubuntu
COPY ../test.txt /test.txt
CMD ["cat", "/test.txt"]

この状態でビルドを試みます。

cd /home/user/project/
docker build -t test-image .

すると、次のエラーが発生します。

COPY failed: Forbidden path outside the build context: ../test.txt ()

エラーの原因

これは、Docker Daemonにファイルがコピーされていないため、デーモンがコピー対象のファイルを見つけられないことが原因です

このエラーは、COPY命令でビルドコンテキストの外にある../test.txtを指定しているためです。Dockerはビルドコンテキスト(docker buildコマンドを実行したディレクトリ)内のファイルしか参照できません。

正しい方法

test.txtをビルドコンテキスト内に移動またはコピーすることで解決できます。

  1. test.txtprojectディレクトリにコピーします。
DockerTest
└── project/  <---カレントディレクトリ
    └── Dockerfile
    └── test.txt
  1. Dockerfileを修正します。
   FROM ubuntu
   COPY test.txt /test.txt
   CMD ["cat", "/test.txt"]
  1. 再度ビルドを実行します。
   docker build -t test-image .
  1. コンテナを実行してtest.txtの内容を表示します。
   docker run --rm test-image

これで、test.txtの内容が正しく表示されます。

ほかの解決策

カレントディレクトリをDockerTestに移動させて、

DockerTest <---カレントディレクトリ
├── project/  
   └── Dockerfile
└── test.txt

Dockerfileの場所を明示的に教えることでファイル構造は変えずにエラーなくイメージを生成することもできます。

docker image build .に追加で-f {Dockerfileまでの相対パス}を記述することで問題を解決できます。

docker image build -f ./project/Dockerfile .

さいごに

Dockerの基本的な仕組みを理解していないとbuild時に何でここでエラーになるんだよというのが多発しような部分だと思います。

かといって、ビルドコンテキストを広げて何でもかんでもDocker Daemonに送ってしまうのは無駄なことなので、必要最小限のファイルをどうすれば構造を崩さずにイメージを構築できるかを考えるのが大切かと思いました

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA