Edited at

Gruntプロジェクトのビルド時間を短縮するDockerfileの書き方

More than 3 years have passed since last update.

株式会社リクルートテクノロジーズ 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プロジェクトを含む、すべての自作モジュールのビルドを実行する処理を書いています。


開発の進め方

このプロジェクトでの開発フローは、以下のようになります。


  1. Gruntプロジェクト内のソースを変更する

  2. 通常はgrunt-contrib-connectを使ってローカル環境で画面の確認をする

  3. ひと通り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環境作成時には、このようにキャッシュを利用して環境の準備時間を短縮できるのはメリットだと思っています。