Docker + Viteな環境でホットリロードが効かない時の対策

2024/02/03に公開

割と既知なネタな気はしますが、もう一段掘った内容にあんまりヒットしなかったので、Docker環境上でViteのホットリロードが動かない問題について私なりにまとめました。

環境

  • Windows 11
  • WSL2
    • Ubuntu
  • VSCode
  • Docker
    • base imageは cypress/browsers:node-18.16.0-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1
      • Node.js v.18.16.0
      • npm v9.5.1
      • Vite v5.0.11
        • 厳密にはAstro v4.2.4 に依存

事象

原因と対策

  • WSL2の制約で、WSL2側がWSL外のアプリケーションからのファイル変更を監視出来ないため
  • Viteもこの点は認識済で、対応策も含めドキュメントで言及済
    • https://ja.vitejs.dev/config/server-options.html#server-watch
    • 対策は下記
      • ホストエディタで無く、WSL2アプリケーションを利用した開発環境を構築する
        • Vite側で推奨するアプローチ
      • Vite(正しくは chokidar)を設定することで、ポーリングによるファイル変更監視を有効化する
        • 今回の開発環境の再構築不要で行える対策

Docker + VSCodeによる開発に不満は無いので、対策としては後者のVite設定による対処を本命とします。

対策の掘り下げ

実装

vite.config.js に下記を追加することで、ファイル変更のポーリングが有効化されます。

vite.config.js
export default defineConfig({
  ...
+  server: {
+    watch: {
+      usePolling: true
+    }
+  }
  ...
})

ちなみに環境の通り私はAstroを利用したため、その場合は astro.config.cjs を下記の様にすれば同等に有効化されます。

astro.config.cjs
export default defineConfig({
  ...
+  vite: {
+    server: {
+      watch: {
+        usePolling: true
+      }
+    }
+  }
  ...
})

どうしてViteでこれが推奨されないのか

usePolling は上でちょっと書きましたが、このオプションが渡される先は chokidar と言うNode.jsのファイルシステムラッパーです。
chokidar のドキュメント内ではこちらで書かれていますが

If polling leads to high CPU utilization, consider setting this to false.

話自体はシンプルで、Dockerコンテナ側でファイル変更を拾うためのポーリング処理を耐えず回すので、その分CPUリソースをたくさん食べますよ、と言うだけです。

結果コンテナ側の処理がbusyになるので、VSCode上で開発を進める中では、例えばコード編集 -> Auto SaveでのPrettier実行、が走ったタイミングで、コンテナとVSCodeの疎通がぶったぎられる様なことが多発します。

ViteとしてはCPUリソースを食う方法よりも、リソースフレンドリーな環境になる方がトラブル無く利用出来るよ、との意味で推奨オプションを定めていると見るべきでしょう。

CPU使用率高騰への対策

CPU使用率高騰はポーリング処理が絶えず走ることが原因なので、ポーリング頻度を低速にコントロールする手段があれば良さそうに見えます。

chokidar のドキュメントを見ると、intervalawaitWriteFinish による変更イベント拾い上げの低速化を狙えそうだなー、と感じましたが、後者はどちらかと言うと巨大なファイルの書き込み処理で物を言うオプションなため、小さなコンポーネントを集めて構築するAstro製アプリケーションの場合は interval を伸ばせば、一定負荷を抑えられそうです。

コンテナCPU監視結果

下記2段階で docker stats 相当の情報を覗きました。

    1. usePolling
    • 1-1) dev server起動
    • 1-2) ホットリロード実行
    1. usePolling + interval
    • interval はデフォルト値が 100 なため、1000 へ引き上げ
    • 2-1) dev server起動
    • 2-2) ホットリロード実行

  • 赤 1番を実行時のCPU使用率
  • 緑 2番を実行時のCPU使用率

思ったよりもがっつり消費リソースが減ってくれました。1番の場合はホットリロードが無くともCPU使用率が100%超え(1コアは使い切っている状態)ですが、ポーリング間隔を10倍に広げたことでホットリロード時も1コア内の範疇に収まりました。やっぱり awaitWriteFinish はいらなかったですね。

しばらくは設定を入れて一旦様子見との感じですが、メトリクスを見る限りは全然安全圏かな、と感じます。

監視踏まえての結論

これを踏まえての vite.config.js の設定は下記。

vite.config.js
export default defineConfig({
  ...
+  server: {
+    watch: {
+      usePolling: true,
+      interval: 1000
+    }
+  }
  ...
})

Discussion