Movatterモバイル変換


[0]ホーム

URL:


The Third Law

Node.jsアプリケーションのDockerイメージのサイズを削減する方法について調べた

Next.jsアプリケーションのDockerイメージのサイズが大きくて困っていたので調べていたところ、Next.jsの公式ドキュメントにDocker Imageというセクションがあり、おすすめ設定が記載されているのを見つけました。以前はここまで詳細な記述はなかったのですが、つい10日ほど前に追加されたようです。

ドキュメントには一番おすすめの方法だけ書かれているのですが、もともと「What is the best way to use NextJS with docker? · Discussion #16995 · vercel/next.js · GitHub」というdiscussionがあり、ドキュメントの記述はここでの議論が元になっているようです。Discussionではいくつか例が示されているのですが、それぞれさまざまな最適化テクニックが利用されており、どれくらいの容量になるか気になったので、Dockerfileを書いて調べてみました。

検証に使ったコードはこちらに置いてあります。

github.com

なお、各テクニックはNext.jsに限らずNode.jsのアプリケーション一般で通用するものだと思います。


まずはベースラインとして最適化なしのDockerfileです。

FROMnodeWORKDIR/usr/src/appCOPYpackage.json yarn.lock ./RUNyarn install --frozen-lockfileCOPY. .RUNyarn buildEXPOSE3000CMD["yarn","start"]


デフォルトのnodeイメージはDebianベースなので元からサイズが大きいです。イメージのサイズを小さくしたい場合は、可能であればまずはAlpine Linuxベースのnode:alpineに乗り換えるのがいいでしょう。

(追記)Alpineでは標準Cライブラリとしてglibcではなくmusl-libcが使われており、Node.jsでもネイティブモジュールを使っている場合に互換性の問題が起きることがあります。2021年時点では、alpineではなく、glibcが採用されておりかつサイズが小さいdistrolessやslimイメージを使うのがおすすめです(ブックマークコメントでの指摘ありがとうございます)。

blog.inductor.me

FROMnode:alpineWORKDIR/usr/src/appCOPYpackage.json yarn.lock /usr/src/appRUNyarn install --frozen-lockfileCOPY. .RUNyarn buildEXPOSE3000CMD["yarn","start"]

yarn installはデフォルトではdevDependenciesも含めて全ての依存パッケージをインストールします。アプリケーション実行用のイメージにLinterやテストフレームワークなどの開発用ツールを含める必要はないので、--productionオプションを利用して、devDependenciesをインストールしないようにすると、イメージのサイズを小さくできます。

FROMnode:alpineWORKDIR/usr/src/appCOPYpackage.json yarn.lock ./RUNyarn install --production --frozen-lockfileCOPY. .RUNyarn buildEXPOSE3000CMD["yarn","start"]

さらに、Yarnのダウンロードキャッシュを削除すると、その分イメージのサイズを小さくできます。先程のdiscussionでは二つ方法が紹介されていました。

一つはyarn cache cleanコマンドを使う方法です。

FROMnode:alpineWORKDIR/usr/src/appCOPYpackage.json yarn.lock ./RUNyarn install --production --frozen-lockfile \  && yarn cache cleanCOPY. .RUNyarn buildEXPOSE3000CMD["yarn","start"]

もう一つは、キャッシュディレクトリを/dev/shm以下にする方法です。/dev/shmはLinuxにおけるRAMディスクのマウント先で、キャッシュディレクトリをここに指定すると、手で明示的にキャッシュを消さずとも、イメージのレイヤーにキャッシュが含まれなくなります。

ただし、この方法を利用する場合は、たいてい/dev/shmのサイズが足りなくなる*1ため、docker buildコマンドの--shm-sizeオプションで十分なサイズを指定してやる必要があります。一つめの方法を採用する方が面倒が少なくて楽でしょう。

FROMnode:alpineWORKDIR/usr/src/appCOPYpackage.json yarn.lock ./ENVYARN_CACHE_FOLDER=/dev/shm/yarn_cacheRUNyarn install --production --frozen-lockfileCOPY. .RUNyarn buildEXPOSE3000CMD["yarn","start"]
$ docker build--shm-size 1gb-f Dockerfile.dev-shm-yarn-cache

さらにイメージサイズを小さくするには、アプリケーションの実行に不要なコードをイメージに含めないようにするとよいです。テストコードは実行に関係ないので削除できますし、Next.jsのようにソースコードをトランスパイル・ビルドしている場合は、ビルド元のコードは不要です。

必要なファイルのみイメージに含める方法はいくつかありますが、ファイルの削除し忘れなどのミスを避けるには、multi-stage buildを使うとよいです。これはNext.jsのドキュメントでおすすめされている方法と同様です。この方法だと、Yarnのパッケージキャッシュを手で削除する必要がないのも楽です。

FROMnode:alpine AS depsWORKDIR/usr/src/appCOPYpackage.json yarn.lock ./RUNyarn install --production --frozen-lockfileFROMnode:alpine AS builderWORKDIR/usr/src/appCOPY. .COPY--from=deps /usr/src/app/node_modules ./node_modulesENVNODE_ENV=productionRUNyarn buildFROMnode:alpine AS runnerWORKDIR/usr/src/appCOPY--from=builder /usr/src/app/public ./publicCOPY--from=builder /usr/src/app/.next ./.nextCOPY--from=builder /usr/src/app/node_modules ./node_modulesEXPOSE3000CMD["yarn","start"]

では、それぞれのイメージのサイズを比較してみます。

$ docker images nextjs-docker--format"table {{.Tag}}\t{{.Size}}" |sed-e'1d' |sort-k2-h-r simple1.17GB# Debianベースalpine               399MB# Alpineベースinstall-production   315MB# yarn install --productiondev-shm-yarn-cache   157MB# キャッシュディレクトリを /dev/shm 以下にするclean-cache          157MB# yarn cache cleanstaged               156MB# Multi-stage build

結果、Next.jsのドキュメントでおすすめされている方法でビルドしたイメージが一番小さくなりました。全く最適化していないものに比べて1/8以下のサイズです。ベースイメージを変更したのが一番影響がありますが、他の手法もMB単位でサイズが小さくなっています。Multi-stage buildはこの比較だとあまり意味がないように見えますが、実際のアプリケーションでは実行時に不要なファイルがこのサンプルより多いでしょうから、ここで示したよりも効果が出るでしょう。

*1:デフォルトは64MB

検索

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp