CELTFの競技サイトを運営していく上で、できる限り低リソースかつ複数のサイトを運用する必要があったので、採用した方法を紹介します。
安定して本番運用するというよりは、立ち上げ当初の小さいサービスを幅広く運営するケースを想定しています。
目的
- 定額で複数のサービスを運用する
- 定額で収めるために、App Engineなどは利用せずVPSで完結するようにします。
- サービス数によって必要メモリを増加させない
- サービス数の増加によりリソースを増強させないため、できる限りメモリはサービス数に依存させないようにします。
システム構成
サービス数によってメモリを増加させないために、できる限りリソースを使いまわせるようにします。
また、別々のサービスを運用するためにドメインで切り分けられるようにします。
構成図
FastAPI
サービスごとにNamespaceを作成し、サービス名をプリフィックスに指定してルーティングします。
Nginx
サービスごとにServer(VirtualHost)を作成します。
ルートロケーションはスタティックファイルが保存されているパスに向け、APIロケーションはFastAPIにプロキシします。
FastAPIへのプロキシはドメイン毎にNamespaceを振り分けて転送します。
構成ファイル
Nginx
Service-AとService-Bのサーバを作成します。
すべてのファイル構成はGitHubを確認してください。
server {
listen 80;
server_name service-a.localhost;
access_log off;
location /api/ {
proxy_pass http://api/service-a/;
}
location / {
root /usr/share/nginx/html/service-a;
index index.html;
}
}
server {
listen 80;
server_name service-b.localhost;
access_log off;
location /api/ {
proxy_pass http://api/service-b/;
}
location / {
root /usr/share/nginx/html/service-b;
index index.html;
}
}
FastAPI
APIRouterを使用し、サービスごとにルーティングを行います。
すべてのファイル構成はGitHubを確認してください。
from fastapi import FastAPI
import service_a
import service_b
app = FastAPI()
app.include_router(service_a.router, prefix="/service-a")
app.include_router(service_b.router, prefix="/service-b")
実演
実際に動作させ、リクエストを送ってみます。
Service-Aへのリクエスト
ブラウザを利用し、Service-Aへリクエストしてみます。
Vue.jsで作成されたスタティックファイルが表示されたので、次はAPIにcurlでリクエストしてみます。
fealone$curl http://service-a.localhost/api/
"Hello World with Service A"
APIへのリクエストも正常に行う事ができました。
Service-Bへのリクエスト
ブラウザを利用し、Service-Bへリクエストしてみます。
Vue.jsで作成されたスタティックファイルが表示されたので、次はAPIにcurlでリクエストしてみます。
fealone$curl http://service-b.localhost/api/
"Hello World with Service B"
Service-Bに対しても、正常にリクエストを行う事ができました。
おわりに
今回はHTTPSの設定をしていませんが、Let's Encryptなどを利用してサーバ毎に証明書を作成する事でHTTPS化も可能です。
コードはGitHub上にアップロードしているので、参考にしてみてください。
当たるかどうかわからないサービスを複数作成し、定額かつ共有リソースで運営したいときにおすすめです。