ホスト上の開発サーバー
roji はコンテナを自動で検出する。一方、ホスト上で直接起動した開発サーバー
(vite dev、wrangler dev、rails s など)はコンテナではないので検出
されない。roji は、こうしたサーバーにもコンテナと同じようにホスト名と
HTTPS を付けられる。
方法は 2 つある。
roji runは、空きポートを選んで開発サーバーを起動する。設定は要らず、 ポートを決める必要も無い- 設定ファイルの
proxy_sitesは、自分で決めたポートにホスト名を付ける。 サーバーが起動していなくてもホスト名は残る
roji run: ポートを決めずに起動する
roji run app -- npx vite --port {port}
# roji: https://app.dev.localhost -> localhost:41873roji run <name> -- <command> は、空きポートを選び、https://<name>.<domain> を
そこへ向けてからコマンドを実行する。ルートはコマンドが動いている間だけ有効。
どのプロジェクトも、ほかが使っていないポートを受け取る。5173 や 8787 を 既定にする開発サーバーを同時に動かしても衝突しないし、プロジェクトの設定に ポートを書く必要も無い。
コマンドへのポートの渡し方
コマンドは、roji が選んだポートで待ち受ける必要がある。ポートは 2 通りの 方法でコマンドに渡る。
- 引数の中の
{port}は、すべてポート番号に置き換わる - 環境変数
PORTにポート番号が入る
どちらを読むかはツールによる。
| ツール | 書き方 |
|---|---|
| Vite | roji run app -- npx vite --port {port} |
| Wrangler | roji run api -- npx wrangler dev --port {port} |
Next.js、Rails など PORT を読むもの | roji run web -- npm run dev |
Vite と Wrangler は PORT を読まない。--port {port} を付けずに起動すると
いつものポートで立ち上がり、roji が表示した URL はエラーページになる。
roji run は 20 秒待っても選んだポートで何も待ち受けていなければ、その旨を
表示する。
コマンドへのホスト名の渡し方
自分のアドレスを知らないと動かない開発サーバーがある(OAuth のメタデータ、 絶対 URL へのリダイレクト、許可するホストの一覧など)。 ホスト名はベースドメインで決まり、ベースドメインはマシンごとの設定なので、 リポジトリに入れるスクリプトには書けない。ホスト名も、ポートと同じ 2 通りの 方法でコマンドに渡る。
| 引数の中 | 環境変数 | 値 |
|---|---|---|
{host} | ROJI_HOST | app.dev.localhost |
{url} | ROJI_URL | https://app.dev.localhost |
roji の https_port が 443 でないとき、URL にはポートが付く。どちらの値も、
稼働中の roji が返したもの。
環境変数の名前を HOST にしていないのは、HOST を待ち受けるアドレスとして
読む開発サーバーが多いため。
リクエストのオリジンを見る Worker には、ホスト名とプロトコルの両方を渡す。
渡さないと、wrangler dev はリクエストを http のものとして Worker に見せる。
roji run api -- npx wrangler dev --port {port} --host {host} --upstream-protocol https-- は省略できない。それより後ろはすべてコマンドのもので、コマンドの
フラグが roji のフラグとして解釈されるのを防いでいる。
roji run がすること、しないこと
- roji が動いている必要がある。
roji runは最初にルートを登録し、登録 できなければコマンドを起動しない - 名前の規則は、下の
hostと同じ。設定ファイルのproxy_sitesかstatic_sitesが宣言している名前と、別のroji runが使用中の名前は 拒否する - 同じホスト名にコンテナがルーティングされている場合、コマンドの実行中は
コンテナが隠れる。
roji runがその旨を表示する - コマンドの終了コードが、そのまま
roji runの終了コードになる。シグナルで 止まったときは、シェルと同じく 128 にシグナル番号を足した値を返す (Ctrl-C なら 130)。Ctrl-C とkillは、コマンドとそこから起動された プロセスのすべてに届く。npxを挟んでもサーバーは残らない - ルートを登録できるのは、roji が動いているマシンからだけ。
bindの設定に よらない roji run自体を強制終了(kill -9)した場合、ルートは 30 秒以内に消える。 起動済みの開発サーバーは止まらない- トンネルでは公開されない。公開したい場合は
proxy_sitesにtunnel: trueを書く
proxy_sites: 自分で決めたポートを使う
~/.config/roji/config.yaml にエントリを追加する。
proxy_sites:
- host: app # -> app.dev.localhost
target: http://localhost:5173 # 開発サーバーの待ち受け先
- host: api
target: http://127.0.0.1:8787開発サーバーをいつもどおり起動し、https://app.dev.localhost を開く。
ホスト名
host の規則は
静的サイトと同じ。
- ドットを含まない名前は
{host}.{domain}に展開する(app→app.dev.localhost) - ドットを含む名前は書いたとおりに使う
ホスト名はベースドメインの 1 階層下に収める。roji の証明書が対応するのは
*.{domain} までで、a.b.dev.localhost のような 2 階層の名前は、別ドメインの
名前と同じくブラウザに拒否される。外れているホスト名は roji doctor が報告する。
ダッシュボードのホスト名(既定では roji.dev.localhost)とベースドメイン
そのものは使えない。ダッシュボードが先に応答するので、そこに宣言したサイトは
警告を出して登録しない。
target
target はスキームまで含めた URL で書く。スキームは http のみ。TLS は
roji が終端する。
target: 8787やtarget: 127.0.0.1:8787は受け付けない。port:の ような略記も無い。1 つのサイトの書き方を 1 通りにするため- target にパスを書くと、すべてのリクエストの先頭に付く。
target: http://localhost:3000/baseなら/itemsは/base/itemsに届く - サーバーがどのアドレスで待ち受けるか分からないときは
http://localhost:ポートと書く。ツールや Node.js の版によって 「localhost」は127.0.0.1にも::1にもなり、roji は両方を試す - 別のマシンを指す target も使えるが、バリデーションが警告を出す。その リクエストは暗号化されずにこのマシンの外へ出る
開発サーバーが受け取るもの
コンテナの場合と同じ。
Hostヘッダーは、ブラウザが要求したホスト名(app.dev.localhost)の まま渡る。target のホスト名にはならない。Vite のserver.allowedHostsの ようにホスト名を検査する開発サーバーには、この名前が見える。Vite は.localhostの名前を既定で許可しているX-Forwarded-Proto: https、X-Forwarded-For、X-Real-IP- WebSocket(Vite の HMR)、Server-Sent Events、gRPC はそのまま通る
サーバーが起動していないとき
開発サーバーをまだ起動していない状態はふつうにあるので、roji は target に
接続できるかを確かめずにサイトを登録する。その状態でアクセスすると、
target のアドレスと、起動すべきものを案内する 502 のページが返る。
Compose プロジェクトと違い、roji はそのプロセスのことを何も知らないので、
roji からは起動できない。
roji doctor は、待ち受けているプロセスが無い proxy サイトを警告として
一覧する。
Docker ルートとの優先順位
proxy サイトは、静的サイトと同じくホスト名を保持する。同じホスト名に
コンテナがルーティングされている場合は proxy サイトが応答し、どの Docker
ルートが隠れたかを roji がログに出す。設定ファイルの中で同じホスト名を
2 回宣言した場合(proxy_sites 同士、static_sites 同士、両方に 1 つずつの
いずれでも)、後に書いた宣言が有効になる。proxy_sites は static_sites の
後に読まれる。
proxy サイトはホスト名全体を受け持つ。パスプレフィックスは指定できない。
変更の適用
再起動は不要。次のコマンドを実行するか、ダッシュボードの Reload Config ボタンをクリックする。
roji config reloadroji を動かしたまま target のポートを変えたいときも、この手順を使う。
コマンドは、宣言したサイトをすべて一覧し、どれが登録されたかを表示する。 登録されなかったサイト(target が URL でない、ダッシュボードのホスト名を 使っている、など)には理由が付く。その場合も、残りのサイトは反映される。
認証
proxy_sites:
- host: api
target: http://127.0.0.1:8787
auth:
basic:
user: admin
pass: secret
realm: API # 任意詳細は 認証のガイドを参照。
トンネルで外部公開する
開発サーバーで webhook を受けるのは、
トンネルの
主な用途の 1 つ。tunnel: true でサイトを公開対象にする。
proxy_sites:
- host: hook
target: http://127.0.0.1:8787
tunnel: true # https://hook.example.com で到達できる公開用の名前は、ローカルの名前のうちベースドメインの部分をトンネルの ドメインへ入れ替えて作る。そのため、公開できるのはベースドメインの下のホスト名だけ。
このキーが無いサイトは非公開のまま。開発サーバーが起動していないときに トンネル経由で来たリクエストには、上の案内ページではなく、トンネルの ほかの拒否と同じ素の 404 を返す。