devcontainer パフォーマンス検証 in フロントエンド

目次

検証目的

シンプルな興味本位というものが大きいですが、フロントエンドの開発でdevcontainerを使うときにどこにソースコードを配置すべきかを把握したかったため検証いたしました。
devcontainerではリモート側(Dockerコンテナ)とホスト側(Windows or wsl)とファイル同期を行うため、どこにソースコードを配置するかでパフォーマンスに影響が出るのはという仮説の元検証いたしました。

前提:環境

  • Windows 11
  • Docker Desktop使用せず、ubuntu内のDockerを使用
  • wsl2
    • Docker v26.1.4
  • Container
    • node v22
    • next.js v14.2.4

ソースコードは以下リポジトリのソースコードを使用。


適当なmaterial-uiの部品を置いたソースコードを300ページ作成して検証しました。
検証時に裏で動いているアプリがどれくらいプロセスを使ってるかの誤差は無視します。

前提:Dockerfileとdocker-compose.ymlの違いを少しだけ説明

  • Dockerfile
    Dockerfileはコンテナイメージをビルドするための設定ファイルです。
    コンテナイメージをどういう構成にするかの設定を記述するファイルです。
    コンテナを料理に置き換えると、Dockerfileはレシピにあたります。
  • docker-compose.yml
    docker-compose.ymlは複数のコンテナをまとめて管理するための設定ファイルです。
    複数のコンテナをまとめて管理するための設定を記述するファイルのため、Dockerfileよりも上位の概念です。
    そのためDockerfileを指定するのもdocker-compose.ymlです。
    コンテナを料理に置き換えると、docker-compose.ymlは献立にあたります。

ざっくりまとめると

Dockerfile = 単独
docker-compose.yml = 複数コンテナ

というイメージです。

以下のソースコードで検証いたしました。

Dockerfile

FROM node:22

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

docker-compose.yml

version: "3.9"
services:
  next:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/app
      - /app/node_modules
    ports:
      - 3000:3000
    environment:
      - NODE_ENV=development
    tty: true

やっていることはシンプルで、node v22のイメージに既存ディレクトリのソースをコピーし、3000ポートのポートフォワーディングしているだけです。
node_modulesはホスト側とリモート側で同期されないようにvolume箇所に記述しています。(volume trickと言うらしい)
※node_modulesがホスト側とリモート側で共有された場合、例えばnodejsをインストールしているバージョンがホスト側とリモート側で異なる場合に壊れてしまいます。

時間計測結果

今回は3パターン検証いたしました。

①Windows側にソースを置いてDockerを立ちあげる場合

②wsl2内にソースを置いてDockerを立ちあげる場合

③Docker内でgit cloneし、container内のみ使用する場合

検証にはnextjsで良く使用されるコマンドを使って検証いたしました。
各コマンドは以下の前提で検証いたしました。

  • npm installnode_modulespackage-lock.jsonを削除した初回に実行
  • npm run build.nextフォルダを削除した初回に実行
  • npm run devは初回の画面表示までの時間 cacheを使わないように.nextフォルダを削除した初回立ち上げで実行

また、計測にはコマンド実行から終了までを計測するtimeコマンドを使用しました。
以下が実行結果です。

実施コマンド①Windows⇔(wsl)⇔container②wsl⇔container③containerのみ
npm install2m 36.214s1m7.595s38.208s
npm run build3m35.006s42.846s41.313s
npm run dev(ready)21s1713ms1792ms
npm run dev(compile)20.4s5s2.5s

「①Windows⇔(wsl)⇔container」時のnpm install npm run build は遅すぎて使い物になりませんでした。
npm run dev で立ち上げた際のコンパイルにも時間がかかって重たい印象です。
これは仮説ですが、もしかしたらbuild時に生成する.next ディレクトリとnpm installに格納されるnode_modules を同期対象外?(そんなことできる?)にすると一時的にですがパフォーマンス向上するのかなと思いました。

「②wsl⇔container」と「③containerのみ」の差はそこまで違いを感じるほどでもなかったです。
 wsl⇔container間の同期は300画面ぐらいのソース量ではそこまで気にしなくても良いのかなと思います。

「③containerのみ」で実際にdockerのみで開発を進める場合はdocker起動用のdevcontainerを使い、その中にソースコードをgit cloneして開発を進める形になると思います。
今回はそこまで用意したくなかったため、同期を取っていないworkspace ディレクトリ以外の/home/node のディレクトリ配下に既存のソースコードをコピーして検証しています。

別件

nextjsをdevcontainerで動かす際にhot-reload(編集した内容が即時に反映される)が有効になっていなかったため、next.config.js を一部編集しています。

/** @type {import('next').NextConfig} */
const nextConfig = {
	  // NOTE: Windowsのdevcontainerでhot-reloadを有効化する。
    webpack: (config, context) => {
        config.watchOptions = {
            ignored: /node_modules/,
            poll: 1000,
            aggregateTimeout: 300
        }
        return config
    }
};

export default nextConfig;

計測結果内訳証跡

検証した際の記録がデタラメな数値では無いという気持ちで、一応各実行コマンド実行結果載せます。

①Windows⇔(wsl)⇔container

npm install

root@3afaf11d1b9e:/workspaces/dev-containe
r# time npm install
real    2m36.214s
user    0m42.335s
sys     0m22.077s

npm run build

root@3afaf11d1b9e:/workspaces/dev-container# time npm run build
+ First Load JS shared by all            87.1 kB
  ├ chunks/7023-d2da6b93d22bcac5.js      31.5 kB
  ├ chunks/fd9d1056-d0c14db3e40745f5.js  53.6 kB
  └ other shared chunks (total)          1.91 kB

○  (Static)  prerendered as static content

real    3m35.006s
user    2m7.220s
sys     0m55.877s

npm run dev

root@3afaf11d1b9e:/workspaces/dev-container# time npm run dev
 ✓ Starting...
 ✓ Ready in 21s
 ✓ Compiled /original in 20.4s (1109 modules)
 GET /original 200 in 22129ms

②wsl⇔container

npm install

root@c56ad4106a32:/workspaces/nextjs-devcontainer# time npm install
real    1m7.595s
user    0m15.004s
sys     0m7.575s

npm run build

root@7ac08c9ad9b1:/workspaces/nextjs-devcontainer# time npm run build
+ First Load JS shared by all            87.1 kB
  ├ chunks/7023-d2da6b93d22bcac5.js      31.5 kB
  ├ chunks/fd9d1056-d0c14db3e40745f5.js  53.6 kB
  └ other shared chunks (total)          1.91 kB

○  (Static)  prerendered as static content


real    0m42.846s
user    1m24.262s
sys     0m18.460s

npm run dev

root@7ac08c9ad9b1:/workspaces/nextjs-devcontainer# time npm run dev
 ✓ Starting...
 ✓ Ready in 1713ms
 ✓ Compiled / in 4s (544 modules)
 GET / 200 in 4193ms
 ○ Compiling /original ...
 ✓ Compiled /original in 5s (1109 modules)
 GET /original 200 in 5351ms

③containerのみ

npm install

root@7ac08c9ad9b1:/home/node/nextjs-devcontainer-docker# time npm install
real    0m38.208s
user    0m13.762s
sys     0m7.153s

npm run build

root@7ac08c9ad9b1:/home/node/nextjs-devcontainer-docker# time npm run build
+ First Load JS shared by all            87.1 kB
  ├ chunks/7023-d2da6b93d22bcac5.js      31.5 kB
  ├ chunks/fd9d1056-d0c14db3e40745f5.js  53.6 kB
  └ other shared chunks (total)          1.91 kB

○  (Static)  prerendered as static content


real    0m41.313s
user    1m18.697s
sys     0m19.593s

npm run dev

root@7ac08c9ad9b1:/home/node/nextjs-devcontainer-docker# time npm run dev
 ✓ Starting...
 ✓ Ready in 1792ms
 ✓ Compiled / in 3.3s (544 modules)
 GET / 200 in 3485ms
 ✓ Compiled /original in 2.5s (1132 modules)
 GET /original 200 in 2752ms

まとめ

フロントエンドでdevcontainerを使う場合は「②wsl⇔(container)」のwslにソースコードを配置するで良いかなと思います。

検証してみて、Windows⇔(wsl) のオーバーへッドが予想以上に大きいなという感想です。
devcontainerを使う際には最低限でもwsl上にソースを置いて開発を進めるべきですね。

containerのみで開発を進めるのが一番パフォーマンスが良いという結果になりましたが、この場合containerを作成しなおしたりすると修正途中のソースコードが消えてしまうので、開発途中でのcontainerの再作成は避けるべきです。
こういったデメリットとのトレードオフを考えてから、wslにするのかcontainerにするのかを選択すべきだと感じました。

devcontainerまだまだ使い始めたばかりなので、より良いパフォーマンス向上・開発体験向上の施策を見つけ次第ブログを更新出来たらなと思います。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

数学科出身のSoftware Engineer
情報通信が好きなのでブログを活用して発信しています。

コメント

コメントする

目次
閉じる