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 直アクセス | 即時 | 自分の別端末(スマホ等)から可 | ¥0 | 0.0.0.0 バインドが条件 |
| cloudflared quick tunnel | 即時 | URL 共有可 | ¥0 | dev サーバーが一時的に公開される点に注意 |
| 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 スキーマを変えて検証したい」となったら破綻する → そのとき初めて別サービス化を検討する