Dockerコンテナ用にnpm runの省メモリでエコな代替を書いた話
DockerでNodeJSのプログラムを動かしてるサーバーでhtopのプロセスツリーを眺めてるとこんな感じになってることが多々あります
npm run
が67MiBとそれなりにメモリを使用している上にスレッドを量産しています。
メモリが潤沢な環境なら気にしなければいい話ではありますが、VPSなどのクラウド上で運用していてメモリに余裕がない場合や、RaspberryPiなどの性能が限られてるSBCで実行している場合にはそれなりに気になってきます。
このnpm run
がやってることはpackage.json
の指定されたscript
を実行してるだけです。これをやるためだけに67MiBもサーバーの貴重なメモリを持ってかれるのはエコではないということで、最近やたら流行っているRustで超簡易的な代替を書いてみたという訳です。
成果物
GitHubでnpmrunとして公開しています。
動作
やっていることはカレントディレクトリのpackage.json
をパースしてコマンドライン引数で指定されたscriptを実行しているだけです。ただし以下の点が微妙に本来のnpm run
と違います。
-
&&
で複数のコマンドが指定されてる場合、同時に実行するのではなく内部的に分割して一つづつ実行します-
Command::new
はコマンドを実行する関数なので&&
みたいなシェル用の構文は使えないため - このままだとディレクトリの移動ができないのでコマンドが
cd
だったらstd::env::set_current_dir
でカレントディレクトリを移動するようにしてあります
-
使い方
Dockerfileに以下の内容を追記します。
まずはnpmrun-builder
ステージでnpmrun
コマンド本体をビルドする部分を先頭の方に付け足します。
FROM rust:1-alpine as npmrun-builder
WORKDIR /src
RUN apk add --no-cache git alpine-sdk
RUN git clone https://github.com/nexryai/npmrun.git .
RUN cargo build --release
続いてnpmrun-builder
でビルドした成果物をrunner
など本体を実行するステージに引っ張ってきます。
Alpineをベースに使ってる場合なのでベースのディストリビューションによっては/usr/local/bin/
の部分が変わってくるかもしれません。
COPY /src/target/release/npmrun /usr/local/bin/npmrun
CMD
なり実行する部分のnpm run
やそれに相当する部分をnpmrun
に置き換えます。
ENV NODE_ENV=production
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["npmrun", "docker:start"]
Done
以下の画像のようにhtopのプロセスツリーを見た時npmrun
で実行されてれば成功です。
まとめ
以上、私がVPS代金をケチるためにした努力の成果の紹介でした。お役に立てれば幸いです。
現段階ではまだ不完全な部分もそれなりに残ってるので、何か問題などあればGitHubからissueを生やしてくれると助かります。
Discussion