Desativar o uso do faturamento com notificações

Este documento explica como desativar automaticamente o faturamento em um projeto quando os custos atingirem ou excederem o orçamento do projeto. Ao desativar o faturamento em um projeto, você encerra todos os serviços Google Cloud no projeto, incluindo os serviços do nível gratuito. Para uma resposta mais sutil às notificações de orçamento, consulte Controlar o uso de recursos com notificações.

Talvez você limite os custos porque tem um valor máximo de dinheiro que pode gastar no Google Cloud. Nesses casos, quando o limite de orçamento é atingido, convém encerrar todos os serviços Google Cloud e o uso para interromper a geração de custos. Desativar o faturamento no seu projeto é um método eficiente para evitar custos nesse projeto.

Limitações

  • Há um atraso entre a geração de custos e o recebimento das notificações de orçamento. Por isso, é possível que haja custos extras pelo uso que não foi faturado no momento em que todos os serviços foram interrompidos. Seguir as etapas neste exemplo não garante que você não vai gastar mais do que o orçamento. Se você tiver uma quantidade limitada de fundos, defina o orçamento máximo abaixo dos recursos disponíveis para compensar atrasos no faturamento.

  • Não é possível desativar o faturamento em um projeto bloqueado para uma conta de faturamento. Para saber mais sobre como bloquear e desbloquear projetos, consulte Proteger o link entre um projeto e a conta de faturamento dele.

Antes de começar

Antes de começar, faça o seguinte:

  1. Ativar a API Cloud Billing
  2. Criar um orçamento com escopo para um único projeto
  3. Configurar notificações de orçamento programático

Configurar uma função do Cloud Run

Para desativar o Cloud Billing em um projeto, crie uma função do Cloud Run e configure-a para chamar a API Cloud Billing.

  1. Siga as etapas em Criar uma função do Cloud Run. Verifique se o tipo de gatilho está definido como o mesmo tópico do Pub/Sub que o orçamento vai usar.
  2. Adicione as seguintes dependências:

    Node.js

    Copie o seguinte para o arquivo 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

    Copie o seguinte para o arquivo requirements.txt:

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

  3. Copie o código abaixo na função do 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. Defina o Ponto de entrada como a função correta a ser executada:

    Node.js

    Defina o Ponto de entrada como stopBilling.

    Python

    Defina o Ponto de entrada como stop_billing.

  5. Revise a lista de variáveis de ambiente definidas automaticamente para determinar se você precisa definir manualmente a variável GOOGLE_CLOUD_PROJECT para o projeto em que quer desativar o Cloud Billing.

  6. Clique em IMPLANTAR.

Configurar permissões da conta de serviço

Sua função do Cloud Run é executada como uma conta de serviço criada automaticamente. Para desativar o faturamento, é necessário conceder à conta de serviço permissões para todos os serviços do projeto que ela precisa modificar. Para isso, siga estas etapas:

  1. Identifique a conta de serviço correta conferindo os detalhes da sua função do Cloud Run. A conta de serviço está listada na parte de baixo da página.
  2. Acesse a página IAM no console do Google Cloud para definir as permissões apropriadas.

    Acessar a página do IAM

  3. Para modificar as permissões da conta de faturamento, acesse a página Gerenciamento da conta no console do Google Cloud, adicione a conta de serviço como um princípio na conta do Cloud Billing e defina as permissões apropriadas da conta de faturamento.

    Acessar a página "Gerenciamento de contas" no Cloud Billing

Saiba como configurar permissões para contas do Cloud Billing.

Testar se o Faturamento do Google Cloud está desativado

Quando o orçamento envia uma notificação, o projeto especificado não tem mais uma conta do Cloud Billing associada a ele. Para garantir que a função funcione conforme o esperado, siga as etapas em Testar uma função do Cloud Run.

Se for bem-sucedido, o projeto não vai mais aparecer na conta do Cloud Billing e os recursos no projeto serão desativados, incluindo a função do Cloud Run, se ela estiver no mesmo projeto.

Para continuar usando os recursos Google Cloud no projeto, no console do Google Cloud, reaative manualmente o Cloud Billing para seu projeto.

A seguir

Confira outros exemplos de notificações programáticas para saber como fazer o seguinte: