Help us understand the problem. What is going on with this article?

.dockerignore アンチパターン

More than 1 year has passed since last update.

.dockerignoreとは

Dockerfileからイメージをビルドする場合、Dockerfileの存在するディレクトリの中身はtarで固められdaemonへと送られます。

$ ls -la
total 2097168
drwxr-xr-x  5 muni  staff         170  3  7 12:40 .
drwxr-xr-x  8 muni  staff         272  3 10 15:19 ..
-rw-r--r--  1 muni  staff          76  2 16 17:04 Dockerfile
-rw-r--r--  1 muni  staff          18  2 16 10:10 docker-compose.yml
-rw-r--r--  1 muni  staff  1073741824  2 16 10:04 dummy.file
$ cat Dockerfile
FROM debian:jessie

このようなimageに含まないファイルも送信するため、

$ time docker build .
Sending build context to Docker daemon 1.074 GB
Step 1 : FROM debian:jessie
 ---> 9a02f494bef8
Successfully built 9a02f494bef8
docker build .  12.28s user 2.98s system 49% cpu 30.589 total #<= slow...

ビルドに余計な時間がかかってしまいます。
そこで .dockerignore の出番です。

.dockerignoreは.gitignoreのようにdockerで無視するファイルを指定することができるファイルです。

.dockerignore
dummy.file

上記のようなファイルをDockerfileと同じディレクトリに置くだけで

$ time docker build .
Sending build context to Docker daemon 4.096 kB
Step 1 : FROM debian:jessie
 ---> 9a02f494bef8
Successfully built 9a02f494bef8
docker build .  0.01s user 0.01s system 37% cpu 0.055 total

1GBの無駄なファイルに時間をとられることなくイメージをビルドできるようになります。

またnode_modulesやvendor/assetsのようなイメージのビルド時に追加されるディレクトリも.dockerignoreに追加しておくことで、

Dockerfile
FROM node

ADD . /src #<= node_modulesを除く全ファイルを/srcに配置する
WORKDIR /src

RUN npm install

CMD ["/usr/local/bin/node", "index.js"]

このようにDockerfileを書けるようになります。

本題

.dockerignoreによってビルドが遅くなる

先ほど.dockerignoreを配置することでイメージのビルド時間の短縮が見込めると言いましたが、そうでない場合もあります。

$ ls
Capfile      Guardfile    bin          import       package.json spec
Dockerfile   Rakefile     config       lib          public       vendor
Gemfile      Vagrantfile  config.ru    log          readme.md
Gemfile.lock app          db           node_modules recipes

このようなRailsのプロジェクトに

.dockerignore
vendor/assets

上記のようなファイルを配置してビルド時間を比較してみましょう。

.dockerignore配置前

$ time docker build .
Sending build context to Docker daemon 1.885 MB
Step 1 : FROM debian:jessie
 ---> 9a02f494bef8
Successfully built 9a02f494bef8
docker build .  0.05s user 0.02s system 42% cpu 0.169 total

.dockerignore配置後

$ echo vendor/assets > .dockerignore
$ time docker build .
Sending build context to Docker daemon 1.883 MB #<= -0.002MB
Step 1 : FROM debian:jessie
 ---> 9a02f494bef8
Successfully built 9a02f494bef8
docker build .  0.10s user 0.04s system 71% cpu 0.188 total #<= +0.019s???

何故かビルド時間が増加してしまいました...

原因

.dockerignore の実装は Go の filepath.WalkRegexp.MatchString です。
つまり Dockerfile が存在するディレクトリをルートとして、ディレクトリをトラバースしながらファイル名が .dockerignore にマッチするかどうかを正規表現によって確認している処理によって実現しています。

実際にフローを文字に書き起こすと以下のようになります。

  1. .dockerignore をパースする
  2. .dockerignore にかかれている文字から正規表現を作る
  3. ディレクトリをトラバーサルする
    • 正規表現がディレクトリにマッチした場合: ディレクトリを Ignore 対象とし、そのディレクトリをスキップする
    • 正規表現がファイルにマッチした場合: ファイルを Ignore 対象とする
  4. 3. を繰り返す

これはプロジェクトのファイル数(ベンダー含む)が多くなるに連れパフォーマンスが低下するということを表しています。

また

正規表現がディレクトリにマッチした場合: ディレクトリを Exclude 対象とし、そのディレクトリをスキップする

は Ignore 対象のディレクトリの中だけど Docker image に含めたいケースで利用する ! が .dockerignore に存在する場合は行なわれません。

つまり ! があると必ず .dockerignore のオーダーは O(kN) になります。
(k: .dockerignore のパターンの数、N: ルートからトラバーサルするファイルの数)

アンチパターン

.gitignoreのように汎用的なものを列挙するのではなく、必要な物だけ記載するようにしましょう。

Dockerfile_bad
# archives
*.zip
*.lzh
*.tar.gz
*.tgz
*.bz2
*.dmg

# OSX
.DS_Store #<= 汎用的なものは記載しない
.Spotlight-V100
.Trashes
.AppleDB
.AppleDesktop
.apdisk

# Rails
.rspec
log
tmp
db/*.sqlite3
db/*.sqlite3-journal
vendor/assets

# Node
node_modules
Dockerfile_good
.rspec
log
tmp
db/*.sqlite3
db/*.sqlite3-journal
vendor/assets
node_modules

また ! をつかわなくていいケースであれば追加しないようにしましょう。

まとめ

大きなプロジェクトでは.dockerignoreによるビルドの速度低下が発生する可能性があります。

.dockeringnoreあり .dockerignore無し
--no-cache=true 14m18.878s 13m15.641s
--no-cache=false 1m7.326s 7.931s

(464M ファイル数10207のRailsアプリケーションのdocker buildの時間比較)

.dockerignoreを一度見なおしてみてはいかがでしょうか?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした