0%

Dockerのnpm installをnode_modulesのシムリンクとボリュームを使って効率化する

Node.jsアプリのDockerイメージを作っているとpackage.jsonのnpm installのときにnode-gypによるネイティブモジュールのビルドが走ることがあります。Node.jsのコードやDockerfileの構成を変更するたびにビルドされて長いときは数分かかります。なにか良い方法がないかググっているとnpm package.json and docker (mounting it…)という記事を見つけました。このソリューションを使っていまビルドに時間がかかっているMeshbluのDockerイメージ作成を修正します。

Meshblu

IoTプラットフォームのMeshbluは内部でMQTTブローカーにMoscaを使い、LebelDBなどのネイティブモジュールのビルドに時間がかかります。またMeshbluのリポジトリのはDockerfileのままではビルドに失敗してしまいます。いくつか修正しながらnode_modulesを効率的にビルドできるようにしていきます。まずgit cloneしてデフォルトのディレクトリ状態を確認します。

$ cd ~/docker_apps
$ git clone https://github.com/octoblu/meshblu.git
$ cd meshblu
$ tree -L 1
.
├── Dockerfile
├── LICENSE
├── README.md
├── app.json
├── app.yml
├── config.js
├── demo.html
├── docker
├── lib
├── meshblu.sublime-project
├── newrelic.js
├── package.json
├── public
├── server.js
└── test

Dockerイメージのビルド修正

アプリディレクトリの作成

デフォルトのリポジトリの状態だとDockerfileと同じ階層をそのまま/var/wwwにデプロイする形でイメージをビルドしています。server.jsなどのアプリケーションと、package.jsonをアプリディレクトリに移動します。このディレクトリでnpm installするので
node_modulesが配置されます。

$ mkdir -p ./var/www
$ mv demo.html lib newrelic.js public server.js test ./var/www
$ tree -L 3
.
├── Dockerfile
├── LICENSE
├── README.md
├── app.json
├── app.yml
├── config.js
├── docker
│   ├── config.js.docker
│   └── supervisor.conf
├── meshblu.sublime-project
├── package.json
└── var
└── www
├── demo.html
├── lib
├── newrelic.js
├── public
├── server.js
└── test

Dockerfileにパッケージ追加

リポジトリからcloneした状態のDockerfileでビルドするとzmq.hdns_sd.hがみつからないので失敗します。必要なパッケージをインストールします。

~/docker_apps/meshblu/Dockerfile
RUN apt-get install -y libzmq-dev libavahi-compat-libdnssd-dev

Dockerfileのディレクトリ修正

node_modulesディレクトリやSupervisor設定ファイルの管理を別にして修正後にDockerイメージを再ビルドしなくてもdocker restartできるようにします。アプリや設定をnpmやインフラのパッケージのインストールを分離することができます。

  • node_modulesをアプリディレクトリの外の/dist/node_modulesに作成して、アプリディレクトリにはシムリンクを作る
  • Dockerfileの最後にCOPYコマンドで、アプリケーションを変更してもnpmインストールが起きないようにする
  • Supervisorの設定ファイルはボリュームからアタッチするように変更する
~/docker_apps/meshblu/Dockerfile
...
#ADD . /var/www
#RUN cd /var/www && npm install
COPY ./var/www/package.json /var/www/
RUN mkdir -p /dist/node_modules && \
ln -s /dist/node_modules /var/www/node_modules && \
cd /var/www && npm install
#ADD ./docker/config.js.docker /var/www/config.js
#ADD ./docker/supervisor.conf /etc/supervisor/conf.d/supervisor.conf
...
CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/supervisord.conf"]
VOLUME /etc/supervisor/conf.d
COPY ./var/www/ /var/www

Meshbluの設定ファイルはDockerfile内でコメントアウトしたので、アプリディレクトリに移動します。

$ mv ./docker/config.js.docker ./var/www/config.js

アプリディレクトリのボリューム

最後に/var/wwwのアプリディレクトリもDockerイメージから分離してボリュームでアタッチできるようにします。Dockerホストには/dist/node_modulesディレクトリは存在しませんが、コンテナ内では存在するシムリンクを作成します。

$ ln -s /dist/node_modules ./var/www/node_modules

修正後のディレクトリ

Meshbluのリポジトリは以下のように修正しました。

$ tree -L 3
.
├── Dockerfile
├── LICENSE
├── README.md
├── app.json
├── app.yml
├── config.js
├── docker
│   └── supervisor.conf
├── meshblu.sublime-project
└── var
└── www
├── config.js
├── demo.html
├── lib
├── newrelic.js
├── node_modules -> /dist/node_modules
├── package.json
├── public
├── server.js
└── test

Dockerイメージのビルドと実行

Dockerイメージをビルドしてコンテナの起動を確認します。

$ docker build -t meshblu .
$ docker run -d --name meshblu \
-p 3000 \
-v $PWD/var/www:/var/www \
-v $PWD/docker:/etc/supervisor/conf.d \
meshblu