本稿では、Dockerfile
を記述する際の「ちょっとしたテクニック」について紹介します。
※ 本稿は元々、取引先向けに記述した文章ですが、取引先から公開について快諾頂いたため、微調整の上ここに公開しています。
2016年8月頃に記述した文章であるため、古い情報を含んでいる可能性もありますのでご注意下さい。もし何かお気づきの点があれば、コメント、編集リクエストを頂ければ幸いです。
1. 本家の「ベストプラクティス」を読む
まずは、本家の公式ドキュメント『Dockerfile のベストプラクティス』(英語版: 『Best practices for writing Dockerfiles』に目を通しましょう。
2. 既存のDockerイメージを検索する
利用したいアプリケーション、ライブラリなどが有名な(利用者の多い)ものである場合、公式のDockerイメージが公開されている場合があります。Docker Hubで検索してみましょう。構築済みのアプリケーション、ライブラリを含むDockerイメージをベースとすることで、より手軽に、より素早く環境を構築できる可能性があります。
ただし、一般ユーザが公開しているDockerイメージは、公式のDockerイメージと比べてセキュリティ上のリスクが高いため、使用する場合は十分に注意しましょう。
3. RUN
に与えるコマンド列の&&
を行頭に記述する
RUN
コマンドで複数のコマンド列を連続して実行する場合、a && b
(a
の終了ステータスが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に与えるコマンド列の&&を行頭に記述する』を参照のこと。