株式会社リクルートテクノロジーズ Advent Calendar 2014の13日目の記事です。
はじめに
あるアプリケーションのリポジトリの中に、静的ファイルを生成するためのGruntプロジェクトを含めています。
このプロジェクトをプレビューするために、リポジトリにDockerfile
を入れて、docker build
時にgrunt
コマンドでビルドしているのですが、Dockerfile
の書き方によってはちょっと更新するだけでDockerイメージの再作成に非常に時間がかかってイライラすることになります。
無駄な処理を入れないためにはADDの書き方が大事だということを学んだので、そのあたりの話を書きます。
プロジェクト構成
私の管理しているプロジェクトは、以下のようなディレクトリ構成になっています。
my-project
├── Dockerfile
├── grunt-project
│ ├── Gruntfile.js
│ ├── package.json
│ └── source
│ ├── # 省略
# 省略(Gruntプロジェクトの他に、アプリケーションのその他のモジュール郡が配備してある)
Gitリポジトリで管理しており、Gruntプロジェクト以外にも、その他独自のモジュール郡をリポジトリのルート以下に配備しています。
静的コンテンツはすべてGruntプロジェクト(上記例ではgrunt-project
ディレクトリ内)として管理しています。
また、リモートサーバでのプレビュー環境作成のために、プロジェクトルートにDockerfile
が配備してあります。
このDockerfile
内にGruntプロジェクトを含む、すべての自作モジュールのビルドを実行する処理を書いています。
開発の進め方
このプロジェクトでの開発フローは、以下のようになります。
- Gruntプロジェクト内のソースを変更する
- 通常は
grunt-contrib-connect
を使ってローカル環境で画面の確認をする - ひと通りGruntプロジェクト内の変更が完了したら、Dockerイメージを再ビルドして、1つのアプリケーションとして全体の動作確認をする
最初にやっていたDockerfileの書き方
はじめは何も考えず、以下のようにDockerfile
を書いていました。
ADD grunt-project /tmp/grunt-project
WORKDIR /tmp/grunt-project
# 必要なモジュールをインストール
RUN npm i
# ビルド実行
RUN grunt build
# ビルド後の静的ファイル群を移動
RUN cp -r /tmp/grunt-project/dist /src/static
しかし、開発を進めているとこの書き方だと無駄なビルド時間が発生することに気づきました。
Gruntプロジェクトにはpackage.json
を含めて管理するかと思いますが、実際の開発ではこのpackage.json
を変更することはまれです。
にもかかわらず、上記のようにGruntプロジェクト全体をまとめてADDしてしまうと、たとえpackage.json
の更新をしていなかったとしても毎回npm install
が実行されてしまいます。
これではDockerコンテナのキャッシュをうまく使えているとは言えません。
まともなDockerfileの書き方
そこで、Building Efficient Dockerfiles - Node.js - bitJudo を参考に以下のようにDockerfile
を書き直しました。
# package.jsonだけを先にADDする
# このようにすることによって、package.jsonが更新されていない場合はnpm installの部分はキャッシュが使われる
ADD grunt-project/package.json /tmp/grunt-project/package.json
WORKDIR /tmp/grunt-project
# 必要なモジュールをインストール
RUN npm i
# Gruntプロジェクトのその他をファイルを、ビルド実行するディレクトリとは別の場所にもう一度ADD
ADD grunt-project /tmp/grunt-project-2
# ファイルを-nで上書きせずにでコピー
RUN cp -nr /tmp/grunt-project-2/* /tmp/grunt-project
# ビルドするために必要なドットファイルなども一緒にコピー
RUN cp -nr /tmp/grunt-project-2/.[^.]* /tmp/grunt-project
# ビルド実行
RUN grunt build
# ビルド後の静的ファイル群を移動
RUN cp -r /tmp/grunt-project/dist /src/static
先にpackage.json
をADDして、後からGruntプロジェクトのその他のファイルをADDして上書きせずにコピーすることによって、npm install
のキャッシュが使えるようになりました。
まとめ
Gruntプロジェクトに限らず、DockerはADDするタイミングをうまく使うことによってキャッシュ機能を活かすことができます。
特にローカル環境でのQA環境作成時には、このようにキャッシュを利用して環境の準備時間を短縮できるのはメリットだと思っています。