G-gen の藤岡です。当記事では、Google Cloud(旧称 GCP)の Cloud Functions(第 2 世代)で関数をデプロイする時の裏側の動きを Cloud Logging から深掘りしていきます。Cloud Functions は裏側を意識せずに使えるサービスではありますが、各サービスとの関係を理解しておくとエラー時に役立ちます。
前提知識
Cloud Functions
Cloud Functions は、FaaS(Function as a Service)と呼ばれるサーバーレスのコンピューティングサービスです。 詳細は以下の記事をご参照ください。 blog.g-gen.co.jp
Cloud Functions には従来の第 1 世代と、Cloud Run と Eventarc がベースの機能強化された第 2 世代の 2 種類が提供されています。
両者の詳細な違いはドキュメントをご参照ください。当記事ではこれ以降 Cloud Functions の第 2 世代を前提に記載します。
サービスアカウントとサービスエージェント
サービスアカウントは「ユーザー管理サービスアカウント」と「Google 管理サービスアカウント」の 2 つのカテゴリに分けられます。
サイトによって表記が異なる場合がありますが、当記事ではこちらのドキュメントの表記に従って記載します。
一般的に、「ユーザー管理サービスアカウント」がサービスアカウントと呼ばれ、「Google 管理サービスアカウント」がサービスエージェントと呼ばれます。 blog.g-gen.co.jp
更に詳細に分類すると以下のようになります。
カテゴリ | タイプ | プリンシパル |
---|---|---|
ユーザー管理サービスアカウント | ユーザー管理サービスアカウント | SERVICE_ACCOUNT_NAME @PROJECT_ID.iam.gserviceaccount.com |
ユーザー管理サービスアカウント | デフォルトのサービスアカウント | サービスによる (例:Compute Engine の場合 PROJECT_NUMBER-compute @developer.gserviceaccount.com |
Google 管理サービスアカウント | サービス固有のサービス エージェント | サービスによる (例:Compute Engine の場合 service-PROJECT_NUMBER @compute-system.iam.gserviceaccount.com |
Google 管理サービスアカウント | Google API サービス エージェント | PROJECT_NUMBER @cloudservices.gserviceaccount.com |
Google 管理サービスアカウント | Google 管理のサービス アカウントのロール マネージャー | service-agent-manager @system.gserviceaccount.com |
ユーザー管理サービスアカウント(サービスアカウント)
ユーザー管理サービスアカウント
ユーザー管理サービスアカウントは、プロジェクトリソースで、ユーザー自身が作り Compute Engine や Cloud Functions 等のリソースにアタッチして使います。
プリンシパルは SERVICE_ACCOUNT_NAME@PROJECT_ID.iam.gserviceaccount.com
です。
デフォルトのサービスアカウント
デフォルトのサービス アカウントは、API の有効化やサービスの利用時に自動で作成されるアカウントです。
デフォルトのサービスアカウントには 編集者(roles/editor)
が付与されます。
Google 管理サービスアカウント(サービスエージェント)
サービス固有のサービスエージェント
サービス固有のサービス エージェントは、Google Cloud サービスが別のサービスを呼び出すときに用いる特殊なサービスアカウントです。
例として、Cloud Logging で Cloud Storage バケットにログシンクする時は、サービスエージェントが裏側では使われています。
付与されているロールを削除すると、普段は意識しない裏側で動いている Google Cloud サービスが機能しなくなる場合があるので注意が必要です。
Google API サービスエージェント
Google API サービスエージェントは、サービス固有のサービスエージェントと同様に裏側で使われます。PROJECT_NUMBER@cloudservices.gserviceaccount.com
というプリンシパルでデフォルトで 編集者(roles/editor)
が付与されます。
Google 管理のサービスアカウントのロールマネージャー
Google 管理のサービスアカウントのロールマネージャーは、上記の Google 管理サービスアカウントを管理します。
サービスエージェントが自動で作成された際、サービスエージェントへのロールの付与をこのロールマネージャーが裏で行っています。
プリンシパルは service-agent-manager@system.gserviceaccount.com
で、これは IAM のページには表示されず、監査ログにのみ表示されます。
Cloud Functions(第 2 世代)
サービスアカウントとサービスエージェント
Cloud Functions では、デフォルトで以下のサービスアカウントとサービスエージェントを使います。
第 2 世代では、デフォルトでは Cloud Functions のサービスアカウントに Compute Engine のデフォルトのサービスアカウント(PROJECT_NUMBER-compute@developer.gserviceaccount.com
)を使いますが、Compute Engine API が有効化されていない場合はこのサービスアカウントが存在しないため、App Engine のデフォルトのサービスアカウント(PROJECT_ID@appspot.gserviceaccount.com
)が使われます。
サービス | カテゴリ | プリンシパル | デフォルトロール |
---|---|---|---|
Cloud Functions | サービスアカウント | PROJECT_NUMBER-compute @developer.gserviceaccount.com /PROJECT_ID @appspot.gserviceaccount.com |
編集者 |
Cloud Functions | サービスエージェント | service-PROJECT_NUMBER @gcf-admin-robot.iam.gserviceaccount.com |
Cloud Functions サービス エージェント |
Cloud Build | サービスアカウント | PROJECT_NUMBER @cloudbuild.gserviceaccount.com |
Cloud Build サービス アカウント |
Cloud Build | サービスエージェント | service-PROJECT_NUMBER @gcp-sa-cloudbuild.iam.gserviceaccount.com |
Cloud Build サービス エージェント |
Artifact Registry | サービスエージェント | service-PROJECT_NUMBER @gcp-sa-artifactregistry.iam.gserviceaccount.com |
Artifact Registry サービス エージェント |
Cloud Logging から確認した結果
前提
- コンソールから Cloud Functions(第 2 世代)をデプロイした時の一連の流れを示しています。
- Cloud Logging のログから挙動を確認しています。
- ログは一部を抜粋しています。
リソースの相関図
Cloud Functions をデプロイする時の各リソースの相関は以下の通りです。

Cloud Logging のログ内容
0. API の有効化
Cloud Functions(第 2 世代)で関数をデプロイするには以下の API を有効化します。
- Cloud Build API
- Cloud Functions API
- Cloud Logging API
- Cloud Pub/Sub API
- Artifact Registry API
- Cloud Run Admin API
API を有効化すると、サービスアカウントやサービスエージェントが自動で作られ、前述の Google 管理のサービスアカウントのロールマネージャー(service-agent-manager@system.gserviceaccount.com
)がそれぞれにロールを付与していきます。
# 一部抜粋 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Google 管理のサービス アカウントのロールマネージャーが実行 "principalEmail": "service-agent-manager@system.gserviceaccount.com" }, ... "serviceName": "cloudresourcemanager.googleapis.com", "methodName": "SetIamPolicy", ... "resourceName": "projects/PROJECT_ID", "serviceData": { "@type": "type.googleapis.com/google.iam.v1.logging.AuditData", "policyDelta": { "bindingDeltas": [ { # Cloud Build サービスアカウントへ権限付与 "action": "ADD", "role": "roles/cloudbuild.builds.builder", "member": "serviceAccount:PROJECT_NUMBER@cloudbuild.gserviceaccount.com" } ] } }, ...
1. Cloud Storage へソースコードのアップロード
Cloud Functions のサービスエージェント(service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
)が Cloud Storage に 2 つのバケットをの作成と、zip 化されたソースコードをアップロードします。
- gcf-v2-uploads-PROJECT_NUMBER-REGION
- gcf-v2-sources-PROJECT_NUMBER-REGION
# gcf-v2-uploads-PROJECT_NUMBER-asia-northeast1 バケットの作成 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "storage.googleapis.com", "methodName": "storage.buckets.create", ... "resourceName": "projects/_/buckets/gcf-v2-uploads-PROJECT_NUMBER-asia-northeast1", "serviceData": { "@type": "type.googleapis.com/google.iam.v1.logging.AuditData", "policyDelta": { "bindingDeltas": [ { "action": "ADD", "role": "roles/storage.legacyBucketOwner", "member": "projectEditor:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyBucketOwner", "member": "projectOwner:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyBucketReader", "member": "projectViewer:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectOwner", "member": "projectEditor:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectOwner", "member": "projectOwner:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectReader", "member": "projectViewer:fast-chess-399414" } ] } }, ...
# gcf-v2-uploads-PROJECT_NUMBER-asia-northeast1 バケットへ zip 化したソースコードをアップロード { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "storage.googleapis.com", "methodName": "storage.objects.create", ... "resourceName": "projects/_/buckets/gcf-v2-uploads-PROJECT_NUMBER-asia-northeast1/objects/5065d76a-5591-4d40-980f-5b9c425f92e4.zip", "serviceData": { "@type": "type.googleapis.com/google.iam.v1.logging.AuditData", "policyDelta": {} }, ...
# gcf-v2-sources-PROJECT_NUMBER-asia-northeast1 バケットの作成 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "storage.googleapis.com", "methodName": "storage.buckets.create", ... "resourceName": "projects/_/buckets/gcf-v2-sources-PROJECT_NUMBER-asia-northeast1", "serviceData": { "@type": "type.googleapis.com/google.iam.v1.logging.AuditData", "policyDelta": { "bindingDeltas": [ { "action": "ADD", "role": "roles/storage.legacyBucketOwner", "member": "projectEditor:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyBucketOwner", "member": "projectOwner:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyBucketReader", "member": "projectViewer:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectOwner", "member": "projectEditor:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectOwner", "member": "projectOwner:fast-chess-399414" }, { "action": "ADD", "role": "roles/storage.legacyObjectReader", "member": "projectViewer:fast-chess-399414" } ] } }, ...
# gcf-v2-sources-PROJECT_NUMBER-asia-northeast1 バケットへ zip 化したソースコードをアップロード { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "storage.googleapis.com", "methodName": "storage.objects.create", ... "resourceName": "projects/_/buckets/gcf-v2-sources-PROJECT_NUMBER-asia-northeast1/objects/function-1/function-source.zip", "serviceData": { "@type": "type.googleapis.com/google.iam.v1.logging.AuditData", "policyDelta": {} }, ...
2. Artifact Registry へリポジトリの作成
Cloud Functions のサービスエージェント(service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
)が Artifact Registry に REGION-docker.pkg.dev/PROJECT_ID/gcf-artifacts
リポジトリを作成します。
# gcf-artifacts リポジトリの作成 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "artifactregistry.googleapis.com", "methodName": "google.devtools.artifactregistry.v1.ArtifactRegistry.CreateRepository", ... "resourceName": "projects/PROJECT_ID/locations/asia-northeast1", "request": { "repository": { "labels": { "goog-managed-by": "cloudfunctions" }, "name": "projects/PROJECT_ID/locations/asia-northeast1/repositories/gcf-artifacts", "format": "DOCKER" }, "@type": "type.googleapis.com/google.devtools.artifactregistry.v1.CreateRepositoryRequest", "repositoryId": "gcf-artifacts", "parent": "projects/PROJECT_ID/locations/asia-northeast1" }, ...
3. Cloud Build でビルド
Cloud Functions のサービスエージェント(service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
)が Cloud Build でビルドを開始します。
# Cloud Build が開始 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "cloudbuild.googleapis.com", "methodName": "google.devtools.cloudbuild.v1.CloudBuild.CreateBuild", ... "resourceName": "projects/PROJECT_ID/builds", ...
Cloud Storage の gcf-v2-sources-PROJECT_NUMBER-asia-northeast1
バケット内の zip 化されたソースコードをビルドで使います。
# gcf-v2-sources-PROJECT_NUMBER-asia-northeast1 バケット内のソースコードを取得 { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Functions のサービスエージェントが実行 "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "storage.googleapis.com", "methodName": "storage.objects.get", ... "resourceName": "projects/_/buckets/gcf-v2-sources-PROJECT_NUMBER-asia-northeast1/objects/function-1/function-source.zip", ...
4. Artifact Registry へコンテナイメージのプッシュ
Cloud Build のサービスアカウント(PROJECT_NUMBER@cloudbuild.gserviceaccount.com
)がビルドしたコンテナイメージを Artifact Registry へプッシュします。
# ビルド済みコンテナイメージのプッシュ { "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": {}, "authenticationInfo": { # Cloud Build のサービスアカウントが実行 "principalEmail": "PROJECT_NUMBER@cloudbuild.gserviceaccount.com", "serviceAccountDelegationInfo": [ { "firstPartyPrincipal": { "principalEmail": "cloud-build-argo-foreman@prod.google.com" } } ], "principalSubject": "serviceAccount:PROJECT_NUMBER@cloudbuild.gserviceaccount.com" }, ... "serviceName": "artifactregistry.googleapis.com", "methodName": "Docker-StartUpload", ... "resourceName": "projects/PROJECT_ID/locations/asia-northeast1/repositories/gcf-artifacts", ...
Tips
Artifact Registry のリポジトリ
Cloud Functions のサービスエージェント(service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com
)は Artifact Registry のリポジトリを作る前に REGION-docker.pkg.dev/PROJECT_ID/gcf-artifacts
リポジトリを探しに行きます。
初回デプロイでは、対象のリポジトリは存在しないため、以下のエラーとなります。
{ "protoPayload": { "@type": "type.googleapis.com/google.cloud.audit.AuditLog", "status": { "code": 5, "message": "repository not found: namespaces/PROJECT_ID/repositories/gcf-artifacts" }, "authenticationInfo": { "principalEmail": "service-PROJECT_NUMBER@gcf-admin-robot.iam.gserviceaccount.com" }, ... "serviceName": "artifactregistry.googleapis.com", "methodName": "google.devtools.artifactregistry.v1.ArtifactRegistry.GetRepository", ... "severity": "ERROR", ...
上記のエラーの後に、CreateRepository メソッドでリポジトリが作成されます。このことから、以下の記事の「(b)Cloud Functions(第二世代)が自動生成するCloud Storage バケット、Artifact Registry リポジトリはCMEKでの暗号化に非対応」の項目にあるように先にリポジトリを作成し CMEK の設定をすることで、Cloud Functions で使うリポジトリの CMEK の利用が可能となります。 blog.g-gen.co.jp
藤岡 里美 (記事一覧)
クラウドソリューション部
数年前までチキン売ったりドレスフィッティングしていました。2022年9月 G-gen にジョイン。
Google Cloud All Certifications Engineer / Google Cloud Partner Top Engineer 2024
Follow @fujioka57621469