알림으로 결제 사용 중지

이 문서에서는 비용이 프로젝트 예산을 충족하거나 초과할 때 프로젝트에서 결제를 자동으로 사용 중지하는 방법을 설명합니다. 프로젝트에서 결제를 사용 중지하면 무료 등급 서비스를 포함한 프로젝트의 모든 Google Cloud 서비스가 종료됩니다. 예산 알림에 대한 더 미묘한 응답을 보려면 알림으로 리소스 사용량 관리를 참고하세요.

Google Cloud에 지출할 수 있는 최대 금액이 있으므로 비용을 제한할 수 있습니다. 이러한 경우 예산 한도에 도달하면 모든 Google Cloud 서비스 및 사용을 종료하여 비용 발생을 중지해야 할 수 있습니다. 프로젝트에서 결제를 사용 중지하면 해당 프로젝트에서 비용이 발생하지 않도록 할 수 있습니다.

제한사항

  • 비용 발생 시점과 예산 알림 수신 시점 사이에는 지연 시간이 있으므로 모든 서비스가 중지된 시간에 기록되지 않은 사용량의 비용이 추가로 발생할 수 있습니다. 이 예의 단계를 따라도 예산보다 많은 비용을 지출하지 않는다고는 보장할 수 없습니다. 자금이 제한되어 있는 경우 결제 지연을 고려하여 가용 자금 아래로 최대 예산을 설정합니다.

  • 결제 계정에 잠겨 있는 프로젝트에서는 결제를 사용 중지할 수 없습니다. 프로젝트 잠금 및 잠금 해제에 관한 자세한 내용은 프로젝트와 결제 계정 간의 링크 보안을 참고하세요.

시작하기 전에

시작하기 전에 다음 작업을 완료해야 합니다.

  1. Cloud Billing API 사용 설정
  2. 단일 프로젝트로 범위가 지정된 예산 만들기
  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 Billing 계정에 서비스 계정을 사용자로 추가하고 적절한 결제 계정 권한을 설정합니다.

    Cloud Billing의 계정 관리 페이지로 이동

Cloud Billing 계정의 권한을 구성하는 방법을 자세히 알아보세요.

Cloud Billing이 사용 중지되었는지 테스트

예산이 알림을 전송하면 지정된 프로젝트에 더 이상 Cloud Billing 계정이 연결되지 않습니다. 함수가 예상대로 작동하는지 확인하려면 Cloud Run 함수 테스트의 단계를 따르세요.

성공하면 프로젝트가 Cloud Billing 계정에 더 이상 표시되지 않으며 동일한 프로젝트에 있는 경우 Cloud Run 함수를 포함하여 프로젝트의 리소스가 사용 중지됩니다.

프로젝트에서 Google Cloud 리소스를 계속 사용하려면 Google Cloud 콘솔에서 프로젝트에 대해 Cloud Billing을 수동으로 다시 사용 설정하세요.

다음 단계

다른 프로그래매틱 알림 예시를 검토하여 다음을 실행하는 방법을 알아보세요.