アクセスが月に1,2件しかない私のポエムブログの運営において、最も頭を悩ませるのが「データベース(Cloud SQLなど)の固定費」です。アクセスがほとんどなく、常に閑古鳥がセッションを奏でているのに、月額約1,500円程度の固定費が私の懐を悩ませ続けます。
そのため、この「データベース代金の捻出」課題を解決するため、データベース自体を完全に排除し、Cloud Run × Google Cloud Storage (GCS) 直結型 JSON/Markdown自作CMSを導入することで、インフラ維持コストを「ほぼ月額0円」に抑え込む超モダンなサーバーレスブログを実現したいと思います。
💡 この記事の3行まとめ(忙しい人向け)
- 解決する課題: アイドル時間であっても発生し続ける高価なデータベース固定費の削減。
- 採用したアーキテクチャ: GCS SDKを用いてJSONとMarkdownを直接読み書きする超軽量な自作フラットファイルCMS、および Gemini によるアイキャッチ自動生成(約6円/枚)。
- 実証成果: ローカル環境(エミュレータ)でのバケット自動初期化およびテスト投稿を即座にパス。
🏗️ アーキテクチャ概要
システム構成図(データフローと対称設計)
「ローカル検証環境(青)」と「Google Cloud本番環境(緑)」を、同じコードで動くようにしています。
💫 GCS直接保存とGemini自動画像生成のコアロジック
このブログではデータベースを使わず、全記事の管理用インデックス(投稿日時・タイトル・画像パス等のリスト)を GCS上の単一ファイル(posts.json) として保存・管理しています。
執筆者は私1人のため、同時書き込みの競合などを心配する必要性は通常不要ですが、複数デバイスからの同時ログインや、「万が一このポエムブログが超絶大バズりして100人のライターを雇用するような緊急事態に陥った場合など」 、後から保存した人のデータで前のアクションが上書き消去される事故(ロストアップデート)が発生してしまうことを懸念しないわけにもいきません。
これを防ぐため、GCSが持つ世代番号(generation)チェック機能を活用した「楽観的ロック(412 Precondition Failed検知と自動リトライ・再マージ)」を組み込んでいきます。
また、投稿時にはGeminiの画像生成モデル gemini-3.1-flash-lite-image (Gemini Enterprise Agent Platform / 旧称 Vertex AI) APIを呼び出し、アイキャッチ画像を自動生成する仕掛けも同梱しています。(※テキスト解析・翻訳には最新の gemini-3.7-flash、画像自動生成には gemini-3.1-flash-lite-image をそれぞれ役割分担して活用します)
⚠️ ハマりポイント注意:gemini-3.1-flash-lite-image (Gemini Enterprise Agent Platform / 旧称 Vertex AI) の呼び出し仕様
(※本仕様は執筆時点・現時点の提供形態に基づくものです。将来的に us-central1 や asia-northeast1 等のリージョンエンドポイントへ展開される可能性があります)
現時点で Gemini Enterprise Agent Platform (旧称 Vertex AI) 上の画像生成モデル gemini-3.1-flash-lite-image を呼び出す際、locations/us-central1 などのリージョンエンドポイントを指定すると 404 NOT_FOUND エラーになる場合があります。
現時点の仕様では locations/global (グローバルエンドポイント) 限定で提供されているため、APIキー不要な Google Cloud サービスアカウント認証で呼び出す際は、エンドポイントの場所指定を global に設定してください:
# ⭕ 成功するグローバルエンドポイントの記述例
https://aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/global/publishers/google/models/gemini-3.1-flash-lite-image:generateContent
ちなみに今回、SQLiteを捨ててGCS直結フラットファイル構成にしております。※なぜLitestreamを諦めたか
当初は流行のSQLiteをGCSにデプロイしてバックグラウンドレプリケーション(Litestream等)させる構成を考えていたのですが、大きな課題が内在していることに気付いて断念いたしました。。。
-
SQLite+Litestreamの限界: ファイルDB+バックグラウンドレプリケーションで行けるやろ!!!と、世間一般でもこの考え方が紹介されており、私も当初そのノリで考えていたのですが、作っている途中で、書き込み後にLitestreamがGCSへWAL(ログ)をバックグラウンド同期する必要があり、リクエスト終了後もCPUを維持する「CPU常時割り当て(
--no-cpu-throttling)」が必要になることに気付きました。
Noスロットリングを設定しないと、レスポンス返却と同時にコンテナのCPUがフリーズし、同期が中断してデータ消失リスクがあります。しかし、記事保存のためCPU常時割り当てにすると、Cloud Runの仕様上リクエスト完了後も約15分間コンテナが稼働し続け、わずか数秒の処理に対して約15分間もの不要なCPU課金が発生し続けてしまうという本末転倒な結論になってしまったのです😢。(いくら無料枠で収まるとはいえ、この課金構造は美しくありません) -
スケーラビリティの制限: 単一ファイルのSQLiteに書き込みが発生する以上、データ破壊を防ぐには最大インスタンス数を
max-instances=1に固定せざるを得ず、Cloud Run本来の強みである「自動水平スケールアウト」を完全に放棄することになり、ダサいにもほどがあります。
そのため、この記事ではデータベース前提の構成を完全に排除し、データを個別ファイルとして保存する疎結合アプローチに変えています。
これによりバックグラウンド同期が不要となり、Cloud Run本来の「CPUスロットリング有効(リクエスト中のみCPU課金)」の恩恵を100%享受でき、不要なアイドリング維持コストをほぼ0秒に抑えこむことができるようになります。
データベース選定とアーキテクチャの比較検証
WordPressなどの従来の構成と、今回構築したGCS直結型自作CMSのアーキテクチャ特性の比較です。
| 評価軸 | MySQL VM (Compute Engine) | Cloud SQL (最安構成) | GCS直結型自作CMS (本構成) |
|---|---|---|---|
| 基本データモデル | リレーショナル(VMローカル) | フルマネージドRDB | 疎結合フラットファイル(JSON/MD) |
| コスト伸縮性 (FinOps) | 低(VMの常時起動による固定費:約1,500円〜) | 低(DBの常時起動による固定費:約2,000円〜) | 極高(0 ⇄ N の超高速な水平スケール、アイドル時完全0円) |
| データ永続化 / 同期 | 手動(rsyncやcronバックアップ) | 自動(マネージド自動バックアップ) | 即時直接保存(GCS SDKによるリクエスト内書き込み) |
| ランニングコスト | 中(VMの定額月額料金:約1,500円〜) | 高(DB常時起動の定額料金:約2,000円〜) | 極小(定額固定費0円、GCSの無料枠〜数十円のみ) |
| 安全対策 / メンテナンスフリー度 | 低(OSアップデートやDB障害復旧の手動保守が必要) | 高(Googleによるフルマネージドバックアップ) | 極高(DB障害ゼロ、SQLインジェクション脆弱性自体を原理的に排除) |
💻 ローカル環境での検証 & 稼働手順
📂 すべての設定ファイルと完全なコードはこちらのGitHubリポジトリで公開しています
👉 github.com/kuiswin/170-serverless-cms
(※本リポジトリのソースコードおよびスクリプトは個人・学習・検証目的でご活用いただけます。商用本番環境での利用は自己責任でお願いします。)
※本リポジトリのコードおよび記事のロジック構築には生成AIを活用しています。動作確認は行っておりますが、AI特有の誤ったコード生成(ハルシネーション)や仕様変更、意図しない不具合等が含まれる可能性があります。本番環境へ適用される場合は、セキュリティやクラウド費用(FinOps)をご自身でご確認の上、自己責任にてご運用ください。
-
筆者の自動公開デモ環境:
- 筆者のプライベートクラウド環境では、コンテナを立ち上げるだけで自動的にドメインとSSLが割り当てられる検証環境を用意しており、以下のURLから動作検証ができる状態になっています。
- 自作ブログ・管理画面: https://cms-p8080-170.kuis.win/
- コンテナ環境は常に立ち上げているわけではないため、アクセス時につながらない可能性があります。また、id/pwのログインも失敗するようにしています。
デプロイに成功すると、以下の画像のような自作ブログサービス(Serverless Journal)が立ち上がります:
📦 システム全体構成 & ローカルコンテナの役割
本システムの全体データフローおよびコンテナ構成は以下の通りです。ローカル検証環境(Docker)では、本番の Cloud Run や GCS の挙動を再現するため、以下2つのコンテナが協調して動作しています。
-
gcs-emulator【サードパーティ製定番イメージ / 無改造】:-
イメージ:
fsouza/fake-gcs-server:latest - 役割: Google Cloud Storage の挙動をローカルでシミュレートするエミュレータ。
-
イメージ:
-
cms-app【独自開発 / アプリ・Web UI本体】:-
イメージ:
Dockerfileよりローカルビルド - 役割: PHP-Apache上で動作する超軽量な自作CMS。
-
イメージ:
💡 余談:神様との「管理画面いらんやん」論争
先日参加した 催し物で、神クラスエンジニアの方と雑談した際、作成途中の記事を見てもらって感想聞いたら、「PCから直接gcloud storage cpで GCS へファイル転送すれば、管理画面(Web UI)自体を物理削除できて攻撃面ゼロ・最高にセキュアになりますよ!」 という正論でぶん殴る至極まっとうなフィードバックをいただきました。
たしかに管理画面を捨てた「完全静的運用」にすればセキュリティは最強になります。
ええ。なりますとも。
……しかし、
スマホやブラウザからログインしてポエムを投稿し、Gemini APIがリアルタイムでアイキャッチ画像を自動生成してくれる「Web CMSとしての体験とエンジニアのロマン」があってこそ技術は面白いのです!
「神様。その案却下です!」
🚀 次のステップ:試験環境と本番環境に展開
それでは、GoogleCloud環境にもデプロイメントを実施してみましょう。
設計思想や、一発自動デプロイスクリプトの実装方法などについては、以下の個人ブログで公開していますのでアクセスしてみてください!
👉 【後編:Google Cloud本番デプロイ編】本番一発自動デプロイとFinOpsコスト防衛手順はこちら(ブログ記事リンク)