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.h
とdns_sd.h
がみつからないので失敗します。必要なパッケージをインストールします。
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の設定ファイルはボリュームからアタッチするように変更する
...
# 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