🌳

GCP開発 Excuse AIで学ぶ生成AIアプリ構築(後編)

に公開

🔁 前編のおさらい

前編では、GCS→Pub/Sub→Cloud Run→GCS という
イベント駆動型のバッチ処理構成 を構築しました。
https://zenn.dev/acntechjp/articles/3764f8722b9e8d

今回の後編では、Webフロントエンド・CI/CD・データ蓄積・分析基盤 を加え、
バッチ処理アプリから 本格的なWebサービス へと発展させていきます。


🌐 Phase 7:Webフロントエンドの追加

バッチ処理だけでは使い勝手が悪いため、Webフロントエンド を追加しました。

🔧 Cloud Runで2つのルートを統合

【ルート①:リアルタイム生成】NEW!
Webフロント → Pub/Sub → Cloud Run → Gemini API → Firestore

【ルート②:バッチ障害処理】既存
GCS(JSON) → Pub/Sub → Cloud Run → Gemini API → GCS + Firestore

💡 Pub/Subで疎結合を維持

どちらのルートも同じCloud Runで処理しますが、メッセージの属性で分岐:

    # Pub/Sub標準形式では "message" が入っている
    pubsub_message = envelope.get('message', {}) or {}
    attributes = pubsub_message.get('attributes', {}) or {}

    # ---------------------------
    # 1) dataフィールド(Base64エンコードされたJSON)を優先的に読む
    # フロントからの直POSTはこちらに必ず入っている想定
    # ---------------------------
    incident_data = None
    raw = pubsub_message.get('data')
    if raw:
        try:
            decoded = json.loads(b64decode(raw).decode('utf-8'))
            if decoded.get('incident_id'):
                # フロント直POST
                incident_data = decoded
            elif decoded.get('bucket') and decoded.get('name'):
                # GCS通知
                bucket_name = bucket_name or decoded.get('bucket')
                object_id   = object_id   or decoded.get('name')

設計のメリット

  • フロントとバッチが疎結合
  • 非同期処理でレスポンス高速
  • 新しい入力元を追加しやすい

⚙️ Phase 8:CI/CDで開発サイクルを加速

🔧 構成

GitHub → Cloud Build → Cloud Run(自動デプロイ)

📋 Step 1: GitHub リポジトリとの連携

GCPコンソールから接続:

  1. Cloud Build > トリガー > 「トリガーを作成」
  2. トリガー設定
    • イベント:「ブランチに push」
    • ブランチ:^main$
    • 構成ファイル:/cloudbuild.yaml

ここが少々難解でしたが、Connect Repositoryで以下のように別途Githubを選びました。


これでmainブランチへのpushが自動的にビルドをトリガーします。


🔐 Step 2: IAM設定(ここで3時間ハマった)

問題:Service Accountが自動作成されない

トリガー作成後、以下のエラーが発生:

ERROR: User [xxxx@cloudbuild.gserviceaccount.com] 
does not have permission to act as service account

AIアシスタントの回答:

「デフォルトのService Accountが自動作成されます」ということを前提にChatGPTとClaudeで質疑が無限ループとなる。。

実際:

  • この機能は 既に廃止済み
  • 手動でService Accountを作成する必要がある
  • AIの知識が古い情報のままだった

解決策:Service Accountの手動作成

2-1. Service Account作成

gcloud iam service-accounts create cloud-build-sa \
  --display-name="Cloud Build Service Account"

2-2. 必要な権限を付与

# Cloud Run管理者権限
  --role="roles/run.admin"

# Service Accountユーザー権限
  --role="roles/iam.serviceAccountUser"

# ログ書き込み権限
  --role="roles/logging.logWriter"

2-3. トリガーにService Accountを紐付け

GCPコンソール:

  1. Cloud Build > トリガー > 該当トリガーを編集
  2. 「Service Account」で作成したSAを選択
  3. 保存

📝 Step 3: cloudbuild.yaml の作成

steps:
  - name: "gcr.io/cloud-builders/gcloud"
    args:
      - "run"
      - "deploy"
      - "excuse-ai-service"
      - "--image=asia-northeast1-docker.pkg.dev/--"
      - "--source=."
      - "--region=asia-northeast1"
      - "--allow-unauthenticated"
      - "--memory=512Mi"
      - "--timeout=300s"
      - "-service-account=excuse-ai-sa@scenic-parity---" # コンテナ実行アカウント

options:
  logging: CLOUD_LOGGING_ONLY

デプロイフローgit push → Cloud Build起動(2〜3分)→ Cloud Run更新


💡 IAM設定で学んだこと

  • AIも万能ではない:最新のGCP仕様変更に追いついていない
  • 公式ドキュメント + コミュニティ:日本語のまとめ記事が解決の糸口に
  • 専用SAを作成:デフォルトのCloud Build SAはもう存在しない

🗃️ Phase 9:Cloud RunでFirestoreに履歴データを残す

生成結果を永続化するため、Firestoreを導入。私見ですが、FirestoreはGCP的にはかなり推しの機能と思われます。RDBMSを作らなくても、JSON取り込みでこれだけの管理が可能です。

from google.cloud import firestore

db = firestore.Client()

    try:
        db.collection('excuses').document(inc_id).set(result)
    except Exception as e:
        print(f"[ERROR] Firestore書き込み失敗: {e}")
        # 再試行しても二重生成の恐れ → ACKで握りつぶし
        return 'Persist failed (ack)', 200


選んだ理由

  • スキーマレスで開発初期に最適
  • SERVER_TIMESTAMPで正確な時刻を自動記録
  • Cloud Functionsとの相性が良い

📈 Phase 10:BigQueryで集計と分析

Firestoreでは大規模集計が困難なため、BigQueryへ定期エクスポート。

🔧 構成

Firestore → Cloud Functions(定期実行) → BigQuery

Cloud Scheduler設定

Cloud Run Function実装

from google.cloud import firestore, bigquery

def firestore_to_bq(request):
    db = firestore.Client()
    bq = bigquery.Client()
    
    rows = []
    for doc in db.collection("excuse_history").stream():
        rows.append(doc.to_dict())
    
    table_id = "<PROJECT_ID>.excuse_ai.logs"
    bq.insert_rows_json(table_id, rows)
    return "OK"

なぜ定期実行か

  • 分析は日次で十分(リアルタイム性不要)
  • ストリーミング挿入よりコスト削減
  • Cloud Run Functionsでサーバーレス運用

📊 Phase 11:Looker Studioで可視化

BigQueryをLooker Studioに接続し、ダッシュボード化。

主な可視化指標

  • 難易度分布:難易度別の円グラフ
  • 日付別件数:日毎の発生件数推移

BigQueryを直接データソースに接続し、URLだけで共有可能。


🏗️ 全体アーキテクチャ(完成版)

🧠 学びと気づき

1. アーキテクチャは段階的に成長

Cloud Run + Gemini(最小構成)
 ↓
+ Pub/Sub(イベント駆動)
 ↓
+ Firestore(データ保存)
 ↓
+ Webフロント(リアルタイム化)
 ↓
+ CI/CD(自動化)
 ↓
+ BigQuery + Looker(分析基盤)

2. Pub/Subが設計の要

  • フロントとバッチを疎結合に保つ
  • 非同期処理で負荷を分散
  • 拡張しやすい

3. マネージドサービスの組み合わせ

各サービスは単機能だが、組み合わせると強力:

  • Pub/Sub + Cloud Run:イベント駆動のスケーラブル処理
  • Firestore + BigQuery:リアルタイム保存 + 大規模分析
  • GitHub + Cloud Build:自動CI/CD

4. Obserbilityは最初から組み込む

後から追加するのではなく、最初からログ・データ保存・分析基盤を設計する。

5. AIだけに頼らない

Cloud BuildのService Account問題で学んだこと:

  • AIの知識は最新とは限らない
  • 公式ドキュメント + 日本語コミュニティ情報を併用

6. コストは意外と安い

フルマネージド構成で全機能を実装しても 月数百円
従量課金で使わなければほぼ0円。


🎯 まとめ

Phase 実装内容 効果
Phase 7 Webフロント追加 バッチ→Webサービス化
Phase 8 CI/CD構築 開発サイクル高速化
Phase 9 Firestore導入 履歴データ蓄積
Phase 10 BigQuery + ETL 大規模分析基盤
Phase 11 Looker Studio 定量評価可視化

Excuse AIは「遊び心のあるテーマ」から始まりましたが、
結果として クラウドAIアプリのおすすめ構成の一例 が作れたのではないかと思います。


🌟 おわりに

結構な長編となりましたが、読んでいただき、ありがとうございました!

GCP学習を始めた当初から、「机上の資格学習」ではなく
「実際に動くものを作りながら理解する」ことができないかと試行錯誤してきました。
結果として Excuse AI は、アプリ開発・CI/CD・分析基盤までを
網羅する実践例になったかなと思います。

そして何より、この段階にいたると「学習」というより技術を道具にして、
「物を作る楽しさ」をとても感じられました。
AIとの協業により、実装自体は1週間ほどで仕上がっています。

Accenture Japan (有志)

Discussion