docker
dockerfile
DockerHub

本稿では、Dockerfileを記述する際の「ちょっとしたテクニック」について紹介します。

※ 本稿は元々、取引先向けに記述した文章ですが、取引先から公開について快諾頂いたため、微調整の上ここに公開しています。
2016年8月頃に記述した文章であるため、古い情報を含んでいる可能性もありますのでご注意下さい。もし何かお気づきの点があれば、コメント、編集リクエストを頂ければ幸いです。

1. 本家の「ベストプラクティス」を読む

まずは、本家の公式ドキュメント『Dockerfile のベストプラクティス』(英語版: 『Best practices for writing Dockerfiles』に目を通しましょう。

2. 既存のDockerイメージを検索する

利用したいアプリケーション、ライブラリなどが有名な(利用者の多い)ものである場合、公式のDockerイメージが公開されている場合があります。Docker Hubで検索してみましょう。構築済みのアプリケーション、ライブラリを含むDockerイメージをベースとすることで、より手軽に、より素早く環境を構築できる可能性があります。

ただし、一般ユーザが公開しているDockerイメージは、公式のDockerイメージと比べてセキュリティ上のリスクが高いため、使用する場合は十分に注意しましょう。

3. RUNに与えるコマンド列の&&を行頭に記述する

RUNコマンドで複数のコマンド列を連続して実行する場合、a && baの終了ステータスが0ならbを実行)を
使うことが多いです。その&&を行末ではなく、行頭に書きましょう。

利点

  • コマンドが連続していることが明確になる。
  • (抽象的な表現ですが)「リズム感」が出る。

具体例

protobufをビルドする例:

RUN cd /root \
  && git clone --depth=1 -b release-0_12 --recursive https://github.com/grpc/grpc.git \
  && cd /root/grpc/third_party/protobuf \
  && ./autogen.sh \
  && ./configure --prefix="${INSTDIR_GRPC}" \
  && make -j \
  && make install

4. [Ubuntu/Debian] apt-get installに与えるパッケージリストをソートする

Dockerfile のベストプラクティス』でも紹介されていますが、apt-get installに与えるパッケージのリストをソートしておくと良いです。

利点

  • 誰が書いても同じ順序になる。
  • パッケージ名が重複することを防ぐことができる。

欠点

  • 順序による表現ができなくなる。(例えばパッケージ間の関連など)

具体例

RUN apt-get update \
  && apt-get install --yes --no-install-recommends \
    autoconf \
    build-essential \
    curl \
    git \
    libtool \
    pkg-config \
    unzip \
    zlib1g-dev \
  && rm --recursive --force /var/lib/apt/lists/*

5. [Ubuntu/Debian] apt-get installしたらapt-get cleanする

Dockerfile のベストプラクティス』でも紹介されていますが、apt-get installを実行したらapt-get cleanも実行しましょう。

利点

  • Dockerイメージサイズを小さくできる。

具体例

4. [Ubuntu/Debian] apt-get installに与えるパッケージリストをソートする』を参照のこと。

6. git clone --depth=1を使用する

GitリポジトリのHEAD、または特定のブランチをビルドしたい場合、git cloneコマンドの--depth=1
オプションを使用することで「shallow clone」(浅いクローン)することができます。また、--single-branchオプションを併用することで、より時間を短縮できる可能性があります。

利点

  • git cloneに要する時間が短縮される。
  • Gitリポジトリの取得に必要なストレージサイズが少なく済む。

欠点

  • 指定できるのばブランチ名のみ。タグ名、コミットIDは指定できない。
  • 指定したコミット数分のログしか確認できない。
  • 「shallow clone」したリポジトリに対する操作が限定される。

具体例

3. RUNに与えるコマンド列の&&を行頭に記述する』を参照のこと。

参考: 実験

実際にどの程度の時間差が生じるのかを実験してみました。6倍程度の時間差が生じていること、Counting objectsの数値がかなり小さくなっていることが確認できます。

No パターン 実時間
1 --branchのみ 0m26.482s
2 --branch+--depth=1 0m4.218s
3 --branch+--depth=1+--single-branch 0m4.965s
$ time git clone --branch=release-0_12 https://github.com/grpc/grpc.git
Cloning into 'grpc'...
remote: Counting objects: 188327, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 188327 (delta 5), reused 3 (delta 3), pack-reused 188310
Receiving objects: 100% (188327/188327), 76.43 MiB | 4.67 MiB/s, done.
Resolving deltas: 100% (141584/141584), done.
Checking connectivity... done.

real    0m26.482s
user    0m13.348s
sys 0m1.696s

$ time git clone --branch=release-0_12 --depth=1 https://github.com/grpc/grpc.git
Cloning into 'grpc'...
remote: Counting objects: 4254, done.
remote: Compressing objects: 100% (2953/2953), done.
remote: Total 4254 (delta 2115), reused 2221 (delta 1216), pack-reused 0
Receiving objects: 100% (4254/4254), 3.70 MiB | 2.61 MiB/s, done.
Resolving deltas: 100% (2115/2115), done.
Checking connectivity... done.

real    0m4.218s
user    0m0.556s
sys 0m0.240s

$ time git clone --branch=release-0_12 --depth=1 --single-branch https://github.com/grpc/grpc.git
Cloning into 'grpc'...
remote: Counting objects: 4254, done.
remote: Compressing objects: 100% (2953/2953), done.
remote: Total 4254 (delta 2115), reused 2221 (delta 1216), pack-reused 0
Receiving objects: 100% (4254/4254), 3.70 MiB | 3.10 MiB/s, done.
Resolving deltas: 100% (2115/2115), done.
Checking connectivity... done.

real    0m4.965s
user    0m0.604s
sys 0m0.220s

参考情報

7. git clone --recursiveを使用する

Gitリポジトリにサブモジュールが含まれている場合、git cloneコマンドの--recursiveオプションを
使用することで、サブモジュールも一括して取得することができます。なお、--recurse-submodulesという別名のオプションも存在します。

利点

  • サブモジュールを取得するコマンド(git submodule update --initなど)を実行する必要がない。

欠点

  • すべてのサブモジュールが必要ではない場合、余分なサブモジュールまで取得してしまう。

具体例

3. RUNに与えるコマンド列の&&を行頭に記述する』を参照のこと。

参考情報