🏗️

DevContainer上で動かしているViteサーバーにホスト側からアクセスできない時の対処方法

2024/05/21に公開

状況

npm run devで開発サーバーをDevContainer内で立ち上げ、ホスト側のChromeからアクセスしたところ接続ができない。

ホスト側からDevContainerのポートが空いているかを確認

lsof -i:ポート番号コマンドで指定したポート番号が空いているかを確認することができる。

lsof -P -i:5173
COMMAND    PID  USER   FD   TYPE            DEVICE SIZE/OFF NODE NAME
Code\x20H 1033 me     29u  IPv4 0xd9abe49ae1cac34      0t0  TCP localhost:5173 (LISTEN)

ポートは空いているようだ。

リモート側からViteサーバーの応答があるかを確認

curl http://localhost:5173コマンドで初期画面のHTMLが帰ってくることで確認できる

$ curl http://localhost:5173
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><title>New Remix App</title><meta name="description" content="Welcome to Remix!"/></head><body><div style="font-family:system-ui, sans-serif;line-height:1.8"><h1>Welcome to Remix</h1><ul><li><a target="_blank" href="https://remix.run/tutorials/blog" rel="noreferrer">15m Quickstart Blog Tutorial</a></li><li><a target="_blank" href="https://remix.run/tutorials/jokes" rel="noreferrer">Deep Dive Jokes App Tutorial</a></li><li><a target="_blank" href="https://remix.run/docs" rel="noreferrer">Remix Docs</a></li></ul></div><script>((STORAGE_KEY, restoreKey) => {
    if (!window.history.state || !window.history.state.key) {
      let key = Math.random().toString(32).slice(2);
      window.history.replaceState({
        key
      }, "");
    }
    try {
      let positions = JSON.parse(sessionStorage.getItem(STORAGE_KEY) || "{}");
      let storedY = positions[restoreKey || window.history.state.key];
      if (typeof storedY === "number") {
        window.scrollTo(0, storedY);
      }
    } catch (error) {
      console.error(error);
      sessionStorage.removeItem(STORAGE_KEY);
    }
  })("positions", null)</script><link rel="modulepreload" href="/@id/__x00__virtual:remix/browser-manifest"/><link rel="modulepreload" href="/app/entry.client.tsx"/><link rel="modulepreload" href="/app/root.tsx"/><link rel="modulepreload" href="/app/routes/_index.tsx"/><script>window.__remixContext = {"url":"/","basename":"/","future":{"v3_fetcherPersist":false,"v3_relativeSplatPath":false,"v3_throwAbortReason":false,"unstable_singleFetch":false},"isSpaMode":false,"state":{"loaderData":{"root":null,"routes/_index":null},"actionData":null,"errors":null}};</script><script type="module" async="">import "/@id/__x00__virtual:remix/inject-hmr-runtime";import "/@id/__x00__virtual:remix/browser-manifest";
import * as route0 from "/app/root.tsx";
import * as route1 from "/app/routes/_index.tsx";
window.__remixRouteModules = {"root":route0,"routes/_index":route1};

import("/app/entry.client.tsx");</script></body></html>

ローカルからはアクセスできるようだ

Vite以外のサーバーを立ち上げて接続を確認

npx serveで簡単にサーバーを立ち上げてListenできる。

こちらは問題なく立ち上がっているようだ

状況の整理

  • DevContainer上ではViteサーバーが立ち上がっている
    • ホストマシンからはViteサーバーにアクセス不可
    • DevContainer内からはViteサーバーにアクセス可能
  • Vite以外のサーバーを立ち上げた場合
    • ホストからアクセスできるようになった

上記の理由からViteサーバー周りが怪しいと仮説をたてた。

調査

ViteのドキュメントからServer周りのConfigを調査。
server.hostをtrueにすることで全てのアドレスをListenすることが可能とのこと。
https://ja.vitejs.dev/config/server-options.html#server-host

つまり、デフォルトではローカル(DevContainer)のみアクセス可能で、LAN内(ホストマシン含む)は弾かれるようになっているようだ。

解決方法

Viteのコンフィグを以下のように修正することで、ホストマシンからlocalhost:5173でアクセスできるようになる。

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

installGlobals();

export default defineConfig({
  plugins: [remix(), tsconfigPaths()],
+  server: {
+    host: true,
+  }
});

Discussion