NestJSアプリケーションのビルドサイズをncc使って300MB減らした話
Dockerイメージサイズをできるだけ小さくする際に試行錯誤した結果、nccを使ってイメージサイズを減らせたので備忘録
まずは結果。354MBの削減に成功しました。
nccを使う前: 599MB
nccを使った後: 245MB
ビルド手順
nccはTypeScriptをサポートしています。ですが、tsconfig.jsonの指定ができないためtsconfig.app.jsonのような複数の設定ファイルを保持している場合、うまくビルドすることができません。
そこで、TypeScriptでのビルドを諦め次のようにWebpack経由でビルドを行います。
例: api というアプリをビルド
1. nest-cliでwebpackモードでビルドする: nest build api --webpack
2. 1の成果物をnccで更にビルドする: ncc build dist/api.js -m -o dist/api
するとアプリに必要なファイルをまとめて出力してくれます。 Dockerイメージにはこれを含めればいいだけ。
nccすごいですよ。webpackのやつを更にビルドできるの。
これにより、node_modulesが必要なくなり必要最低限のファイルのみがDockerイメージに含まれることになりました。
(node_modulesはもちろん`npm ci --production`でインストールしたもの)
以下は、nccでビルドできるまで躓いた点
... attempted to require "@nestjs/microservices" but could not be resolved, assuming external.
(↑正確なエラー文忘れて記録にも残ってないので前半ぼかしてます...)
nccでビルドしたファイルを実行すると、@nestjs/microservicesのインポートがうまく解決できないエラーが発生しました。関連issueはこちら: https://github.com/zeit/ncc/issues/463
私の場合、@nestjs/microservicesはメインで使っておらず@nestjs/terminusが動的に読み込みを行っているようです。terminusはヘルスチェックに必要ですが、少し前にサーバーレスに移行したため、重要度が低くなりました。なので@nestjs/terminus自体を削除しました。
nccの成果物にtypescriptが含まれる
場合によって、TypeScriptがまるっと含まれる可能性があります。47.8MB、めちゃくちゃでかい。当たり前なんですが、typescriptフォルダ自体削除しても特に動作に支障はありませんでした。
え、300MBも削減されてんの?動くのこれ?という不安
↑の結果を初めて見た時、かなり不安になりました。本当に最適化されただけ...?何か重要なファイル消えてない..?と言う感じで。
e2eテストをこのnccで行いましたが、ちゃんとテストは通っているので問題無しとしています(テスト書いててよかった)
あと、パッケージによっては古いバージョンのライブラリを参照してることがあります。バージョンの異なる同じパッケージが含まれているか調べましたが、ちゃんと含まれているようです(uuidのパッケージが4つも含まれてた...)。
余談: 更にpkgでシングルファイルにすれば完璧では...!?
nodeも実行ファイルもアセットもシングルファイルにすれば完璧な圧縮が可能になるのでは...!?と思ったので試してみました。Dockerイメージはalpineを使用。
...が結果は動かなくて諦めました。
・alpine + pkg の組み合わせは動きませんでした。static link周りでエラーが出ます。
https://github.com/zeit/pkg-fetch/pull/72
・そもそもpkgはサーバーレス用途では使わないでってREADMEに書いてありました。
https://github.com/zeit/pkg/blob/master/README.md