Google Cloudは、ほとんどのクラウド費用をプロジェクトに対して請求します。では、どのプロジェクトに請求されるのでしょうか?
Google Compute Engineのようなリソースベースのサービスなら答えは簡単で、VMなどのリソースを保有するプロジェクトに料金が割り当てられます。
しかし、Vision、Translate、BigQueryといったGoogle CloudサービスのAPIを呼び出す場合、課金モデルはより柔軟になります。料金の一部または全額が、ユーザーが指定した「Client」プロジェクトに紐づき、異なる事業部門やプロジェクトへのコスト按分が可能になるのです。
本記事で解説する内容は次のとおりです。
- 「Client」プロジェクトとは何か、そしてその指定方法。
- サービスの種類ごとに、課金をプロジェクトへ紐づける主なパターン。
- Googleのドキュメントが課金について述べる際、なぜほぼ一貫して「クォータプロジェクト(quota project)」という表現を使うのか、そしてそれが「課金プロジェクト(billing project)」とどう関係するのか。
- 任意のAPI呼び出しについて、どのプロジェクトに課金されたかを調べる方法。
- コストをビジネスカテゴリに按分する際の課題を乗り越える方法。
クォータ/課金プロジェクトの設定
API呼び出しがGoogle Cloudインフラに到達すると、以下の階層に従ってClientプロジェクトが評価されます。非リソース系サービスでは、これが課金にも使われます(「クォータプロジェクト」と「課金プロジェクト」の概念については後述します)。
- この評価はGoogleのService Infrastructure / Service Controlが行い、Google管理サービス全体で統一された動作を提供します。
- 権限について:あるプロジェクトをClientとして指定するには、呼び出し元のプリンシパル(Service Account、ユーザー、Workforce Identity FederationまたはWorkload Identity Federationなど)が、そのプロジェクトにService Usage Consumerロールを持っている必要があります(Service Accountがそのプロジェクトで定義されているか別プロジェクトで定義されているかは関係ありません)。
- 本記事の内容はGoogle Cloud Platformのサービスに当てはまりますが、WorkspaceやMapsといった他のGoogleサービスには必ずしも当てはまりません。
- Clientプロジェクトの指定方法は以下のとおりで、優先順位の高い順に評価されます。
Clientの指定方法
1. リクエスト内で指定
- HTTPヘッダー:
X-goog-user-projectHTTPヘッダーをリクエストに直接付与できます。curlなど、生のRESTコールで便利です。 - クライアントライブラリ設定:Google Cloud SDKでクライアントオプションとして設定するプロジェクトです。Python版Google Cloudクライアントライブラリを使った例を示します。
from google.cloud import translate_v3from google.api_core.client_options import ClientOptions# 1. 課金プロジェクトを定義options = ClientOptions(quota_project_id="your-billing-project-id")# 2. クライアント生成時に注入client = translate_v3.TranslationServiceClient(client_options=options)2. 環境変数
環境変数 GOOGLE_CLOUD_QUOTA_PROJECT の値です。コンテナ環境では、ClientOptionsにハードコードせずクォータプロジェクトを動的に設定する手段として、この値を定義するのがベストプラクティスです。
3. APIキー
呼び出しに使ったAPIキーが生成されたプロジェクトです。
4. ADCのクォータプロジェクト
ローカル環境(主に開発時)では、gcloud auth application-default set-quota-project で設定します。
ローカル開発の落とし穴: クォータと課金用のClientプロジェクトを設定しないまま
gcloud auth application-default loginを実行すると、ローカルのスクリプトはほとんどのAPIに対して、特殊な制限付きプロジェクト(「Cloud SDKプロジェクト」764086051850)で認証されてしまいます。これは全世界で共有されているため利用が厳しく制限されており、すぐに"Quota exceeded for project 764086051850"エラーが発生します。Client値を明示的に設定すれば回避できます。
5. 認証トークン
- Service Account:リクエストを行うService Account(なりすまし対象のService Accountを含む)を所有するプロジェクト。
- OAuthクライアントID:エンドユーザートークンの生成に使ったOAuth 2.0クライアントIDを所有するプロジェクト。
6. Workforce Identity Federation
APIのプリンシパルがWorkforce Identity Federationのユーザーである場合、workforce poolsのユーザープロジェクトが使われます。
なぜGoogleは「課金プロジェクト」ではなく「クォータプロジェクト」と呼ぶのか?
Google Cloudのドキュメントやパラメーターでは、ほぼ一貫して「クォータプロジェクト」という用語が使われており、「課金プロジェクト」という用語はほとんど登場しません。
例外もあります。たとえばgcloudのドキュメントでは、同じパラメーターを指しているにもかかわらず両方の用語が並べて使われていて混乱を招きます。永続的なツール設定では billing/quota_project、コマンドラインフラグでは --billing-project です。
理由はこうです。Google Cloud全体で見ると、Client設定がインフラレベルで行うのは1つだけ。すなわち、APIゲートウェイが使うクォータプロジェクトを設定することで、課金自体はリソースのプロジェクトに向かいます。ただし、非リソースベースのサービスでは課金システムがサーバー側のリソースを見つけられないため、課金はデフォルトでクォータプロジェクトに割り当てられるのです。
リソースベース vs. 非リソースベースのサービス
最大の違いは、リソース単位で課金されるサービスと、そうでないサービスの違いです。まずは概要から見ていきましょう。
| サービスタイプ | 主な課金ドライバー | 主なクォータドライバー | 例 |
|---|---|---|---|
| リソースベース | リソースを保有するプロジェクト | Clientプロジェクト | Compute Engine、Spanner、Cloud Run |
| Clientベース | Client(クォータ)プロジェクト | Clientプロジェクト | Vision API、Translate API |
リソースベースのAPI:サーバー側で識別可能なプロジェクト
Googleのインフラ、ストレージ、コンピュートのAPIがこれにあたります。これらはステートフルです。呼び出すとき、ユーザーはプロジェクト内に存在する特定のアセットやデータの読み取り、変更、削除をGoogleに依頼しています。課金プロジェクトはリソースが置かれている場所であり、通常はURLで指定されます。一方クォータプロジェクトは、Client設定によって決まります。
Cloud Storage、Pub/Sub、BigQueryには特殊なケースがあり、後述します。
例:
- Compute Engine API: 仮想マシン、ディスク、ネットワークの管理。
GET https://compute.googleapis.com/compute/v1/projects/{project}/zones/{zone}/instances - Cloud Spanner API: リレーショナルデータベースのクエリや管理。
POST https://spanner.googleapis.com/v1/projects/{project}/instances/{instance}/databases/{database}/sessions - Cloud Run は「サーバーレス」ですが、Cloud RunサービスはGoogleではなくユーザーが管理するため、リソースベースの課金になります。Cloud RunサービスのURLにはプロジェクトが含まれず、ドメインも
googleapis.comではありません。
Clientベースのクォータと課金:サーバー側にリソースがない
グローバルに共有されたアルゴリズムでしかないステートレスなAPIでは、どのプロジェクトにもリソースは存在せず、REST URLにサーバー側のプロジェクトも現れません。これらの場合、課金はClient設定によって決まります。
例:
- Cloud Natural Language API: 文章を渡すと、感情分析の結果が返ります。
POST https://language.googleapis.com/v1/documents:analyzeSentiment - Cloud Video Intelligence API: 動画を渡すと、その中のオブジェクトのラベルが返ります。
POST https://videointelligence.googleapis.com/v1/videos:annotate
Cloud Storage:Requester Paysオプション
Cloud Storageはリソースベースのサービスですが、GET https://storage.googleapis.com/storage/v1/b/{bucket}/o/{object} という形式のURLにプロジェクトIDは含まれません。
それでもサーバー側のプロジェクトは存在し、APIゲートウェイがバケット名から特定します。
他のリソースベースAPIと同様、課金はバケットのプロジェクトに割り当てられ、クォータは指定されたClientに割り当てられます。
ただし、Cloud Storage独自の「Requester Pays」オプションでこの動作を変更できます。バケット所有者がRequester Paysを有効にすると、API呼び出し元は(前述のClient設定で)課金可能なプロジェクトを指定する必要があり、そのプロジェクトに下り(egress)とAPI操作の料金が請求されます(ただしストレージ料金は引き続きバケット所有者の負担です)。
BigQuery:2種類のリソース
BigQueryは、ストレージとコンピュートが別々のプロジェクトに存在し得るという点で特殊です。
- ストレージは、リソース(保存データを持つデータセット)を保有するプロジェクトに課金されます。
- クエリ処理は、クエリジョブが実行されたプロジェクトに課金されます。ジョブはコンピュートの一種ですが、独立したステートフルなリソースとして扱われます。課金はそのジョブのプロジェクトに対して行われ、ストレージのプロジェクトとも、ジョブを起動した場所とも異なる場合があります。
Pub/Sub:トピックとサブスクリプションは別プロジェクトに置ける
他のリソースベースのサービスと同様、Pub/Subの料金はAPI呼び出し元に関わらずリソースの所有関係に紐づき、Client設定はクォータの算出にのみ使われます。
ただしPub/Subは、リソース自体がプロジェクトをまたいで分散できるという点で特殊です。
- メッセージ発行の課金を決めるリソースは、Topicです。
- Pub/Subサブスクリプションはそれ自体が独立したリソースで、Topicのプロジェクトや呼び出し元Clientのプロジェクトとは別のプロジェクトに作成できます。これによって配信スループット、データ転送(egress)、サブスクリプションレベルのストレージの課金先が決まります。
サービス呼び出しの課金プロジェクトを調べる
特定の呼び出しについて、どのプロジェクトに課金されたかを調べる方法を紹介します。Google Cloud APIログには標準の「billed_project」フィールドはありませんが、一般的には情報を見つけ出せます。
前提条件: API呼び出しを追跡するには、まず課金されていると思われるプロジェクトで、対象のAPI(例:Cloud Storage、BigQuery)についてデータアクセス監査ログを有効化する必要があります。Google Cloudコンソールの IAMと管理 -> 監査ログ から設定します。
Google Cloudサービス全般の場合
対象サービスのログを次のようにフィルタリングします。
logName=~"cloudaudit.googleapis.com"protoPayload.serviceName="[SERVICE_NAME].googleapis.com"必要に応じてメソッドでも絞り込めます。例:
logName=~"cloudaudit.googleapis.com/data_access"protoPayload.serviceName="vision.googleapis.com"protoPayload.methodName="google.cloud.vision.v1.ImageAnnotator.AnnotateImage"ログエントリ内の resource.labels.project_id フィールドを確認します。このフィールドは、リソースベースかClientベースかを問わず、課金されたプロジェクトを示します(Cloud StorageのRequester Paysバケットを除く)。複数プロジェクトのログを集約したLog Sink経由で混在データを参照している場合でも、この情報は確認できます。
BigQueryのコンピュートジョブの場合
BigQueryはコンピュートとストレージを分離しているため、ストレージログではなくコンピュート実行ログを確認する必要があります。Logs Explorerで以下のクエリを実行し、他のGoogleサービスと同様に resource.labels.project_id フィールドを確認します。
resource.type="bigquery_project"protoPayload.methodName=("google.cloud.bigquery.v2.JobService.InsertJob" OR "google.cloud.bigquery.v2.JobService.Query")protoPayload.metadata."@type"="type.googleapis.com/google.cloud.audit.BigQueryAuditMetadata"GCS Requester Paysの場合
Cloud StorageのRequester Paysバケットでは、監査ログの protoPayload.authorizationInfo.resource のなかで「projects/」に続く形で、課金されたプロジェクト番号が(16進数で)表示されます。例:「projects/0000004d9813b13」。
コスト按分の解決策
こうした課金ログを可視化する方法がわかったら、次の課題はそれをビジネス、たとえば特定の事業部門や事業プロジェクトにマッピングすることです。
私はDoiT InternationalのCloud Architectとして、お客様のコスト最適化をご支援しています。DoiTでは、それを実現するための強力なツール群をDoiT Cloud Intelligenceとして提供しています。たとえばDoiT Cost Allocationは、ビジネス軸でコストをグルーピングし、按分できます。
コスト按分に役立つラベルをサポートするサービスもあります。
- リソースベースのサービスでは、リソース単位で按分でき、必要に応じてビジネスカテゴリに合わせたラベルをリソースに付与できます。
- 例外的に、多くの生成AIモデルへのAPI呼び出しは、按分に使えるカスタムコストラベルを受け取れます。
- Vertex AIパイプラインにも独自のコスト按分機能があります。Pipeline自体にはわずかなコストが発生しますが、より重要なのは複数種類のリソースを派生させられる点です。Vertex AIは派生する各リソースに
vertex-ai-pipelines-run-billing-idラベルを自動的に付与し、Pipeline実行全体へのコスト按分を可能にします。
しかし、ほとんどの非リソースベースサービスでは、GCPのコストデータにユーザーやその他の情報がラベル付けされておらず、ビジネスカテゴリで切り分けることができません。
解決策はこうです。Clientとして使うプロジェクトを複数作成しておくのです。Google Cloudの組織では一般に、フォルダで整理された多数のプロジェクトを運用します。これらのプロジェクトをClientに指定することで、コストをビジネスカテゴリに按分できます。これらのプロジェクトは中身が空でも構いませんし、課金プロジェクトのなかでクライアントコードを実行する必要もありません。重要なのは呼び出し時のClientプロジェクトだけです。こうしてプロジェクト自体が、事業部門を示す「ラベル」として機能します。