📓 memotty

SSH先のWSL2で立てた開発サーバーを手元ブラウザで確認する

課題 #

普段は手元のマシンから開発機(WSL2)に SSH して tmux + Vim で作業している。この構成だと WSL2 上で make dev などで開発サーバーを立てても、その画面をブラウザで確認できない。

最初は「Cloud Run をブランチごとにデプロイして確認すればいいのでは」と考えたが、用途を分解すると別問題だと分かった。

  • (a) 開発中の画面確認: 修正と確認を秒単位で繰り返す。ホットリロード(ファイル保存時に自動で再ビルド・反映される仕組み)前提
  • (b) ブランチ成果物の確認・共有: 本番相当の環境で、URL を共有したい

ブランチデプロイは (b) の解決策であり、(a) に使うと 1 回の確認に 3〜5 分(commit → push → build → deploy)かかってホットリロードも失われる。

解決策: SSH ポートフォワード(用途 a) #

すでに張っている SSH 接続に相乗りするだけ。追加インフラゼロ・無料・即時。

ssh -L 8080:localhost:8080 user@wsl2-host

-L はポート転送(ポートフォワード)の指定。「手元マシンの 8080 番ポートへのアクセスを、SSH 接続を通して接続先の localhost:8080 へ流す」という意味になる。

毎回打ちたくないので手元マシンの ~/.ssh/config に書いておく。

Host wsl2
  HostName <WSL2ホスト>
  LocalForward 8080 localhost:8080

これで接続中は手元ブラウザから http://localhost:8080 で WSL2 上の開発サーバーがそのまま見える。air(Go 用のホットリロードツール)による自動反映も効いたまま。

Tailscale 環境なら直接アクセスもできる #

WSL2 ノードに Tailscale を入れているなら、手元ブラウザから http://<WSL2ノードのTailscale IP>:8080 を直接開ける。ポート転送が不要で、SSH が切れても見え続ける。

条件は、開発サーバーが 0.0.0.0(すべてのネットワークインターフェース)で待ち受けていること。localhost だけで待ち受けていると、他の端末からの通信は届かない。Go の http.ListenAndServe(":8080", ...) はすべてのインターフェースで待ち受けるのでそのまま行ける。関連: 202606022355 Tailscale 経由で WSL2 に SSH すると鍵交換で固まる問題

比較した案 #

確認の速さ共有費用備考
SSH -L即時・ホットリロード維持不可(自分のみ)¥0設定 1 行
Tailscale 直アクセス即時自分の別端末(スマホ等)から可¥00.0.0.0 バインドが条件
cloudflared quick tunnel即時URL 共有可¥0dev サーバーが一時的に公開される点に注意
Cloud Run リビジョンタグ3〜5 分可・本番相当ほぼ ¥0用途 (b) の本命
ブランチごとに別サービス3〜5 分ほぼ ¥0タグ方式で足りるうちは過剰。サービスが増え続けて掃除が面倒

cloudflared quick tunnel は、ローカルのサーバーに https://xxx.trycloudflare.com のような一時的な公開 URL を発行する Cloudflare の機能。アカウント登録なしで使えるが、開発サーバーがインターネットに公開される。

ブランチ成果の共有には Cloud Run リビジョンタグ(用途 b) #

Cloud Run はデプロイのたびに「リビジョン」(そのときのイメージと設定のスナップショット)を作る。リビジョンにタグを付けると、そのリビジョンだけに向く専用 URL が発行される。ブランチごとにサービスを増やすのではなく、この仕組みで 1 つのサービスに相乗りするのが定石。

gcloud run deploy my-service --image ...:$SHORT_SHA \
  --no-traffic --tag feat-my-branch
  • 本番トラフィックには一切触れずに https://feat-my-branch---my-service-xxxx.run.app という専用 URL が生える
  • min-instances: 0(リクエストがない間はコンテナを 0 台まで減らす設定)のままなら、プレビューを放置しても費用はかからない
  • タグ名は URL の一部になるため、使えるのは小文字英数とハイフンだけ。feature/foo のようなブランチ名は feature-foo に変換してから使う
  • 掃除は gcloud run services update-traffic my-service --remove-tags feat-my-branch
  • 溜まるのはリビジョンと、Artifact Registry(コンテナイメージの保管サービス)上のイメージ。後者は古いものを自動削除するクリーンアップポリシーを設定できる

使い分けと破綻ポイント #

  • まず SSH -L で開発ループを解決し、共有が必要になった時点でリビジョンタグ方式を足す二段構え
  • SSH -L は「他人やスマホに見せたい」となった瞬間に破綻する → Tailscale 直アクセスかタグ方式へ
  • タグ方式は「ブランチごとに環境変数や DB スキーマを変えて検証したい」となったら破綻する → そのとき初めて別サービス化を検討する