C# での Google Cloud Functions

1. はじめに

Google Cloud Run functions は、イベント ドリブンのサーバーレス コンピューティング プラットフォームです。Cloud Run functions を使用すると、リソースのプロビジョニングや、要件の変化に対応するためのスケーリングを気にせずにコードを記述できます。

Cloud Run functions には次の 2 つのタイプがあります。

  • HTTP 関数は HTTP リクエストに応答します。
  • イベント関数は、Cloud Pub/Sub へのメッセージのパブリッシュや Cloud Storage へのファイルのアップロードなどのイベントによってトリガーされます。

efb3268e3b74ed4f.png

この Codelab では、C# で独自の Cloud Run 関数を作成する方法について説明します。具体的には、さまざまな Google Cloud ソースからの HTTP と CloudEvents に応答する C# 関数をデプロイします。

学習内容

  • Functions Framework for .NET。
  • HTTP 関数を作成する方法。
  • Cloud Storage イベントに応答するイベント トリガー関数を作成する方法。
  • Cloud Pub/Sub イベントに応答するイベント トリガー関数を作成する方法。
  • 任意のイベントに応答するイベント トリガー関数を作成する方法。

2. 設定と要件

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

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

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.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 アイコンをクリックします。

55efc1aaa7a4d3ad.png

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

7ffe5cbb04455448.png

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

3. 始める前に

Cloud Shell で次のコマンドを実行して、必要なサービスを有効にします。

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

次に、リージョンを設定します。

REGION=<YOUR_REGION>

この Codelab では、必要な Eventarc 権限と Cloud Run 起動元ロールを持つサービス アカウントを作成し、Cloud Storage からイベントを受信して Cloud Run 関数を呼び出します。

まず、サービス アカウントを作成します。

PROJECT_ID=$(gcloud config get-value core/project)
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

SERVICE_ACCOUNT="cloud-run-functions"
SERVICE_ACCOUNT_ADDRESS=$SERVICE_ACCOUNT@$PROJECT_ID.iam.gserviceaccount.com

gcloud iam service-accounts create $SERVICE_ACCOUNT \
  --display-name="Cloud Run functions Eventarc service account"

次に、トリガーがイベント プロバイダからイベントを受信できるように、プロジェクトの Eventarc イベント受信者のロール(roles/eventarc.eventReceiver)を Eventarc トリガーに関連付けられたサービス アカウントに付与します。

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role=roles/eventarc.eventReceiver

次に、サービス アカウントに Cloud Run 起動元ロールを付与して、関数を呼び出せるようにします。

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member serviceAccount:$SERVICE_ACCOUNT_ADDRESS \
  --role=roles/run.invoker

4. Functions Framework for .NET

Functions Framework for .NET は、Google Cloud Functions チームが提供する、ポータブルな .NET 関数を記述するためのオープンソースの FaaS(Functions as a Service)フレームワークです。

Functions Framework を使用すると、次のようなさまざまな環境で実行される軽量関数を作成できます。

  • Google Cloud Run functions
  • ローカル開発マシン
  • Cloud Run および Cloud Run on GKE
  • Knative ベースの環境

この Codelab では、Functions Framework for .NET とそのテンプレートを使用して、C# で Cloud Functions を作成してデプロイします。

Cloud Shell で次のコマンドを実行して、dotnet の Cloud Functions テンプレートをインストールします。

dotnet new install Google.Cloud.Functions.Templates

これにより、dotnet の 3 つのテンプレートがインストールされます。各テンプレートは C#、F#、VB で使用できます(ただし、このラボでは C# のみを使用します)。テンプレートがインストールされていることを確認するには、次のコマンドを実行します。

dotnet new list

Templates                                                 Short Name            
-----------------------------------------------------------------------
Google Cloud Functions CloudEvent Function                gcf-event
Google Cloud Functions CloudEvent Function (Untyped)      gcf-untyped-event
Google Cloud Functions HttpFunction                       gcf-http

5. HTTP 関数

HTTP リクエストに応答する HTTP 関数を作成してデプロイします。

gcf-http テンプレートを使用して HTTP 関数を作成します。

mkdir HelloHttp
cd HelloHttp
dotnet new gcf-http

これにより、プロジェクトと HTTP リクエストに応答する Function.cs ファイルが作成されます。

.csproj ファイルで、ターゲット フレームワークを net8.0 に変更します。

<TargetFramework>net8.0</TargetFramework>

Cloud Run 関数を Cloud Run に直接デプロイするには、次のコマンドを実行します。

gcloud beta run deploy hello-http-function \
    --source . \
    --function HelloHttp.Function \
    --base-image dotnet8 \
    --region $REGION \
    --allow-unauthenticated

Cloud Functions 第 2 世代としてデプロイする場合は、次のコマンドを使用します。

gcloud functions deploy hello-http-function \
    --allow-unauthenticated \
    --entry-point HelloHttp.Function \
    --gen2 \
    --region $REGION \
    --runtime dotnet8 \
    --trigger-http

関数がデプロイされたら、次の curl コマンドを使用して関数を呼び出すことができます。

SERVICE_URL=$(gcloud run services describe hello-http-function --platform managed --region $REGION --format 'value(status.url)')

curl $SERVICE_URL

6. CloudEvent 関数 - GCS

Google Cloud Storage(GCS)イベントに応答する CloudEvent 関数を作成してデプロイします。

まず、Cloud Storage バケットを作成します。これは、後でイベントをリッスンするバケットです。

BUCKET_NAME="cloud-functions-bucket-${PROJECT_ID}"
gsutil mb -l us-central1 gs://${BUCKET_NAME}

gcf-event テンプレートを使用して CloudEvent 関数を作成します。

cd ..
mkdir HelloGcs
cd HelloGcs
dotnet new gcf-event

これにより、プロジェクトと、CloudEvent リクエストに応答する Function.cs ファイルが作成されます。また、CloudEvent のデータを StorageObjectData に解析します。

.csproj ファイルで、ターゲット フレームワークを net8.0 に変更します。

<TargetFramework>net8.0</TargetFramework>

Cloud Run 関数を Cloud Run に直接デプロイするには、まず関数をデプロイしてから、その関数のトリガーを作成します。

gcloud beta run deploy hello-gcs-function \
      --source . \
      --function HelloGcs.Function \
      --region $REGION \
      --base-image dotnet8 \
      --no-allow-unauthenticated

Cloud Run 関数のトリガーを作成します。

BUCKET_REGION=$REGION

gcloud eventarc triggers create hello-gcs-function-trigger \
     --location=$REGION \
     --destination-run-service=hello-gcs-function \
    --destination-run-region=$BUCKET_REGION \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=$BUCKET_NAME" \
     --service-account=$SERVICE_ACCOUNT_ADDRESS

Cloud Functions 第 2 世代としてデプロイする場合は、次のコマンドを使用して、trigger-event フラグと trigger-resource フラグを使用して関数をデプロイできます。

gcloud functions deploy hello-gcs-function \
    --allow-unauthenticated \
    --entry-point HelloGcs.Function \
    --gen2 \
    --region $REGION \
    --runtime dotnet8 \
    --trigger-event google.storage.object.finalize \
    --trigger-resource ${BUCKET_NAME} \
    --service-account=$SERVICE_ACCOUNT_ADDRESS

数分後、関数が Cloud コンソールに表示されます。

c28654d74bb31420.png

ストレージ バケットにファイルをアップロードして関数をトリガーします。

echo "Hello from Storage" > random.txt
gsutil cp random.txt gs://${BUCKET_NAME}

ログを読み取って、関数がトリガーされたことを確認します。

Cloud Run 関数の場合は、次のコマンドを実行します。

gcloud logging read "resource.labels.service_name=hello-gcs-function AND textPayload: Name" --format=json

第 2 世代の関数の場合は、次のコマンドを実行します。

gcloud functions logs read hello-gcs-function \
    --gen2 \
    --region us-central1

7. CloudEvent 関数 - Pub/Sub

Cloud Pub/Sub イベントに応答する CloudEvent 関数を作成してデプロイします。

まず、イベントを出力する Cloud Pub/Sub トピックを作成します。

TOPIC_NAME=cloud-functions-topic
gcloud pubsub topics create ${TOPIC_NAME}

gcf-event テンプレートを使用して CloudEvent 関数を作成します。

cd ..
mkdir HelloPubSub
cd HelloPubSub
dotnet new gcf-event

これにより、プロジェクトと、CloudEvent リクエストに応答する Function.cs ファイルが作成されます。また、デフォルトでは CloudEvent のデータを StorageObjectData に解析します。

.csproj ファイルで、ターゲット フレームワークを net8.0 に変更します。

<TargetFramework>net8.0</TargetFramework>

Pub/Sub メッセージを解析するには、StorageObjectDataMessagePublishedData に変更します。Google.Events.Protobuf.Cloud.Storage.V1Google.Events.Protobuf.Cloud.PubSub.V1 に変更します。

最終的に、関数は次のようになります。

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.PubSub.V1;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace HelloPubSub;

public class Function : ICloudEventFunction<MessagePublishedData>
{
    public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
    {
        var nameFromMessage = data.Message?.TextData;
        var name = string.IsNullOrEmpty(nameFromMessage) ? "world" : nameFromMessage;
        Console.WriteLine($"Hello {name}");
        return Task.CompletedTask;
    }
}

Cloud Run 関数を Cloud Run に直接デプロイするには、まず関数をデプロイしてから、その関数のトリガーを作成します。

gcloud beta run deploy hello-pubsub-function \
      --source . \
      --function HelloPubSub.Function \
      --region $REGION \
      --base-image dotnet8 \
      --no-allow-unauthenticated \
      --service-account=$SERVICE_ACCOUNT_ADDRESS

Cloud Run 関数のトリガーを作成します。

gcloud eventarc triggers create my-pubsub-trigger \
    --location=$REGION \
    --service-account=$SERVICE_ACCOUNT_ADDRESS \
    --destination-run-service=hello-pubsub-function \
    --destination-run-region=$REGION \
    --destination-run-path="/" \
    --event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \
    --transport-topic=projects/$PROJECT_ID/topics/$TOPIC_NAME

Cloud Functions 第 2 世代としてデプロイする場合は、次のコマンドを使用して trigger-topic フラグを使用して関数をデプロイできます。

gcloud functions deploy hello-pubsub-function \
    --allow-unauthenticated \
    --entry-point HelloPubSub.Function \
    --gen2 \
    --region us-central1 \
    --runtime dotnet8 \
    --trigger-topic ${TOPIC_NAME}

数分後、関数が Cloud コンソールに表示されます。

3443808da7caf3bc.png

トピックにメッセージをパブリッシュして関数をトリガーします。

gcloud pubsub topics publish ${TOPIC_NAME} --message="World"

ログを読み取って、関数がトリガーされたことを確認します。

Cloud Run 関数の場合は、次のコマンドを実行します。

gcloud logging read "resource.labels.service_name=hello-pubsub-function AND textPayload: World" --format=json

第 2 世代の関数の場合は、次のコマンドを実行します。

gcloud functions logs read hello-pubsub-function \
    --gen2 \
    --region us-central1

8. CloudEvent 関数 - 型なし

CloudEvents をテストしていて、まだ commit するペイロード データモデルがない場合は、関数で任意の Cloud Event を処理できるように、型指定のない CloudEvent 関数を使用できます。

gcf-untyped-event テンプレートを使用して CloudEvent 関数を作成します。

cd ..
mkdir HelloUntyped
cd HelloUntyped
dotnet new gcf-untyped-event

これにより、CloudEvent のリクエストに応答するプロジェクトと Function.cs ファイルが作成されます。CloudEvent のデータは解析されません。

.csproj ファイルで、ターゲット フレームワークを net8.0 に変更します。

<TargetFramework>net8.0</TargetFramework>

Cloud Run 関数を Cloud Run に直接デプロイするには、まず関数をデプロイしてから、その関数のトリガーを作成します。

gcloud beta run deploy hello-untyped-function \
      --source . \
      --function HelloUntyped.Function \
      --region $REGION \
      --base-image dotnet8 \
      --no-allow-unauthenticated

Cloud Run 関数のトリガーを作成します。

BUCKET_REGION=$REGION

gcloud eventarc triggers create hello-untyped-function-trigger \
     --location=$REGION \
     --destination-run-service=hello-untyped-function \
    --destination-run-region=$BUCKET_REGION \
     --event-filters="type=google.cloud.storage.object.v1.finalized" \
     --event-filters="bucket=$BUCKET_NAME" \
     --service-account=$SERVICE_ACCOUNT_ADDRESS

Cloud Functions 第 2 世代としてデプロイする場合は、次のコマンドを使用して、trigger-event フラグと trigger-resource フラグを使用して関数をデプロイできます。

gcloud functions deploy hello-untyped-function \
    --allow-unauthenticated \
    --entry-point HelloUntyped.Function \
    --gen2 \
    --region us-central1 \
    --runtime dotnet8 \
    --trigger-event google.storage.object.finalize \
    --trigger-resource ${BUCKET_NAME}

ファイルがストレージ バケットにアップロードされると、関数がトリガーされます。

数分後、関数が Cloud コンソールに表示されます。

afe56530826787c6.png

ストレージ バケットにファイルをアップロードして関数をトリガーします。

echo "Hello from Storage" > random.txt
gsutil cp random.txt gs://${BUCKET_NAME}

ログを読み取って、関数がトリガーされたことを確認します。

Cloud Run 関数の場合は、次のコマンドを実行します。

gcloud logging read "resource.labels.service_name=hello-gcs-function AND textPayload: Name" --format=json

第 2 世代の関数の場合は、次のコマンドを実行します。

gcloud functions logs read hello-untyped-function \
    --gen2 \
    --region us-central1

9. 完了

以上で、この Codelab は完了です。

学習した内容

  • Functions Framework for .NET。
  • HTTP Cloud Functions の関数を作成する方法。
  • Cloud Storage イベントに応答する CloudEvent 関数を作成する方法。
  • Cloud Pub/Sub イベントに応答する CloudEvent 関数を作成する方法。
  • 任意のイベントに応答する CloudEvent 関数を作成する方法。