小さいアプリをVPSに置く時の作業を、もう少し楽にしたくなりました。
アプリをアップロードして、ビルドして、ポートを決めて、プロセスを起動して、ドメインをリバースプロキシにつなぐ。
一度なら手でやれます。
でも、アプリが増えると、毎回同じような作業を繰り返すことになります。
そこで、ローカルや小さなサーバー用のPaaSっぽいものをGoで試し始めました。
最初はプロセスを起動すればよいと思っていた
ランタイムごとにコマンドを決めて、アップロードされたディレクトリで実行すれば動きます。
静的ファイルならCaddyから直接配信してもよいし、NodeやBunならプロセスを起動してポートを割り当てればよい。
最初はこのくらいのイメージでした。
ただ、ホスト上のファイルをCaddyが読む構成にすると、実行ユーザーの権限で詰まりやすいです。
アップロード先がユーザーのホームディレクトリ配下にあると、Caddyのサービスユーザーから見えないことがあります。
ああ、これはファイルを見せるのではなく、アプリごと動かした方がよさそうだと考えました。
すべてのアプリをコンテナにする
今の試作では、静的サイトもNodeもBunもGoのバイナリも、コンテナでビルドして起動する形に寄せています。
管理APIからアプリを作成し、zipをアップロードすると、ランタイムの初期値を決めます。
その後、ランタイムに合わせたDockerfileを生成してビルドします。
起動時には 31000 から 31999 の範囲でポートを割り当て、実際にlistenできるかも確認します。
Caddyはアップロードされたファイルを直接読みません。
動いているコンテナのポートに reverse_proxy するだけです。
この形にすると、ホストのファイル権限と公開サーバーの権限を少し切り離せます。
ただ起動するだけでも考えることがある
アプリの作成、ビルド、起動、停止、再起動の状態が必要です。
ビルドに失敗した時に、起動済みの古いコンテナをどうするかも決めないといけません。
アップロードされたzipの展開では、../ を含むパスを受け付けないようにしました。
このあたりはPaaSの本体というより、周辺の小さい処理です。
でも、ここが雑だと、アプリを起動する前に別の問題が起きます。
E2Eも最初から置く
最初のデプロイ経路をPlaywrightのAPIクライアントで通すE2Eも置きました。
サーバーをテスト用ポートで起動し、静的アプリを作成して、index.html を含むzipをアップロードし、Caddyfileを生成するところまで確認します。
ブラウザを立ち上げるテストはオプションにして、基本のE2Eはブラウザランタイムなしで動くようにしました。
管理画面の見た目より先に、アップロードから公開設定までの流れが成立しているかを見たかったためです。
おわり
まだ本格的なPaaSではありません。
認証、ログ、リソース制限、ロールバック、ドメイン設定など、運用を考えると足りないものは多いです。
それでも、アプリをコンテナにして、Caddyはポートへつなぐだけにするところまで持っていくと、構成の境界は見やすくなりました。
小さいVPSに自分用のアプリをいくつか置くなら、このくらいの仕組みでもあとで効きそうです。
まずは、アップロードしたものが安全にビルドされて、決めた場所で動くところを固めていきます。