コンテナ
サーバーのテンプレートは Docker でコンテナのイメージを作ります。このイメージは Fly.io、Cloud Run、Render のようなホストで動きます。
テンプレートからデプロイする
このコマンドを実行するには、Rust 1.99 以降と Docker が必要です。cargo generate はサーバーのテンプレートを my-app にコピーし、パッケージにその名前を付けます。最後の 2 行でイメージをビルドし、起動します。
cargo install cargo-generate
cargo generate --git https://github.com/imaimai17468/hypersig templates/server --name my-app
cd my-app
docker build -t my-app .
docker run --rm -p 3000:3000 my-apphttp://localhost:3000 を開いてください。プロジェクトの中身は クイックスタート のカウンターで、すべてのインターフェースの PORT のポートで待ち受けます。Dockerfile は 1 つ目のステージでバイナリをビルドし、2 つ目のステージで小さなイメージにコピーします。ブラウザのランタイムはバイナリに埋め込まれているので、イメージに入るのはバイナリだけです。Fly.io、Cloud Run、Render のように Dockerfile からビルドするホストにはこのままのプロジェクトを渡し、イメージを動かすホストにはここでビルドしたイメージをそのホストのレジストリに push して渡します。
既存のプロジェクトに Dockerfile を追加する
テンプレートの Dockerfile はパッケージの名前を書いていないので、バイナリが 1 つのパッケージならどのプロジェクトでもそのままビルドできます。Cargo.toml のあるディレクトリにダウンロードし、target をビルドの対象から外す .dockerignore も置いてください。
curl -O https://raw.githubusercontent.com/imaimai17468/hypersig/main/templates/server/Dockerfile
echo target >> .dockerignoreこのリポジトリの例のように 127.0.0.1 で待ち受ける main には、コンテナの外から接続できません。すべてのインターフェースの PORT のポートで待ち受けるように変えてください。
// A container host names the port in `PORT` and connects from outside the
// container, so listen on every interface.
let port = std::env::var("PORT").unwrap_or_else(|_| "3000".to_owned());
let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await?;
axum::serve(listener, app).awaitDockerfile は cargo install --path . でビルドします。ワークスペースのルートにはパッケージがないので、そこでは失敗します。その場合は . をパッケージのディレクトリに変えてください。イメージの Rust は 1.99 なので、プロジェクトの rust-version がそれより新しいときは FROM の行のバージョンも上げてください。
ホストに必要なこと
- ストリームはパッチを送るあいだリクエストを開いたままにします。Cloud Run のようにリクエストを一定の時間で終えるホストでは、その時間でストリームが切れるので、上限を延ばしてください。
reconnect()を付けた呼び出しはストリームをつなぎ直します。 - ルーターの状態に置いたカウンターのようにメモリに持つ状態は、1 つのプロセスのものです。ホストがインスタンスを複数動かすときは、その状態をデータベースに置いてください。