PasteDBは、FastAPI・MongoDB・Cloudinaryを組み合わせたペースト共有サービスです。フロントエンドをNetlifyにホスティングし、バックエンドAPIをPythonで動かすというこの構成は、個人開発の枠を超えて、オンプレからクラウドへ移行する際の典型的なアーキテクチャパターンとして参照できます。
静的ホスティングとバックエンドAPIを分離する設計
PasteDBはフロントエンド(HTML/CSS/JavaScript)をNetlifyに、バックエンドのFastAPIを別に配置する構成を取っています。これはJamstack(ジャムスタック)と呼ばれるアーキテクチャパターンで、静的ファイルをCDN(コンテンツデリバリネットワーク)上に配置し、動的処理はAPIへ委譲します。
この分離によって得られる運用上のメリットは大きいです。Netlifyのような静的ホスティングサービスはDDoS耐性やCDNによる自動スケールが標準装備されているため、フロントエンドの可用性を別途管理する必要がありません。障害の影響範囲も「APIが落ちている」「DBが落ちている」「ストレージが落ちている」と切り分けやすくなります。
たとえばCloudinaryへの画像アップロードが失敗した場合、テキストのペースト機能には影響しません。障害の局所化(障害が一部のコンポーネントに留まること)が設計レベルで担保されています。
MongoDB+Cloudinaryの監視で見るべきポイント
PasteDBはデータベースにMongoDBを、画像ストレージにCloudinaryを採用しています。それぞれ外部マネージドサービス(自社でインフラを持たず、クラウドベンダーが運用を担うサービス)です。外部マネージドサービスを組み合わせるアーキテクチャでは、監視設計がオンプレと根本的に異なります。
オンプレの場合はサーバー自体のCPU・メモリ・ディスクI/Oを監視の起点にします。一方、マネージドサービスではインフラ層の監視はベンダー側に委ねられるため、こちらが見るべき指標はアプリケーション層に移ります。具体的には以下の3点です。
- APIレスポンスタイム(FastAPIからMongoDBへのクエリ応答時間)
- 外部サービスのエラーレート(Cloudinary・Judge0 APIへのリクエスト失敗率)
- エンドユーザー体感の成功率(ペーストの作成・取得が正常に完了した割合)
この考え方はSLI(サービスレベル指標)と呼ばれます。「サーバーが生きているか」ではなく「ユーザーが目的を達成できているか」を指標にする設計思想です。
エンドツーエンド暗号化とIndexedDBの運用上の注意
PasteDBの特徴として、エンドツーエンド暗号化(E2EE)とIndexedDB(ブラウザ上でデータを保存するクライアントサイドDB)の組み合わせがあります。E2EEは暗号化キーをサーバーに送らないため、サーバーログにパスワードやキーが残りません。この設計はセキュリティ上望ましい反面、運用に注意点があります。
障害調査の際、暗号化前のデータはサーバー側には存在しません。つまり「特定のペーストが表示できない」という障害が発生しても、サーバーサイドのログを見てもデータ内容の確認はできないのです。原因調査はAPIのステータスコード・HTTPヘッダ・MongoDB側のドキュメント構造の確認に限られます。
IndexedDBについても同様で、ブラウザのストレージに保存されたキー情報はサーバー側からアクセスできません。ユーザーがブラウザのキャッシュをクリアするとキーが消えてペーストが復号できなくなる、という問題は設計上の制約として受け入れる必要があります。この点を運用ドキュメントやユーザー向けの注意事項に明記しておく習慣が重要です。
Judge0 APIに見る外部依存の管理
コードをブラウザ上で実行する機能にJudge0 APIを使っています。Judge0はコードのサンドボックス実行(隔離された環境でコードを動かす仕組み)をAPIとして提供するサービスです。
外部APIへの依存は、障害の根本原因分析を複雑にします。「コードが実行できない」という事象が発生した場合、原因はJudge0のサービス障害・ネットワークのタイムアウト・レートリミット(一定時間内のリクエスト上限)超過など複数の候補があります。これらを区別するには、外部APIへのリクエストごとにステータスコードとレイテンシ(応答時間)をログに残しておく必要があります。
import httpx
import logging
logger = logging.getLogger(__name__)
async def run_code(source_code: str, language_id: int):
async with httpx.AsyncClient() as client:
try:
response = await client.post(
"https://judge0-ce.p.rapidapi.com/submissions",
json={"source_code": source_code, "language_id": language_id},
timeout=10.0
)
logger.info("judge0 status=%d latency=%.3fs", response.status_code, response.elapsed.total_seconds())
return response.json()
except httpx.TimeoutException:
logger.error("judge0 timeout")
raiseこのようにリクエストごとにステータスコードとレイテンシを記録しておくと、障害発生時に「Judge0側の問題か自分のAPIの問題か」を迅速に切り分けられます。
オンプレからクラウドへ移行する際に参照できるパターン
PasteDBの構成は、オンプレで動いていたサービスをクラウドに移行する際の到達点のひとつとして参考になります。ストレージをCloudinaryのようなマネージドサービスに置き換えることで、ディスク容量の監視・バックアップ設計・レプリケーション設定といったオンプレ特有の運用負荷を大きく削減できます。
モノリス(単一サーバーにすべての機能が同居する構成)からこのような分散構成へ移行するとき、最初に外部化すべきはストレージです。次にDBのマネージドサービス化、そしてAPIの分離という順番が一般的です。いきなり全てを置き換えるのではなく、障害の影響範囲が小さいコンポーネントから段階的に移行することで、リスクを抑えながら運用知見を積めます。
PasteDBが採用した「フロントエンドとバックエンドを分離し、ストレージと実行環境は外部APIに委ねる」という構成は、その最終形をシンプルに体現しています。