𝕏 f B! L
案件・求人数 12,345
案件を探す(準備中) エージェントを探す(準備中) お役立ち情報 ログイン
案件・求人数 12,345
Google Cloud Eventarcでイベント駆動アプリケーションを構築する方法【完全ガイド】

Google Cloud Eventarcでイベント駆動アプリケーションを構築する方法【完全ガイド】

Google CloudEventarcイベント駆動Cloud Runサーバーレス
目次
⚡ 3秒でわかる!この記事のポイント
  • EventarcはGoogle Cloudのイベント駆動基盤で、120以上のGCPサービスからイベントを受信可能
  • Cloud Run・Cloud Functions・GKEをイベントターゲットとして設定し、サーバーレスで処理
  • Eventarc案件はSES市場で単価70-90万円、クラウドネイティブ設計スキルとして需要が急増中

「GCSにファイルがアップロードされたら自動処理したい」「Firestoreの変更をリアルタイムで検知したい」

Google Cloud Eventarcは、こうしたイベント駆動の要件を統一的なインターフェースで実現するサービスです。GCPの各サービスが発行するイベントをキャッチし、Cloud RunやCloud Functionsに自動的にルーティングできます。

この記事では、Eventarcの基本概念から実践的なユースケースまでを体系的に解説します。

この記事でわかること
  • Eventarcのアーキテクチャと基本概念
  • トリガーの設定方法(直接・Pub/Sub・Audit Log経由)
  • Cloud Run / Cloud Functionsとの連携パターン
  • 実践的なユースケース(画像処理、データ連携、監査)
  • SES現場でのイベント駆動案件の需要と必要スキル

Eventarcのアーキテクチャ

イベントの流れ

Eventarcは、イベントソース → トリガー → ターゲットの3層構造で動作します。

[イベントソース]          [トリガー]           [ターゲット]
GCS バケット        →  Eventarc Trigger  →  Cloud Run
Firestore          →  (フィルタ条件)      →  Cloud Functions
Cloud Audit Logs   →                      →  GKE
Pub/Sub Topic      →                      →  Workflows
カスタムイベント    →                      →

イベントタイプ

Eventarcは3種類のイベントソースをサポートしています。

イベントタイプ説明
直接イベントGCPサービスが直接発行GCSオブジェクト作成、Firestore更新
Cloud Audit Log監査ログから生成BigQueryジョブ完了、IAM変更
Pub/SubPub/Subトピック経由カスタムイベント、サードパーティ連携

トリガーの設定

GCSイベントトリガー

ファイルアップロードを検知して自動処理する最も基本的なパターンです。

# GCSオブジェクト作成イベントのトリガー
gcloud eventarc triggers create gcs-upload-trigger \
  --location=asia-northeast1 \
  --destination-run-service=image-processor \
  --destination-run-region=asia-northeast1 \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=my-uploads-bucket" \
  --service-account=eventarc-sa@PROJECT_ID.iam.gserviceaccount.com

Terraformでの設定

# terraform/eventarc.tf

# GCSアップロードトリガー
resource "google_eventarc_trigger" "gcs_upload" {
  name     = "gcs-upload-trigger"
  location = "asia-northeast1"
  project  = var.project_id

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.storage.object.v1.finalized"
  }

  matching_criteria {
    attribute = "bucket"
    value     = google_storage_bucket.uploads.name
  }

  destination {
    cloud_run_service {
      service = google_cloud_run_v2_service.image_processor.name
      region  = "asia-northeast1"
    }
  }

  service_account = google_service_account.eventarc_sa.email
}

# Firestoreドキュメント変更トリガー
resource "google_eventarc_trigger" "firestore_update" {
  name     = "firestore-update-trigger"
  location = "asia-northeast1"
  project  = var.project_id

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.firestore.document.v1.written"
  }

  matching_criteria {
    attribute = "database"
    value     = "(default)"
  }

  destination {
    cloud_run_service {
      service = google_cloud_run_v2_service.data_sync.name
      region  = "asia-northeast1"
    }
  }

  service_account = google_service_account.eventarc_sa.email
}

# Audit Logトリガー(BigQueryジョブ完了)
resource "google_eventarc_trigger" "bigquery_job_complete" {
  name     = "bigquery-job-trigger"
  location = "asia-northeast1"
  project  = var.project_id

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.audit.log.v1.written"
  }

  matching_criteria {
    attribute = "serviceName"
    value     = "bigquery.googleapis.com"
  }

  matching_criteria {
    attribute = "methodName"
    value     = "google.cloud.bigquery.v2.JobService.InsertJob"
  }

  destination {
    cloud_run_service {
      service = google_cloud_run_v2_service.job_monitor.name
      region  = "asia-northeast1"
    }
  }

  service_account = google_service_account.eventarc_sa.email
}

Cloud Runイベントハンドラの実装

画像処理サービス

GCSにアップロードされた画像を自動的にリサイズ・最適化するサービスの実装例です。

// src/handlers/image-processor.ts
import express from 'express';
import sharp from 'sharp';
import { Storage } from '@google-cloud/storage';

const app = express();
const storage = new Storage();

interface CloudEvent {
  id: string;
  type: string;
  source: string;
  time: string;
  data: {
    bucket: string;
    name: string;
    contentType: string;
    size: string;
    metageneration: string;
  };
}

app.post('/', express.json(), async (req, res) => {
  const event: CloudEvent = req.body;

  console.log(`Processing event: ${event.id}`, {
    bucket: event.data.bucket,
    object: event.data.name,
    contentType: event.data.contentType,
  });

  // 画像ファイルのみ処理
  if (!event.data.contentType?.startsWith('image/')) {
    console.log('Skipping non-image file');
    return res.status(200).send('OK - skipped');
  }

  // 処理済みファイルの再処理を防止
  if (event.data.name.startsWith('processed/')) {
    console.log('Skipping already processed file');
    return res.status(200).send('OK - already processed');
  }

  try {
    const bucket = storage.bucket(event.data.bucket);
    const file = bucket.file(event.data.name);

    // 元画像をダウンロード
    const [buffer] = await file.download();

    // リサイズバリエーションを生成
    const variants = [
      { suffix: 'thumb', width: 150, height: 150 },
      { suffix: 'medium', width: 800, height: 600 },
      { suffix: 'large', width: 1920, height: 1080 },
      { suffix: 'og', width: 1200, height: 630 },
    ];

    const results = await Promise.all(
      variants.map(async (variant) => {
        const processed = await sharp(buffer)
          .resize(variant.width, variant.height, {
            fit: 'cover',
            position: 'center',
          })
          .webp({ quality: 85 })
          .toBuffer();

        const outputPath = `processed/${variant.suffix}/${event.data.name.replace(/\.[^.]+$/, '.webp')}`;
        await bucket.file(outputPath).save(processed, {
          contentType: 'image/webp',
          metadata: {
            originalFile: event.data.name,
            variant: variant.suffix,
            processedAt: new Date().toISOString(),
          },
        });

        return { variant: variant.suffix, path: outputPath, size: processed.length };
      })
    );

    console.log('Image processing complete', { results });
    res.status(200).json({ success: true, variants: results });
  } catch (error) {
    console.error('Image processing failed', error);
    res.status(500).json({ error: 'Processing failed' });
  }
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`Image processor listening on port ${PORT}`);
});

データ同期サービス

Firestoreの変更をリアルタイムで検知し、他のシステムに同期するパターンです。

// src/handlers/data-sync.ts
import express from 'express';
import { BigQuery } from '@google-cloud/bigquery';
import { PubSub } from '@google-cloud/pubsub';

const app = express();
const bigquery = new BigQuery();
const pubsub = new PubSub();

interface FirestoreEvent {
  id: string;
  type: string;
  data: {
    oldValue?: { fields: Record<string, any> };
    value?: { fields: Record<string, any> };
    updateMask?: { fieldPaths: string[] };
  };
}

app.post('/', express.json(), async (req, res) => {
  const event: FirestoreEvent = req.body;

  try {
    // 変更の種類を判定
    const changeType = getChangeType(event);
    console.log(`Firestore ${changeType}:`, { eventId: event.id });

    // 1. BigQueryに変更ログを書き込み
    await bigquery
      .dataset('analytics')
      .table('firestore_changes')
      .insert([{
        event_id: event.id,
        change_type: changeType,
        document_data: JSON.stringify(event.data.value?.fields),
        changed_fields: event.data.updateMask?.fieldPaths?.join(','),
        timestamp: new Date().toISOString(),
      }]);

    // 2. 変更通知をPub/Subにパブリッシュ
    await pubsub.topic('data-changes').publishMessage({
      json: {
        changeType,
        data: event.data.value?.fields,
        oldData: event.data.oldValue?.fields,
      },
      attributes: { changeType },
    });

    res.status(200).send('OK');
  } catch (error) {
    console.error('Data sync failed', error);
    res.status(500).send('Error');
  }
});

function getChangeType(event: FirestoreEvent): string {
  if (!event.data.oldValue && event.data.value) return 'CREATE';
  if (event.data.oldValue && !event.data.value) return 'DELETE';
  return 'UPDATE';
}

Google Cloud Eventarcのイベント駆動アーキテクチャ全体像

実践的なユースケース

ユースケース1: セキュリティ監査の自動化

# IAMポリシー変更を検知してアラートを送信
gcloud eventarc triggers create iam-audit-trigger \
  --location=asia-northeast1 \
  --destination-run-service=security-auditor \
  --event-filters="type=google.cloud.audit.log.v1.written" \
  --event-filters="serviceName=iam.googleapis.com" \
  --event-filters="methodName=google.iam.admin.v1.SetIamPolicy" \
  --service-account=eventarc-sa@PROJECT_ID.iam.gserviceaccount.com

ユースケース2: CI/CDパイプラインの自動トリガー

# Container Registryへのイメージプッシュでデプロイをトリガー
gcloud eventarc triggers create deploy-trigger \
  --location=asia-northeast1 \
  --destination-run-service=deployment-manager \
  --event-filters="type=google.cloud.audit.log.v1.written" \
  --event-filters="serviceName=artifactregistry.googleapis.com" \
  --event-filters="methodName=docker.upload" \
  --service-account=eventarc-sa@PROJECT_ID.iam.gserviceaccount.com

ユースケース3: コスト異常検知

# Billing Export → BigQuery → Eventarc → アラート
resource "google_eventarc_trigger" "cost_anomaly" {
  name     = "cost-anomaly-trigger"
  location = "asia-northeast1"

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.audit.log.v1.written"
  }

  matching_criteria {
    attribute = "serviceName"
    value     = "bigquery.googleapis.com"
  }

  destination {
    cloud_run_service {
      service = google_cloud_run_v2_service.cost_monitor.name
      region  = "asia-northeast1"
    }
  }

  service_account = google_service_account.eventarc_sa.email
}

カスタムイベントの発行

Pub/Sub経由のカスタムイベント

アプリケーション固有のイベントをEventarcで処理する方法です。

// カスタムイベントの発行
import { PubSub } from '@google-cloud/pubsub';

const pubsub = new PubSub();

async function publishCustomEvent(eventType: string, data: Record<string, any>) {
  const topic = pubsub.topic('custom-events');

  await topic.publishMessage({
    json: {
      specversion: '1.0',
      type: `com.ses-base.${eventType}`,
      source: '/services/api-gateway',
      id: crypto.randomUUID(),
      time: new Date().toISOString(),
      data,
    },
    attributes: {
      eventType,
    },
  });
}

// 使用例
await publishCustomEvent('engineer.registered', {
  engineerId: 'eng-001',
  name: '田中太郎',
  skills: ['TypeScript', 'React', 'AWS'],
});

モニタリングとデバッグ

Eventarcのログ確認

# トリガーの実行ログ
gcloud logging read 'resource.type="cloud_run_revision" AND
  textPayload:"Processing event"' \
  --limit=20 \
  --format=json

# トリガーの失敗ログ
gcloud logging read 'resource.type="eventarc.googleapis.com/Trigger" AND
  severity>=WARNING' \
  --limit=10

# Cloud Monitoringでアラート設定
gcloud alpha monitoring policies create \
  --display-name="Eventarc Trigger Failures" \
  --condition-display-name="High failure rate" \
  --condition-filter='resource.type="cloud_run_revision" AND
    metric.type="run.googleapis.com/request_count" AND
    metric.labels.response_code_class="5xx"' \
  --condition-threshold-value=10 \
  --condition-threshold-duration=300s \
  --notification-channels=CHANNEL_ID

リトライとDead Letter

# リトライポリシーの設定
resource "google_eventarc_trigger" "with_retry" {
  name     = "retry-trigger"
  location = "asia-northeast1"

  matching_criteria {
    attribute = "type"
    value     = "google.cloud.storage.object.v1.finalized"
  }

  destination {
    cloud_run_service {
      service = google_cloud_run_v2_service.processor.name
      region  = "asia-northeast1"
    }
  }

  # Cloud Runのタイムアウトとリトライ
  transport {
    pubsub {
      topic = google_pubsub_topic.eventarc_transport.id
    }
  }

  service_account = google_service_account.eventarc_sa.email
}

# Dead Letter Topic
resource "google_pubsub_subscription" "eventarc_sub_with_dlq" {
  name  = "eventarc-retry-sub"
  topic = google_pubsub_topic.eventarc_transport.id

  retry_policy {
    minimum_backoff = "10s"
    maximum_backoff = "600s"
  }

  dead_letter_policy {
    dead_letter_topic     = google_pubsub_topic.dead_letter.id
    max_delivery_attempts = 5
  }
}

SES現場でのEventarc案件

需要と単価

スキル月単価目安主な案件タイプ
Eventarc設計・構築70-90万円イベント駆動基盤の新規構築
Cloud Run + Eventarc65-85万円サーバーレスアプリ開発
データパイプライン構築75-95万円リアルタイムデータ処理
GCP基盤設計コンサル80-100万円アーキテクチャ設計・移行支援

面談でのアピールポイント

【イベント駆動基盤構築実績】
- Google Cloud Eventarcで120以上のイベントソースを統合管理
- Cloud Run + Eventarcでサーバーレスなデータ処理基盤を構築し
  インフラ運用コストを70%削減
- Audit Logトリガーでセキュリティ監査を自動化し
  コンプライアンス対応工数を月40時間→2時間に短縮
- カスタムイベントとPub/Subで社内システム間のリアルタイム連携を実現

まとめ

Google Cloud Eventarcを活用したイベント駆動アプリケーション構築は、以下のステップで進めるのが効果的です。

  1. イベント設計: ユースケースに応じたイベントタイプの選定
  2. トリガー設定: Terraform/gcloudでEventarcトリガーを構成
  3. ハンドラ実装: Cloud Run/Cloud Functionsでイベント処理ロジックを実装
  4. リトライ・DLQ: 障害時の自動リトライとDead Letter Queueを設定
  5. モニタリング: Cloud MonitoringとLoggingでイベントフローを監視

EventarcスキルはSES市場で需要が高まっており、クラウドネイティブ設計の重要な要素として高単価案件の獲得につながります。

関連記事

SES案件をお探しですか?

SES記事をもっと読む →
🏗️

SES BASE 編集長

SES業界歴10年以上のメンバーが在籍する編集チーム。SES企業での営業・エンジニア経験、フリーランス独立経験を持つメンバーが、業界のリアルな情報をお届けします。

📊 業界データに基づく記事制作 🔍 IPA・経済産業省データ参照 💼 SES実務経験者が執筆・監修