通知で課金の使用状況を無効にする

このドキュメントでは、費用がプロジェクトの予算に達したときにプロジェクトの課金を自動的に無効にする方法について説明します。プロジェクトで課金を無効にすると、無料枠サービスを含め、プロジェクト内のすべてのサービスが停止します。 Google Cloud 予算通知に対するより微妙なレスポンスを設定するには、通知でリソース使用量を制御するをご覧ください。

Google Cloudに支出できる金額に上限があるため、費用を制限している場合があります。このような場合、予算の上限に達したら、すべてのサービスと使用を停止して費用の発生を停止したいと考えるかもしれません。 Google Cloud プロジェクトで課金を無効にすると、そのプロジェクトで費用が発生しないようにできます。

制限事項

  • 費用が発生してから予算通知を受け取るまでに時間差があるため、すべてのサービスが停止された時点で確認されていなかった使用により追加費用が発生することがあります。この例で示される上限を設定する手順を行っても、予算を超えた費用が発生する場合があります。資金が限られている場合は、請求の遅延を考慮して、使用可能な資金より低く最大予算を設定してください。

  • 請求先アカウントにロックされているプロジェクトの課金を無効にすることはできません。プロジェクトのロックとロック解除の詳細については、プロジェクトと請求先アカウント間のリンクを保護するをご覧ください。

始める前に

開始前に、次のタスクを完了する必要があります。

  1. Cloud Billing API を有効にする
  2. 1 つのプロジェクトを範囲とする予算を作成する
  3. プログラムによる予算通知を設定する

Cloud Run 関数を設定する

プロジェクトの Cloud Billing を無効にするには、Cloud Run 関数を作成し、Cloud Billing API を呼び出すように構成します。

  1. Cloud Run 関数を作成するの手順を完了します。[トリガーの種類] が、予算で使用する Pub/Sub トピックと同じに設定されていることを確認します。
  2. 次の依存関係を追加します。

    Node.js

    次のコードを package.json ファイルにコピーします。

    {
      "name": "cloud-functions-billing",
      "private": "true",
      "version": "0.0.1",
      "description": "Examples of integrating Cloud Functions with billing",
      "main": "index.js",
      "engines": {
        "node": ">=16.0.0"
      },
      "scripts": {
        "compute-test": "c8 mocha -p -j 2 test/periodic.test.js --timeout=600000",
        "test": "c8 mocha -p -j 2 test/index.test.js --timeout=5000 --exit"
      },
      "author": "Ace Nassri <anassri@google.com>",
      "license": "Apache-2.0",
      "dependencies": {
        "@google-cloud/billing": "^4.0.0",
        "@google-cloud/compute": "^4.0.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^143.0.0",
        "slack": "^11.0.1"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^3.0.0",
        "c8": "^10.0.0",
        "gaxios": "^6.0.0",
        "mocha": "^10.0.0",
        "promise-retry": "^2.0.0",
        "proxyquire": "^2.1.0",
        "sinon": "^18.0.0",
        "wait-port": "^1.0.4"
      }
    }
    

    Python

    次のコードを requirements.txt ファイルにコピーします。

    slackclient==2.9.4
    google-api-python-client==2.131.0
    

  3. 次のコードを Cloud Run 関数にコピーします。

    Node.js

    const {CloudBillingClient} = require('@google-cloud/billing');
    const {InstancesClient} = require('@google-cloud/compute');
    
    const PROJECT_ID = process.env.GOOGLE_CLOUD_PROJECT;
    const PROJECT_NAME = `projects/${PROJECT_ID}`;
    const billing = new CloudBillingClient();
    
    exports.stopBilling = async pubsubEvent => {
      const pubsubData = JSON.parse(
        Buffer.from(pubsubEvent.data, 'base64').toString()
      );
      if (pubsubData.costAmount <= pubsubData.budgetAmount) {
        return `No action necessary. (Current cost: ${pubsubData.costAmount})`;
      }
    
      if (!PROJECT_ID) {
        return 'No project specified';
      }
    
      const billingEnabled = await _isBillingEnabled(PROJECT_NAME);
      if (billingEnabled) {
        return _disableBillingForProject(PROJECT_NAME);
      } else {
        return 'Billing already disabled';
      }
    };
    
    /**
     * Determine whether billing is enabled for a project
     * @param {string} projectName Name of project to check if billing is enabled
     * @return {bool} Whether project has billing enabled or not
     */
    const _isBillingEnabled = async projectName => {
      try {
        const [res] = await billing.getProjectBillingInfo({name: projectName});
        return res.billingEnabled;
      } catch (e) {
        console.log(
          'Unable to determine if billing is enabled on specified project, assuming billing is enabled'
        );
        return true;
      }
    };
    
    /**
     * Disable billing for a project by removing its billing account
     * @param {string} projectName Name of project disable billing on
     * @return {string} Text containing response from disabling billing
     */
    const _disableBillingForProject = async projectName => {
      const [res] = await billing.updateProjectBillingInfo({
        name: projectName,
        resource: {billingAccountName: ''}, // Disable billing
      });
      return `Billing disabled: ${JSON.stringify(res)}`;
    };

    Python

    import base64
    import json
    import os
    
    from googleapiclient import discovery
    
    PROJECT_ID = os.getenv("GCP_PROJECT")
    PROJECT_NAME = f"projects/{PROJECT_ID}"
    def stop_billing(data, context):
        pubsub_data = base64.b64decode(data["data"]).decode("utf-8")
        pubsub_json = json.loads(pubsub_data)
        cost_amount = pubsub_json["costAmount"]
        budget_amount = pubsub_json["budgetAmount"]
        if cost_amount <= budget_amount:
            print(f"No action necessary. (Current cost: {cost_amount})")
            return
    
        if PROJECT_ID is None:
            print("No project specified with environment variable")
            return
    
        billing = discovery.build(
            "cloudbilling",
            "v1",
            cache_discovery=False,
        )
    
        projects = billing.projects()
    
        billing_enabled = __is_billing_enabled(PROJECT_NAME, projects)
    
        if billing_enabled:
            __disable_billing_for_project(PROJECT_NAME, projects)
        else:
            print("Billing already disabled")
    
    
    def __is_billing_enabled(project_name, projects):
        """
        Determine whether billing is enabled for a project
        @param {string} project_name Name of project to check if billing is enabled
        @return {bool} Whether project has billing enabled or not
        """
        try:
            res = projects.getBillingInfo(name=project_name).execute()
            return res["billingEnabled"]
        except KeyError:
            # If billingEnabled isn't part of the return, billing is not enabled
            return False
        except Exception:
            print(
                "Unable to determine if billing is enabled on specified project, assuming billing is enabled"
            )
            return True
    
    
    def __disable_billing_for_project(project_name, projects):
        """
        Disable billing for a project by removing its billing account
        @param {string} project_name Name of project disable billing on
        """
        body = {"billingAccountName": ""}  # Disable billing
        try:
            res = projects.updateBillingInfo(name=project_name, body=body).execute()
            print(f"Billing disabled: {json.dumps(res)}")
        except Exception:
            print("Failed to disable billing, possibly check permissions")
    
    

  4. [エントリ ポイント] を、実行する正しい関数に設定します。

    Node.js

    [エントリ ポイント] を stopBilling に設定します。

    Python

    [エントリ ポイント] を stop_billing に設定します。

  5. 自動的に設定される環境変数のリストを確認し、Cloud Billing を無効にするプロジェクトに GOOGLE_CLOUD_PROJECT 変数を手動で設定する必要があるかどうかを判断します。

  6. [デプロイ] をクリックします。

サービス アカウント権限を構成する

Cloud Run 関数は自動的に作成されたサービス アカウントとして実行されます。課金を無効にするには、次の手順に沿って、変更が必要なプロジェクトのサービスにサービス アカウントの権限を付与する必要があります。

  1. Cloud Run 関数の詳細を表示して、正しいサービス アカウントを特定します。サービス アカウントはページの下部に表示されます。
  2. Google Cloud コンソールの [IAM] ページに移動して、適切な権限を設定します。

    IAM ページに移動

  3. 請求先アカウントの権限を変更するには、Google Cloud コンソールで請求の [アカウント管理] ページに移動し、サービス アカウントを Cloud 請求先アカウントのプリンシパルとして追加して、適切な請求先アカウントの権限を設定します。

    Cloud Billing の [アカウント管理] ページに移動

詳しくは、Cloud 請求先アカウントの権限を構成する方法をご覧ください。

Cloud Billing が無効であることをテストする

予算が通知を送信すると、指定したプロジェクトから Cloud 請求先アカウントが削除されます。関数が想定どおりに動作することを確認するには、Cloud Run 関数をテストするの手順に沿って操作します。

成功すると、プロジェクトは Cloud 請求先アカウントに表示されなくなり、同じプロジェクト内にある Cloud Run 関数を含めプロジェクト内のリソースが無効になります。

プロジェクトのリソースを引き続き使用するには、Google Cloud コンソールでプロジェクトの Cloud Billing を手動で有効に戻します。 Google Cloud

次のステップ

他のプログラムによる通知の例を確認して、以下を行う方法を学びます。