Eventarc と Workflows でイベント駆動型オーケストレーションを構築する

1. はじめに

f2f35f5c40b91a3c.png c637dcc97f298e7e.png c4a9d5b95f111710.png

Eventarc を使用すると、Cloud Run サービスをさまざまなソースからのイベントに簡単に接続できます。これにより、マイクロサービスが疎結合で分散されたイベント ドリブン アーキテクチャを構築できます。イベントの取り込み、配信、セキュリティ、認可、エラー処理を自動的に行います。

Workflows は、フルマネージドのオーケストレーション プラットフォームで、定義した順序(ワークフロー)でサービスを実行します。これらのワークフローでは、Cloud Run や Cloud Functions でホストされているサービス、Cloud Vision AI や BigQuery などの Google Cloud サービス、任意の HTTP ベースの API を含むサービスを組み合わせることができます。

この Codelab では、画像を処理するマイクロサービスのイベント ドリブン型オーケストレーションを構築します。Workflows を使用して、4 つの画像処理 Cloud Functions の順序、入力、出力をオーケストレートします。次に、Eventarc を使用して、疎結合な方法で Cloud Storage イベントに応答するようにオーケストレーションを有効にします。

最終的には、画像を処理するための柔軟で構造化されたサーバーレス アーキテクチャが完成します。

b75a14a4268cbe73.png

学習内容

  • Eventarc と Workflows の概要
  • Cloud Functions サービスをデプロイする方法
  • Workflows を使用してサービスをオーケストレートする方法
  • Eventarc を使用して Cloud Storage イベントに応答するようにワークフローを設定する方法

2. 設定と要件

セルフペース型の環境設定

  1. Google Cloud Console にログインして、プロジェクトを新規作成するか、既存のプロジェクトを再利用します。Gmail アカウントも Google Workspace アカウントもまだお持ちでない場合は、アカウントを作成してください。

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • プロジェクト名は、このプロジェクトの参加者に表示される名称です。Google API では使用されない文字列です。いつでも更新できます。
  • プロジェクト ID は、すべての Google Cloud プロジェクトにおいて一意でなければならず、不変です(設定後は変更できません)。Cloud コンソールでは一意の文字列が自動生成されます。通常は、この内容を意識する必要はありません。ほとんどの Codelab では、プロジェクト ID(通常は PROJECT_ID と識別されます)を参照する必要があります。生成された ID が好みではない場合は、ランダムに別の ID を生成できます。または、ご自身で試して、利用可能かどうかを確認することもできます。このステップ以降は変更できず、プロジェクトを通して同じ ID になります。
  • なお、3 つ目の値として、一部の API が使用するプロジェクト番号があります。これら 3 つの値について詳しくは、こちらのドキュメントをご覧ください。
  1. 次に、Cloud のリソースや API を使用するために、Cloud コンソールで課金を有効にする必要があります。この Codelab の操作をすべて行って、費用が生じたとしても、少額です。このチュートリアルの終了後に請求が発生しないようにリソースをシャットダウンするには、作成したリソースを削除するか、プロジェクトを削除します。Google Cloud の新規ユーザーは、300 米ドル分の無料トライアル プログラムをご利用いただけます。

Cloud Shell の起動

Google Cloud はノートパソコンからリモートで操作できますが、この Codelab では、Google Cloud Shell(Cloud 上で動作するコマンドライン環境)を使用します。

Google Cloud Console で、右上のツールバーにある Cloud Shell アイコンをクリックします。

Cloud Shell をアクティブにする

プロビジョニングと環境への接続にはそれほど時間はかかりません。完了すると、次のように表示されます。

環境が接続されていることを示す Google Cloud Shell ターミナルのスクリーンショット

この仮想マシンには、必要な開発ツールがすべて用意されています。永続的なホーム ディレクトリが 5 GB 用意されており、Google Cloud で稼働します。そのため、ネットワークのパフォーマンスと認証機能が大幅に向上しています。この Codelab での作業はすべて、ブラウザ内から実行できます。インストールは不要です。

gcloud を設定する

Cloud Shell で、プロジェクト ID と、アプリケーションのデプロイ先にするリージョンを設定します。これらの情報は、PROJECT_ID 変数と REGION 変数として保存します。利用可能なリージョンについては、Cloud Functions のロケーションをご覧ください。

PROJECT_ID=your-project-id
gcloud config set project $PROJECT_ID

ソースコードを取得する

アプリケーションのソースコードは、eventarc-samples リポジトリの processing-pipelines フォルダにあります。

リポジトリのクローンを作成します。

git clone https://github.com/GoogleCloudPlatform/eventarc-samples.git

eventarc-samples/processing-pipelines フォルダに移動します。

cd eventarc-samples/processing-pipelines

3. アーキテクチャの概要

アプリケーションのアーキテクチャは次のとおりです。

7b810e1505054c0c.png

  1. 画像が入力バケットに保存され、Cloud Storage の作成イベントが生成されます。
  2. Cloud Storage の作成イベントは、Cloud Storage トリガーを介して Eventarc によって読み取られ、CloudEvent として Workflows に渡されます。
  3. ワークフローの最初の手順である Filter では、Cloud Functions サービスが Vision API を使用して画像が安全かどうかを判断します。画像が安全な場合、Workflows は次の手順に進みます。
  4. ワークフローの 2 番目のステップである Labeler(Cloud Functions サービス)は、Vision API を使用して画像のラベルを抽出し、ラベルを出力バケットに保存します。
  5. 3 番目のステップの Resizer は、別の Cloud Functions サービスで、ImageSharp を使用して画像のサイズを変更し、サイズ変更した画像を出力バケットに保存します。
  6. 最後のステップでは、別の Cloud Functions サービスである Watermarker が、ImageSharp を使用して Labeler のラベルの透かしをサイズ変更された画像に追加し、画像を出力バケットに保存します。

アプリケーションは Cloud Storage イベントによってトリガーされるため、イベント ドリブンです。画像の処理はワークフローで行われるため、オーケストレーションです。最終的には、画像を処理するための柔軟で構造化されたサーバーレス アーキテクチャのイベント ドリブン オーケストレーションになります。

4. バケットを作成する

ユーザーが画像をアップロードする入力バケットと、画像処理パイプラインが処理済み画像を保存する出力バケットを作成します。

Cloud Shell で次のコマンドを実行します。

REGION=us-central1
BUCKET1=$PROJECT_ID-images-input-$RANDOM
BUCKET2=$PROJECT_ID-images-output-$RANDOM

gsutil mb -l $REGION gs://$BUCKET1
gsutil mb -l $REGION gs://$BUCKET2

5. フィルタ サービスをデプロイする

まず、最初のサービスをデプロイしましょう。この Cloud Functions サービスは、バケットとファイルの情報を受け取り、Vision API で画像が安全かどうかを判断して、結果を返します。

まず、Cloud Functions gen2 と Vision API に必要なサービスを有効にします。

gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  cloudfunctions.googleapis.com \
  run.googleapis.com \
  vision.googleapis.com

最上位の processing-pipelines フォルダ内で、サービスをデプロイします。

SERVICE_NAME=filter

gcloud functions deploy $SERVICE_NAME \
  --gen2 \
  --allow-unauthenticated \
  --runtime dotnet3 \
  --trigger-http \
  --region=$REGION \
  --entry-point Filter.Function \
  --set-build-env-vars GOOGLE_BUILDABLE=image-v3/filter/csharp

関数がデプロイされたら、サービス URL を変数に設定します。これは後で必要になります。

FILTER_URL=$(gcloud functions describe $SERVICE_NAME --region=$REGION --gen2 --format 'value(serviceConfig.uri)')

6. ラベラー サービスをデプロイする

2 番目の Cloud Functions サービスは、バケットとファイルの情報を受け取り、Vision API で画像のラベルを抽出し、ラベルを出力バケットに保存します。

最上位の processing-pipelines フォルダ内で、サービスをデプロイします。

SERVICE_NAME=labeler

gcloud functions deploy $SERVICE_NAME \
  --gen2 \
  --allow-unauthenticated \
  --runtime dotnet3 \
  --trigger-http \
  --region=$REGION \
  --set-env-vars BUCKET=$BUCKET2 \
  --entry-point Labeler.Function \
  --set-build-env-vars GOOGLE_BUILDABLE=image-v2/labeler/csharp

関数がデプロイされたら、サービス URL を変数に設定します。これは後で必要になります。

LABELER_URL=$(gcloud functions describe $SERVICE_NAME --region=$REGION --gen2 --format 'value(serviceConfig.uri)')

7. リサイザー サービスをデプロイする

この Cloud Functions サービスは、バケットとファイルの情報を受け取り、ImageSharp を使用して画像のサイズを変更し、出力バケットに画像を保存します。

最上位の processing-pipelines フォルダ内で、サービスをデプロイします。

SERVICE_NAME=resizer

gcloud functions deploy $SERVICE_NAME \
  --gen2 \
  --allow-unauthenticated \
  --runtime dotnet3 \
  --trigger-http \
  --region=$REGION \
  --set-env-vars BUCKET=$BUCKET2 \
  --entry-point Resizer.Function \
  --set-build-env-vars GOOGLE_BUILDABLE=image-v2/resizer/csharp \
  --timeout=120s

リサイザー関数に処理時間を追加するために、timeout 値を 2 分に設定します。

関数がデプロイされたら、サービス URL を変数に設定します。これは後で必要になります。

RESIZER_URL=$(gcloud functions describe $SERVICE_NAME --region=$REGION --gen2 --format 'value(serviceConfig.uri)')

8. ウォーターマーカー サービスをデプロイする

この Cloud Functions サービスは、バケット、ファイル、ラベルの情報を受け取り、ファイルを読み取ります。次に、ImageSharp を使用してラベルを透かしとして画像に追加し、画像を出力バケットに保存します。

最上位の processing-pipelines フォルダ内で、サービスをデプロイします。

SERVICE_NAME=watermarker

gcloud functions deploy $SERVICE_NAME \
  --gen2 \
  --allow-unauthenticated \
  --runtime dotnet3 \
  --trigger-http \
  --region=$REGION \
  --set-env-vars BUCKET=$BUCKET2 \
  --entry-point Watermarker.Function \
  --set-build-env-vars GOOGLE_BUILDABLE=image-v2/watermarker/csharp

関数がデプロイされたら、サービス URL を変数に設定します。これは後で必要になります。

WATERMARKER_URL=$(gcloud functions describe $SERVICE_NAME --region=$REGION --gen2 --format 'value(serviceConfig.uri)')

この時点で、4 つの Cloud Functions すべてがデプロイされ、実行されているはずです。

fe662925cb0121e9.png

9. ワークフローを定義してデプロイする

Workflows を使用して、フィルタ、ラベル付け、サイズ変更、透かし入れの各サービスをワークフローに統合します。Workflows は、定義した順序とパラメータでこれらのサービスを呼び出すようにオーケストレートします。

まず、Workflows に必要なサービスを有効にします。

gcloud services enable \
  workflows.googleapis.com \
  workflowexecutions.googleapis.com

定義

ワークフローは CloudEvent をパラメータとして受け取ります。これは、トリガーを作成すると Eventarc から取得されます。最初の 2 つのステップで、Workflows はイベントをロギングし、イベントからバケットとファイル情報を抽出します。

main:
  params: [event]
  steps:
  - log_event:
      call: sys.log
      args:
          text: ${event}
          severity: INFO
  - extract_bucket_and_file:
      assign:
      - bucket: ${event.data.bucket}
      - file: ${event.data.name}

filter ステップでは、Workflows は前にデプロイしたフィルタ サービスを呼び出します。その後、ファイルの安全性をログに記録して確認します。

  - filter:
      call: http.post
      args:
        url: FILTER_URL # TODO: Replace
        auth:
          type: OIDC
        body:
            bucket: ${bucket}
            file: ${file}
      result: filterResponse
  - log_safety:
      call: sys.log
      args:
          text: ${filterResponse.body.safe}
          severity: INFO
  - check_safety:
      switch:
        - condition: ${filterResponse.body.safe == true}
          next: label
      next: end

label ステップで、Workflows はラベラー サービスを呼び出し、レスポンス(上位 3 つのラベル)を取得します。

  - label:
      call: http.post
      args:
        url: LABELER_URL # TODO: Replace
        auth:
          type: OIDC
        body:
            bucket: ${bucket}
            file: ${file}
      result: labelResponse

resize ステップで、Workflows はサイズ変更サービスを呼び出し、レスポンス(サイズ変更された画像のバケットとファイル)を取得します。

  - resize:
      call: http.post
      args:
        url: RESIZER_URL # TODO: Replace
        auth:
          type: OIDC
        body:
            bucket: ${bucket}
            file: ${file}
      result: resizeResponse

watermark ステップで、Workflows はサイズ変更された画像とラベルを使用して透かしサービスを呼び出し、結果(サイズ変更され透かしが追加された画像)を取得します。

  - watermark:
      call: http.post
      args:
        url: WATERMARKER_URL # TODO: Replace
        auth:
          type: OIDC
        body:
            bucket: ${resizeResponse.body.bucket}
            file: ${resizeResponse.body.file}
            labels: ${labelResponse.body.labels}
      result: watermarkResponse

final ステップでは、Workflows はラベラー、リサイザー、ウォーターマーカー サービスから HTTP ステータス コードを返します。

  - final:
      return:
        label: ${labelResponse.code}
        resize: ${resizeResponse.code}
        watermark: ${watermarkResponse.code}

デプロイ

ワークフローをデプロイする前に、サービス URL がデプロイされた関数の URL に手動で置き換えられているか、sed を使用して置き換えられていることを確認してください。

最上位の processing-pipelines フォルダ内で、workflows.yaml ファイルが配置されている image-v3 フォルダに移動します。

cd image-v3/

sed を実行して、プレースホルダ URL をデプロイされたサービスの実際の URL に置き換えます。

sed -i -e "s|FILTER_URL|${FILTER_URL}|" workflow.yaml
sed -i -e "s|LABELER_URL|${LABELER_URL}|" workflow.yaml
sed -i -e "s|RESIZER_URL|${RESIZER_URL}|" workflow.yaml
sed -i -e "s|WATERMARKER_URL|${WATERMARKER_URL}|" workflow.yaml

ワークフローをデプロイします。

WORKFLOW_NAME=image-processing

gcloud workflows deploy $WORKFLOW_NAME \
    --source=workflow.yaml \
    --location=$REGION

数秒後に、コンソールにワークフローがデプロイされます。

a5f537f2b3f3bd3.png

10. トリガーを作成する

ワークフローがデプロイされたので、最後のステップとして、Eventarc トリガーを使用して Cloud Storage イベントに接続します。

1 回限りの設定

まず、Eventarc に必要なサービスを有効にします。

gcloud services enable \
 eventarc.googleapis.com

Eventarc トリガーで使用するサービス アカウントを作成します。

SERVICE_ACCOUNT=eventarc-trigger-imageproc-sa

gcloud iam service-accounts create $SERVICE_ACCOUNT \
  --display-name="Eventarc trigger image processing service account"

サービス アカウントが Eventarc から Workflows を呼び出すことができるように、workflows.invoker ロールを付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --role roles/workflows.invoker \
  --member serviceAccount:$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

eventarc.eventReceiver ロールを付与します。これにより、サービス アカウントを

Cloud Storage トリガー:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --role roles/eventarc.eventReceiver \
  --member serviceAccount:$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

Cloud Storage サービス アカウントに pubsub.publisher ロールを付与します。これは、Eventarc の Cloud Storage トリガーに必要です。

STORAGE_SERVICE_ACCOUNT="$(gsutil kms serviceaccount -p $PROJECT_ID)"

gcloud projects add-iam-policy-binding $PROJECT_ID \
    --member serviceAccount:$STORAGE_SERVICE_ACCOUNT \
    --role roles/pubsub.publisher

作成

次のコマンドを実行してトリガーを作成します。このトリガーは、入力 Cloud Storage バケットからの新しいファイル作成イベントをフィルタし、前に定義したワークフローに渡します。

TRIGGER_NAME=trigger-image-processing

gcloud eventarc triggers create $TRIGGER_NAME \
  --location=$REGION \
  --destination-workflow=$WORKFLOW_NAME \
  --destination-workflow-location=$REGION \
  --event-filters="type=google.cloud.storage.object.v1.finalized" \
  --event-filters="bucket=$BUCKET1" \
  --service-account=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

Cloud Console の Eventarc セクションで、トリガーが作成され、準備が整っていることを確認できます。

a5f4301863d7d9e4.png

11. パイプラインをテストする

画像処理パイプラインは、Cloud Storage からイベントを受信する準備が整いました。パイプラインをテストするには、入力バケットに画像をアップロードします。

gsutil cp beach.jpg gs://$BUCKET1

写真をアップロードするとすぐに、ワークフローの実行がアクティブな状態になります。

2c914341950b5fde.png

1 分ほど経過すると、実行が成功したことがわかります。ワークフローの入力と出力も確認できます。

9abba6c28c51a9b5.png

出力バケットの内容を一覧表示すると、サイズ変更された画像、サイズ変更されて透かしが入った画像、画像のラベルが表示されます。

gsutil ls gs://$BUCKET2

gs://$PROJECT_ID-images-output-$RANDOM/beach-400x400-watermark.jpeg
gs://$PROJECT_ID-images-output-$RANDOM/beach-400x400.png
gs://$PROJECT_ID-images-output-$RANDOM/beach-labels.txt

結果を確認するには、サイズ変更と透かしの追加を行った画像を開きます。

46d375cb05a8aae4.jpeg

12. 完了

お疲れさまでした。これでこの Codelab は終了です。

学習した内容

  • Eventarc と Workflows の概要
  • Cloud Functions サービスをデプロイする方法
  • Workflows を使用してサービスをオーケストレートする方法
  • Eventarc を使用して Cloud Storage イベントに応答するようにワークフローを設定する方法