InfraAcademy

InfraAcademy Blog

dockerignoreとは?Dockerfileでコピーしたくないファイルやディレクトリを指定する

| #Docker

こんにちは、フルスタックエンジニアのryuです。

「dockerignoreってなに?」とお困りの方!今回の記事ではdockerignoreについて解説します。

dockerignoreとは、DockerfileのCOPYコマンドでコピーしたくないファイルやディレクトリを指定するファイルのことです。ビルドに不要なファイルを除外することで、ビルド時間の短縮やイメージサイズの最適化、機密情報の漏洩防止につながります。

Dockerを使っている方やDocker初心者の方必見の内容です!

では、dockerignoreについて詳しく解説します!

dockerignoreとは?

コピーしたくないファイルやディレクトリを指定するファイル

まずは、dockerignoreがどんな役割を持つファイルなのかを整理していきます。

dockerビルドに必要ない(tarの中に入れたくない)ファイルは.dockerignoreファイルに記載します。

Dockerfileからイメージをビルドする場合、Dockerfileの存在するディレクトリの中身はtarで固められdaemonへと送られます。DockerfileのCOPYコマンドを使用して、必要なファイルをコピーします。

つまり、.dockerignoreに書いたファイルはDocker側へ送られなくなります。COPYの対象から外れるだけでなく、転送そのものから除外される点がポイントです。

DockerfileやCOPYコマンドの基本から確認したい方は、Dockerfileの書き方を解説した記事を先に読んでみてください。

ビルドコンテキストの仕組みを理解しよう

.dockerignoreを理解するうえで欠かせないのが、ビルドコンテキストという考え方です。聞き慣れない言葉かもしれませんが、仕組みはシンプルですよ。

ビルドコンテキストとは、docker buildコマンドの引数で指定するディレクトリ一式のことです。docker build .と実行した場合は、カレントディレクトリ全体がビルドコンテキストになります。

Dockerはビルドを始める前に、このディレクトリの中身をまるごとtarにまとめてDocker daemonへ転送します。COPYやADDでイメージにコピーできるのは、この転送されたファイルだけです。

ここで問題になるのが、ビルドに関係ないファイルまで一緒に転送されてしまうことです。node_modulesや大量のログファイルがあると、転送だけで時間がかかってしまいますよね。

そこで.dockerignoreの出番です。転送したくないファイルをあらかじめ指定しておけば、ビルドコンテキストを小さく保てます。

dockerignoreを使う3つの効果

.dockerignoreを作成することで、ビルド時間の短縮、docker imageサイズの最適化などの効果があります。主な効果を表に整理すると、次のようになります。

効果 内容
ビルド時間の短縮 転送されるファイルが減り、コンテキストの送信が速くなる
イメージサイズの最適化 不要なファイルがCOPYされなくなり、イメージが軽くなる
機密情報の漏洩防止 .envや秘密鍵などがイメージに入り込む事故を防げる

特に見落とされがちなのが、3つ目の機密情報の保護です。COPY . .のようにディレクトリごとコピーするDockerfileでは、.envのようなファイルまでイメージに焼き込まれてしまいます。

イメージはレジストリで共有されたり、チームのメンバーへ配布されたりするものです。.envや秘密鍵などの機密ファイルは、.dockerignoreで必ず除外しておきましょう。

しかも、一度イメージのレイヤーに入った機密情報は、後のRUN命令で削除してもレイヤーの履歴に残り続けます。だからこそ、そもそもDocker側へ送らないという対策が重要になるわけです。

dockerignoreの書き方

ファイル名と配置場所

では、実際にdockerignoreを作成してみましょう。dockerignoreは.dockerignoreというファイル名で作成します。

ファイル名の先頭にドットが付く、隠しファイル形式である点に注意してください。名前が1文字でも違うと、まったく読み込まれません。

配置場所にもルールがあります。dockerignoreは原則として、ビルドコンテキストルートにある.dockerignoreファイルしか読み込まれません。

サブディレクトリにある.dockerignoreファイルは読み込まれません。そのため、.dockerignoreファイルはビルド時にルートとなるディレクトリに配置しておきましょう。

なお、記事の後半で紹介するDockerfile専用のignoreファイルだけは、この原則の例外になります。まずは、コンテキストのルートに置くと覚えておけば大丈夫です。

パターンを書くときの基準も、このコンテキストルートになります。パスの基準という考え方に自信がない方は、絶対パスと相対パスの解説記事を読んでおくと理解がスムーズですよ。

dockerignoreの記述例

書く場所が分かったところで、実際に中身を書いてみましょう。ファイルの中身は以下のように記述します。

# コメント
*/temp*
*/*/temp*
temp?

例えば、node_modulesをコピーしたくない場合は、以下のように記述します。

node_modules

このようにDockerのビルドに不必要なファイルやディレクトリは、.dockerignoreに記述します。1行に1パターンずつ並べるだけなので、書き方自体はとても簡単ですよね。

dockerignoreの文法一覧

.dockerignoreのパターンは、いくつかの記号を組み合わせて表現します。主な文法を表にまとめました。

ルール 挙動
#コメント コメントとして無視される
*/temp* ルート直下のサブディレクトリ内で、tempで始まる名前のファイルやディレクトリを除外
*/*/temp* ルートから2階層下のサブディレクトリ内で、tempで始まる名前のファイルやディレクトリを除外
temp? ルートディレクトリ内で、tempと任意の1文字が一致する名前のファイルやディレクトリを除外
**/temp 階層の深さに関係なく、tempという名前のファイルやディレクトリを除外
!temp.txt 除外の例外を指定する(temp.txtは除外しない)

引用:Dockerfile リファレンス — Docker-docs-ja 20.10 ドキュメント

パターンの解釈は、Go言語のfilepath.Matchのルールに従います。とはいえ、まずは表の6パターンをおさえておけば実務では十分です。

この中で特に重要なのが、**!の2つです。順番に補足しますね。

**は、階層の深さに関係なくマッチする特別なワイルドカードです。たとえば**/*.logと書けば、どの階層にあるログファイルもまとめて除外できます。

!は、除外ルールに例外を作るための記号です。*.mdで除外した後に!README.mdと書けば、README.mdだけは転送の対象に残せます。

ルール同士が競合した場合は、より後ろの行に書いたルールが優先されます。例外の!は、対応する除外ルールより後ろに書くと覚えておきましょう。

実務でよく除外するファイルの例

文法が分かったところで、次は実際に何を除外すべきかを見ていきます。プロジェクトによって差はありますが、除外候補の定番はある程度決まっています。

除外候補 除外する理由
.git 過去の履歴まで含まれてサイズが大きく、ビルドには不要
node_modules イメージの中でインストールし直すため、転送が無駄になる
.env APIキーやパスワードなどの機密情報が漏洩する恐れがある
*.log 実行時のログはビルドに不要で、サイズも大きくなりがち
テストコードやドキュメント コンテナの実行時には使われない

まずは.gitnode_modules.envの3つから除外を検討するのがおすすめです。この3つを外すだけでも、転送量と安全性が大きく改善します。

Node.jsプロジェクトの記述テンプレート

イメージをつかみやすいように、Node.jsプロジェクトを例にした.dockerignoreを紹介します。そのままコピーして、プロジェクトに合わせて調整してみてください。

# バージョン管理
.git
.gitignore

# 依存パッケージ(イメージ内でインストールし直す)
node_modules
npm-debug.log

# 機密情報
.env
*.pem

# ドキュメント(README.mdだけ残す)
*.md
!README.md

node_modulesを除外するのは、ホスト側とイメージ側で環境が異なるからです。依存パッケージはDockerfileの中でnpm installを実行し、イメージ側で入れ直すのが基本になります。

Pythonプロジェクトなら、__pycache__venv*.pycあたりが定番の除外候補です。使っている言語に合わせて読み替えてみてください。

gitignoreとの違いと効かないときの原因

.dockerignoreを書いたのに効かない、という悩みは意外と多いです。原因の多くは、.gitignoreと同じ感覚で書いてしまうことにあります。

2つのファイルは見た目がそっくりですが、細かい仕様が異なります。主な違いを表で整理しました。

比較項目 .gitignore .dockerignore
役割 Gitの管理対象から外す ビルドコンテキストから除外する
置ける場所 各サブディレクトリにも置ける コンテキストルートのみ(原則)
node_modulesと書いた場合 どの階層のnode_modulesにもマッチ ルート直下のnode_modulesだけにマッチ
相互の連携 なし .gitignoreの内容は引き継がれない

深い階層のファイルにマッチしない

いちばん多いつまずきが、パターンの基準の違いです。.dockerignoreのパターンは、常にコンテキストルートからの相対パスとして解釈されます。

たとえばnode_modulesと書いた場合、除外されるのはルート直下のnode_modulesだけです。packages/app/node_modulesのような深い階層にあるものは除外されません。

どの階層でもマッチさせたいときは、**/node_modulesと書きましょう。.gitignoreとの一番大きな違いなので、ここだけでもしっかりおさえておきたいところです。

配置場所やファイル名が間違っている

パターンが正しいのに効かない場合は、ファイルの場所と名前を疑いましょう。前述のとおり、.dockerignoreはビルドコンテキストのルートに置く必要があります。

docker build -f docker/Dockerfile .のようにDockerfileをサブディレクトリに置く構成でも、通常の.dockerignore.で指定したコンテキスト側のルートに置きます。Dockerfileの隣に置いても読み込まれないため、注意してください。

例外指定の順番が間違っている

!による例外が効かないケースは、順番のミスがほとんどです。ルールは上から評価され、最後にマッチした行が優先されます。

!README.mdを先に書いて、その後ろに*.mdを書くと、READMEまで除外されてしまいます。例外は必ず、対応する除外ルールより後ろの行に書きましょう。

また、ディレクトリを丸ごと除外すると、その中のファイルを!でうまく戻せないことがあります。例外を使う予定があるなら、dirではなくdir/**のようにパターン形式で除外しておくのが安全です。

dockerignoreが効いているか確認する方法

.dockerignoreを書き終えたら、実際に効いているかを確認してみましょう。特別なツールは必要なく、ビルド時の出力を見るだけで確認できます。

docker buildを実行すると、出力の序盤に次のような行が表示されます。

=> [internal] load build context
=> => transferring context: 2.15kB

このtransferring contextに表示されているのが、daemonへ転送されたビルドコンテキストのサイズです。.dockerignoreを書く前と後でこの数値を見比べれば、除外が効いているかどうか一目で分かります。

node_modulesのような大きなディレクトリを除外すると、転送量が桁違いに小さくなることも珍しくありません。ビルドの体感速度も変わってくるので、ぜひ確認してみてください。

あわせて、完成したイメージのサイズはdocker imagesコマンドで確認できます。COPYされる範囲が変わった場合は、イメージサイズの変化にも効果が現れます。

Dockerfileごとにdockerignoreを分ける方法

最近のDockerには、Dockerfile単位でignoreファイルを切り替える仕組みも用意されています。1つのリポジトリに複数のDockerfileがある場合に便利な機能です。

使い方は、Dockerfileと同じディレクトリに<Dockerfile名>.dockerignoreという名前のファイルを置くだけです。たとえばproduction.Dockerfile用なら、production.Dockerfile.dockerignoreという名前になります。

ルートの.dockerignoreと両方が存在する場合は、Dockerfile専用のignoreファイルのほうが優先されます。この仕組みは、現在のDockerで標準ビルダーとなっているBuildKitで利用できます。

開発用と本番用でDockerfileを分けているプロジェクトでは、除外ルールも別々に管理できて重宝します。モノレポ構成で、サービスごとにビルドを分けたい場合にも活躍しますよ。

dockerignoreに関するよくある疑問

最後に、dockerignoreについて初心者の方がつまずきやすい疑問へ順番に答えていきます。

Dockerfile自体もdockerignoreに書ける?

Dockerfileや.dockerignore自身を、除外の対象として書くことはできます。ビルドの動作にも支障はありません。

この2つはビルド処理に必要なため、除外を書いてもdaemonへは送られます。ただし、COPYやADDでイメージの中へコピーすることはできなくなる点だけ覚えておきましょう。

docker composeを使う場合にも有効?

docker composeでビルドする場合にも、.dockerignoreはそのまま有効です。composeのbuild設定で指定したコンテキストのルートにある.dockerignoreが読み込まれます。

composeそのものの基本を確認したい方は、docker-composeとdockerコマンドの違いを解説した記事をご覧ください。

ボリュームマウントしたファイルにも効く?

.dockerignoreが働くのは、あくまでビルドのときだけです。docker run -vやcomposeのvolumesで共有されるファイルには、一切影響しません。

除外したはずのnode_modulesがコンテナ内に見える場合は、マウントで持ち込まれていないかを確認してみてください。ビルド時と実行時の違いを意識すると、混乱しなくなりますよ。

.gitignoreがあれば.dockerignoreは不要?

Dockerが.gitignoreを自動で読み込むことはありません。Gitで無視しているファイルでも、ビルドコンテキストにはそのまま含まれてしまいます。

内容が重複していても、.dockerignoreは別途作成しましょう。前述のとおり記法も微妙に違うため、そのままコピーして使う場合は深い階層のパターンに注意が必要です。

dockerignoreとは?まとめ

今回の記事では、dockerignoreについて解説しました。dockerignoreを有効に使って、ビルドの時間を短縮してみましょう!

.dockerignoreはビルドコンテキストから不要なファイルを除外するファイルで、ビルド時間の短縮やイメージサイズの最適化、機密情報の保護に役立つのでしたね。まずは.gitとnode_modules、.envの除外から始めて、transferring contextのサイズで効果を確かめてみてください。

Dockerfileの理解をさらに深めたい方は、EXPOSEでリッスンポートを指定する方法もあわせてどうぞ。コンテナ技術をさらに広く学びたい方には、Kubernetesを1から解説した入門記事もおすすめです。

当ブログでは、このようなITに関する記事を発信しているので興味のある方は引き続きご覧ください。Docker入門シリーズの続きは、Docker入門講座一覧から読み進められます。

インフラエンジニアへの転職を考えている方には、インフラエンジニアにオススメの転職サイトを解説した記事も参考になります。動画で体系的に学びたい方は、Udemyのオススメ講座を紹介した記事をチェックしてみてください。

Dockerを書籍でじっくり学びたい方には、次の2冊もおすすめです。

Next Action

記事で読んだ内容を、講座で実装してみましょう

InfraAcademyでは、ブラウザ上でLinuxやネットワークの実践環境を使いながら学習できます。無料で始められる講座から、学習の流れを試せます。

この記事を書いた人

ryu

InfraAcademy運営 / エンジニア

エンジニア歴10年。Linux、ネットワーク、クラウドを中心に、実務で役立つインフラ技術を初心者にもわかりやすく解説しています。

X: @ryu63614894

Related

関連記事

ブログ一覧へ

Roadmap

まずはこの4講座から

ログインすれば無料で始められる講座です。気になったテーマから手を動かして学べます。

講座一覧を見る