Dockerコンテナ内でのnpm installを改善してみる

開発やCIでやDockerイメージをbuildする際にnpm installすると、cacheがないため時間がかかる。
これはpackageの数が多くなると結構辛いことになるので、なんとかできないかと試してみた。

以下のような適当なpackageを使って試してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"name": "example-project",
"version": "0.0.1",
"description": "example",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "kazu69",
"license": "MIT",
"dependencies": {
"express": "^4.13.4",
"react": "^15.0.1"
},
"devDependencies": {
"babel": "^6.5.2",
"browserify": "^13.0.0",
"eslint": "^2.8.0"
}
}

Alpine Linuxを利用する

そもそものDcokerイメージを最小にしておくこと。
Alpine Linuxでイメージを作成する。

1
2
3
4
5
6
# application.dockerfile
FROM alpine
RUN apk add --update nodejs bash

WORKDIR /var/
ADD ./package.json .

この状態で計測する。

1
2
3
4
5
time npm i

real 0m29.258s
user 0m0.000s
sys 0m0.000s

Install Progressを無効化する

npm install時にProgress Barを出すと遅くなるので、非表示にする。

1
2
3
4
5
time npm i --production --no-progress

real 0m28.150s
user 0m0.010s
sys 0m0.000s

SSLを利用しない

もし可能ならばSSLを使わない、proxyをつかってhttpのregistryを使う。

1
2
3
4
5
time npm i --no-progress --registry http://registry.npmjs.org/

real 0m26.405s
user 0m0.000s
sys 0m0.000s

Cache Container を使う

キャッシュコンテナを利用するために、簡単なpackageをキャッシュするだけなコンテナを作成。

1
2
3
4
5
6
7
8
# cache.dockerfile
FROM alpine
RUN apk add --update nodejs bash

WORKDIR /var/
ADD ./package.json .
RUN npm i
VOLUME /var/node_modules

このコンテナのnode_modulesディレクトリをマウントする。

1
2
3
4
5
6
7
8
docker build -t cache -f cache.dockerfile .
docker run -d --name cache_volume -it cache:latest /bin/bash

docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
621b7fe67bc8 cache:latest "/bin/bash" 3 seconds ago Up 2 seconds cache_volume

docker run --volumes-from cache_volume -it ci:latest /bin/bash
1
2
3
4
5
time npm i

real 0m2.270s
user 0m0.000s
sys 0m0.000s

CIなどでDockerを使っている場合はcacheコンテナをサポートしていると思うので、
cacheコンテナを利用すると圧倒的に改善できそう。

毎回キャッシュコンテナにinstallしてるのつらいので、package.jsonに変更ある場合のみキャッシュコンテナをupdateする必要がある。

1
2
3
git diff --name-only master $(git rev-parse --abbrev-ref HEAD) | grep -q package.json
test $? -eq 0 && \
docker exec -t cache npm i --no-progress --registry http://registry.npmjs.org/

そのほか

一度キャッシュしてしまえば、以下のように
cache-min オプションに十分に大きな値を渡す。
cache-minはキャッシュを再チェックするまでの最小時間となるので、これによりキャッシュからインストールするようにできる。

1
npm i --cache-min 999999

また、devDependenciesが不要ならばproductionオプションを追加して

1
npm i --production

でdependenciesのみインストールすると良さそう。

参考にしたページ

cache-min & unknown version
feature request: force install from cache.

Comments