1. はじめに
概要
Cloud Run functions は、Cloud Run と Eventarc を活用した Google Cloud の Functions as a Service ソリューションです。パフォーマンスとスケーラビリティのさらに高度な制御、関数のランタイムに関するより細かい制御、90 を超えるイベントソースを使ったトリガーを実現できます。
この Codelab では、HTTP 呼び出しに応答し、Pub/Sub メッセージと Cloud Audit Logs によってトリガーされる Cloud Run functions の関数を作成します。
この Codelab では、--base-image
フラグを使用してベースイメージを指定することで、関数デプロイのベースイメージの自動更新も使用します。Cloud Run のベースイメージの自動更新を構成すると、Google はベースイメージのオペレーティング システムと言語ランタイム コンポーネントにセキュリティ パッチを自動的に適用できます。ベースイメージを更新するためにサービスを再ビルドまたは再デプロイする必要はありません。詳細については、ベースイメージの自動更新をご覧ください。
ベースイメージの自動更新を使用しない場合は、この Codelab に示す例から --base-image
フラグを削除します。
学習内容
- Cloud Run functions の概要と、ベースイメージの自動更新の使用方法。
- HTTP 呼び出しに応答する関数を作成する方法。
- Pub/Sub メッセージに応答する関数を記述する方法。
- Cloud Storage イベントに応答する関数を作成する方法。
- 2 つのリビジョン間でトラフィックを分割する方法。
- 最小インスタンスを使用してコールド スタートを回避する方法。
2. 設定と要件
ルートフォルダを作成する
すべての例のルートフォルダを作成します。
mkdir crf-codelab cd crf-codelab
環境変数を設定する
この Codelab 全体で使用する環境変数を設定します。
gcloud config set project <YOUR-PROJECT-ID> REGION=<YOUR_REGION> PROJECT_ID=$(gcloud config get-value project)
API を有効にする
必要なサービスをすべて有効にします。
gcloud services enable \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com \ eventarc.googleapis.com \ run.googleapis.com \ logging.googleapis.com \ pubsub.googleapis.com
3. HTTP 関数
最初の関数では、HTTP リクエストに応答する認証済みの Node.js 関数を作成します。また、10 分のタイムアウトを使用して、関数が HTTP リクエストに応答する時間を増やす方法を示します。
作成
アプリ用のフォルダを作成し、そのフォルダに移動します。
mkdir hello-http cd hello-http
HTTP リクエストに応答する index.js
ファイルを作成します。
const functions = require('@google-cloud/functions-framework'); functions.http('helloWorld', (req, res) => { res.status(200).send('HTTP with Node.js in Cloud Run functions!'); });
依存関係を指定する package.json
ファイルを作成します。
{ "name": "nodejs-run-functions-codelab", "version": "0.0.1", "main": "index.js", "dependencies": { "@google-cloud/functions-framework": "^2.0.0" } }
導入
関数をデプロイします。
gcloud run deploy nodejs-run-function \ --source . \ --function helloWorld \ --base-image nodejs22 \ --region $REGION \ --timeout 600 \ --no-allow-unauthenticated
このコマンドは、Buildpack を使用して、関数ソースコードを本番環境に対応したコンテナ イメージに変換します。
次の点にご注意ください。
--source
フラグは、関数を実行可能なコンテナベースのサービスにビルドするように Cloud Run に指示するために使用されます。--function
フラグ(新規)は、呼び出す関数シグネチャになるように新しいサービスのエントリ ポイントを設定するために使用されます。--base-image
フラグ(新規)は、関数のベースイメージ環境(nodejs22
、python312
、go123
、java21
、dotnet8
、ruby33
、php83
など)を指定します。ベースイメージと各イメージに含まれるパッケージの詳細については、ランタイム ベースイメージをご覧ください。- (省略可)
--timeout
フラグを使用すると、HTTP リクエストに応答するためのタイムアウトを長くできます。この例では、600 秒を使用して 10 分の応答時間を示しています。 - (省略可)関数が一般公開で呼び出し可能にならないようにする
--no-allow-unauthenticated
テスト
次のコマンドを使用して関数をテストします。
# get the Service URL SERVICE_URL="$(gcloud run services describe nodejs-run-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
レスポンスとして HTTP with Node.js in Cloud Run functions!
というメッセージが表示されます。
4. Pub/Sub 関数
2 つ目の関数では、特定のトピックにパブリッシュされた Pub/Sub メッセージによってトリガーされる Python 関数を作成します。
Pub/Sub 認証トークンを設定する
2021 年 4 月 8 日以前に Pub/Sub サービス アカウントを有効にした場合は、Pub/Sub サービス アカウントに iam.serviceAccountTokenCreator
ロールを付与します。
PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)') gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:service-$PROJECT_NUMBER@gcp-sa-pubsub.iam.gserviceaccount.com \ --role roles/iam.serviceAccountTokenCreator
作成
サンプルで使用する Pub/Sub トピックを作成します。
TOPIC=cloud-run-functions-pubsub-topic gcloud pubsub topics create $TOPIC
アプリ用のフォルダを作成し、そのフォルダに移動します。
mkdir ../hello-pubsub cd ../hello-pubsub
CloudEvent ID を含むメッセージをロギングする main.py
ファイルを作成します。
import functions_framework @functions_framework.cloud_event def hello_pubsub(cloud_event): print('Pub/Sub with Python in Cloud Run functions! Id: ' + cloud_event['id'])
次の内容の requirements.txt
ファイルを作成して、依存関係を指定します。
functions-framework==3.*
導入
関数をデプロイします。
gcloud run deploy python-pubsub-function \ --source . \ --function hello_pubsub \ --base-image python313 \ --region $REGION \ --no-allow-unauthenticated
サービス アカウント ID に使用するプロジェクト番号を取得します。
PROJECT_NUMBER=$(gcloud projects list --filter="project_id:$PROJECT_ID" --format='value(project_number)')
トリガーを作成する
gcloud eventarc triggers create python-pubsub-function-trigger \ --location=$REGION \ --destination-run-service=python-pubsub-function \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.pubsub.topic.v1.messagePublished" \ --transport-topic=projects/$PROJECT_ID/topics/$TOPIC \ --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
テスト
トピックにメッセージを送信して関数をテストします。
gcloud pubsub topics publish $TOPIC --message="Hello World"
受信した CloudEvent がログに表示されます。
gcloud run services logs read python-pubsub-function --region $REGION --limit=10
5. Cloud Storage 関数
次の関数では、Cloud Storage バケットからのイベントに応答する Node.js 関数を作成します。
設定
Cloud Storage の関数を使用するには、Cloud Storage サービス アカウントに pubsub.publisher
IAM ロールを付与します。
SERVICE_ACCOUNT=$(gsutil kms serviceaccount -p $PROJECT_NUMBER) gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$SERVICE_ACCOUNT \ --role roles/pubsub.publisher
作成
アプリ用のフォルダを作成し、そのフォルダに移動します。
mkdir ../hello-storage cd ../hello-storage
Cloud Storage イベントに単純に応答する index.js
ファイルを作成します。
const functions = require('@google-cloud/functions-framework'); functions.cloudEvent('helloStorage', (cloudevent) => { console.log('Cloud Storage event with Node.js in Cloud Run functions!'); console.log(cloudevent); });
依存関係を指定する package.json
ファイルを作成します。
{ "name": "nodejs-crf-cloud-storage", "version": "0.0.1", "main": "index.js", "dependencies": { "@google-cloud/functions-framework": "^2.0.0" } }
導入
まず、Cloud Storage バケットを作成します(または、既存のバケットを使用します)。
export BUCKET_NAME="gcf-storage-$PROJECT_ID" export BUCKET="gs://gcf-storage-$PROJECT_ID" gsutil mb -l $REGION $BUCKET
関数をデプロイします。
gcloud run deploy nodejs-crf-cloud-storage \ --source . \ --base-image nodejs22 \ --function helloStorage \ --region $REGION \ --no-allow-unauthenticated
関数がデプロイされると、Cloud Console の Cloud Run セクションに表示されます。
Eventarc トリガーを作成します。
BUCKET_REGION=$REGION gcloud eventarc triggers create nodejs-crf-cloud-storage-trigger \ --location=$BUCKET_REGION \ --destination-run-service=nodejs-crf-cloud-storage \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.storage.object.v1.finalized" \ --event-filters="bucket=$BUCKET_NAME" \ --service-account=$PROJECT_NUMBER-compute@developer.gserviceaccount.com
テスト
バケットにファイルをアップロードして関数をテストします。
echo "Hello World" > random.txt gsutil cp random.txt $BUCKET/random.txt
受信した CloudEvent がログに表示されます。
gcloud run services logs read nodejs-crf-cloud-storage --region $REGION --limit=10
6. Cloud Audit Logs
次の関数では、Compute Engine VM インスタンスの作成時に Cloud Audit Log イベントを受信する Node.js 関数を作成します。これに応じて、新しく作成された VM にラベルが追加され、VM の作成者が指定されます。
新しく作成された Compute Engine VM を特定する
VM が作成されると、Compute Engine は 2 つの監査ログを出力します。
最初の 1 つは、VM の作成の開始時に発行されます。2 つ目は、VM の作成後に発行されます。
監査ログでは、オペレーション フィールドが異なり、first: true
値と last: true
値が含まれています。2 番目の監査ログにはインスタンスにラベルを付けるために必要なすべての情報が含まれているため、last: true
フラグを使用して Cloud Run functions で検出します。
設定
Cloud Audit Log 関数を使用するには、Eventarc の監査ログを有効にする必要があります。また、eventarc.eventReceiver
ロールを持つサービス アカウントを使用する必要があります。
- Compute Engine API で、Cloud Audit Logs の管理読み取り、データ読み取り、データ書き込みの各ログタイプを有効にします。
- デフォルトの Compute Engine サービス アカウントに
eventarc.eventReceiver
IAM ロールを付与します。
gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \ --role roles/eventarc.eventReceiver
関数を作成する
この Codelab では node.js を使用しますが、他の例については https://github.com/GoogleCloudPlatform/eventarc-samples をご覧ください。
package.json
ファイルを作成する
{
"dependencies": {
"googleapis": "^84.0.0"
}
}
node.js
ファイルを作成する
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
const { google } = require("googleapis");
var compute = google.compute("v1");
exports.labelVmCreation = async (cloudevent) => {
const data = cloudevent.body;
// in case an event has >1 audit log
// make sure we respond to the last event
if (!data.operation || !data.operation.last) {
console.log("Operation is not last, skipping event");
return;
}
// projects/dogfood-gcf-saraford/zones/us-central1-a/instances/instance-1
var resourceName = data.protoPayload.resourceName;
var resourceParts = resourceName.split("/");
var project = resourceParts[1];
var zone = resourceParts[3];
var instanceName = resourceParts[5];
var username = data.protoPayload.authenticationInfo.principalEmail.split("@")[0];
console.log(`Setting label username: ${username} to instance ${instanceName} for zone ${zone}`);
var authClient = await google.auth.getClient({
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
});
// per docs: When updating or adding labels in the API,
// you need to provide the latest labels fingerprint with your request,
// to prevent any conflicts with other requests.
var labelFingerprint = await getInstanceLabelFingerprint(authClient, project, zone, instanceName);
var responseStatus = await setVmLabel(
authClient,
labelFingerprint,
username,
project,
zone,
instanceName
);
// log results of setting VM label
console.log(JSON.stringify(responseStatus, null, 2));
};
async function getInstanceLabelFingerprint(authClient, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
auth: authClient
};
var response = await compute.instances.get(request);
var labelFingerprint = response.data.labelFingerprint;
return labelFingerprint;
}
async function setVmLabel(authClient, labelFingerprint, username, project, zone, instanceName) {
var request = {
project: project,
zone: zone,
instance: instanceName,
resource: {
labels: { "creator": username },
labelFingerprint: labelFingerprint
},
auth: authClient
};
var response = await compute.instances.setLabels(request);
return response.statusText;
}
導入
関数をデプロイします。
gcloud run deploy gce-vm-labeler \ --source . \ --function labelVmCreation \ --region $REGION \ --no-allow-unauthenticated
次に、トリガーを作成します。この関数は、--trigger-event-filters
フラグを使用して Compute Engine の挿入の監査ログをフィルタリングしています。
gcloud eventarc triggers create gce-vm-labeler-trigger \ --location=$REGION \ --destination-run-service=gce-vm-labeler \ --destination-run-region=$REGION \ --event-filters="type=google.cloud.audit.log.v1.written,serviceName=compute.googleapis.com,methodName=v1.compute.instances.insert" \ --service-account=$ROJECT_NUMBER-compute@developer.gserviceaccount.com
テスト
環境変数を設定します。
# if you're using europe-west1 as your region
ZONE=europe-west1-d
VM_NAME=codelab-crf-auditlog
VM を作成するには、次のコマンドを実行します。
gcloud compute instances create $VM_NAME --zone=$ZONE --machine-type=e2-medium --image-family=debian-11 --image-project=debian-cloud
VM の作成が完了すると、Cloud Console の [基本情報] セクションで、または次のコマンドを使用して、VM に追加された creator
ラベルが表示されます。
gcloud compute instances describe $VM_NAME --zone=$ZONE
出力に次の例のようなラベルが表示されます。
... labelFingerprint: ULU6pAy2C7s= labels: creator: atameldev ...
クリーンアップ
VM インスタンスを削除してください。このラボでは、このバケットは再度使用されません。
gcloud compute instances delete $VM_NAME --zone=$ZONE
7. トラフィック分割
Cloud Run functions では、関数の複数のリビジョンがサポートされ、トラフィックを異なるリビジョン間で分割したり、関数を以前のバージョンにロールバックしたりできます。
このステップでは、関数の 2 つのリビジョンをデプロイし、それらの間でトラフィックを 50 対 50 に分割します。
作成
アプリ用のフォルダを作成し、そのフォルダに移動します。
mkdir ../traffic-splitting cd ../traffic-splitting
色の環境変数を読み取り、その背景色で Hello World
を返信する Python 関数を含む main.py
ファイルを作成します。
import os color = os.environ.get('COLOR') def hello_world(request): return f'<body style="background-color:{color}"><h1>Hello World!</h1></body>'
次の内容の requirements.txt
ファイルを作成して、依存関係を指定します。
functions-framework==3.*
導入
オレンジ色の背景で関数の最初のリビジョンをデプロイします。
COLOR=orange gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
この時点で、ブラウザで HTTP トリガー(上記のデプロイ コマンドの URI 出力)を表示して関数をテストすると、オレンジ色の背景の Hello World
が表示されます。
黄色の背景で 2 番目のリビジョンをデプロイします。
COLOR=yellow gcloud run deploy hello-world-colors \ --source . \ --base-image python313 \ --function hello_world \ --region $REGION \ --allow-unauthenticated \ --update-env-vars COLOR=$COLOR
これは最新のリビジョンであるため、関数をテストすると、黄色の背景の Hello World
が表示されます。
トラフィックを 50 対 50 で分割する
オレンジと黄色のリビジョンの間でトラフィックを分割するには、Cloud Run サービスのリビジョン ID を確認する必要があります。リビジョン ID を表示するコマンドは次のとおりです。
gcloud run revisions list --service hello-world-colors \ --region $REGION --format 'value(REVISION)'
出力例を以下に示します。
hello-world-colors-00001-man hello-world-colors-00002-wok
次に、次の手順で 2 つのリビジョン間でトラフィックを分割します(リビジョン名に応じて X-XXX
を更新します)。
gcloud run services update-traffic hello-world-colors \ --region $REGION \ --to-revisions hello-world-colors-0000X-XXX=50,hello-world-colors-0000X-XXX=50
テスト
パブリック URL にアクセスして関数をテストします。オレンジ色のリビジョンと黄色のリビジョンが半々の割合で表示されます。
詳細については、ロールバック、段階的なロールアウト、トラフィックの移行をご覧ください。
8. 最小インスタンス数
Cloud Run functions では、ウォーム状態を維持し、いつでもリクエストを処理できる関数インスタンスの最小数を指定できます。これは、コールド スタートの数を制限するのに役立ちます。
このステップでは、初期化に時間がかかる関数をデプロイします。コールド スタートの問題が発生します。次に、最小インスタンス値を 1 に設定して関数をデプロイし、コールド スタートを回避します。
作成
アプリのフォルダを作成し、そのフォルダに移動します。
mkdir ../min-instances cd ../min-instances
main.go
ファイルを作成します。この Go サービスには、初期化に時間がかかることをシミュレートするために 10 秒間スリープする init
関数があります。また、HTTP 呼び出しに応答する HelloWorld
関数もあります。
package p import ( "fmt" "net/http" "time" ) func init() { time.Sleep(10 * time.Second) } func HelloWorld(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "Slow HTTP Go in Cloud Run functions!") }
導入
最小インスタンスのデフォルト値である 0 を使用して、関数の最初のリビジョンをデプロイします。
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated
次のコマンドで関数をテストします。
# get the Service URL SERVICE_URL="$(gcloud run services describe go-slow-function --region $REGION --format 'value(status.url)')" # invoke the service curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
最初の呼び出しで 10 秒の遅延(コールド スタート)が発生し、その後メッセージが表示されます。後続の呼び出しはすぐに返される必要があります。
最小インスタンス数を設定する
最初のリクエストでコールド スタートを回避するには、次のように --min-instances
フラグを 1 に設定して関数を再デプロイします。
gcloud run deploy go-slow-function \ --source . \ --base-image go123 \ --function HelloWorld \ --region $REGION \ --no-allow-unauthenticated \ --min-instances 1
テスト
関数をもう一度テストします。
curl -H "Authorization: bearer $(gcloud auth print-identity-token)" -X GET $SERVICE_URL
最初のリクエストで 10 秒の遅延は発生しなくなります。最小インスタンスのおかげで、最初の呼び出し(長時間呼び出しがない場合)のコールド スタートの問題がなくなりました。
詳細については、最小インスタンスの使用をご覧ください。
9. 完了
以上で、この Codelab は完了です。
学習した内容
- Cloud Run functions の概要と、ベースイメージの自動更新の使用方法。
- HTTP 呼び出しに応答する関数を作成する方法。
- Pub/Sub メッセージに応答する関数を記述する方法。
- Cloud Storage イベントに応答する関数を作成する方法。
- 2 つのリビジョン間でトラフィックを分割する方法。
- 最小インスタンスを使用してコールド スタートを回避する方法。