Desative a utilização da faturação com notificações

Este documento explica como desativar automaticamente a faturação num projeto quando os custos atingem ou excedem o orçamento do projeto. Quando desativa a faturação num projeto, termina todos os serviços Google Cloud no projeto, incluindo os serviços do Nível gratuito. Para uma resposta mais detalhada às notificações de orçamento, consulte o artigo Controle a utilização de recursos com notificações.

Pode limitar os custos porque tem um valor máximo de dinheiro que pode gastar em Google Cloud. Nestes casos, quando o limite do orçamento é atingido, pode querer encerrar todos os seus Google Cloud serviços e utilização para deixar de incorrer em custos. A desativação da faturação no seu projeto é um método eficiente para deixar de incorrer em custos nesse projeto.

Limitações

  • Existe um atraso entre a incorrência de custos e a receção de notificações de orçamento, pelo que pode incorrer em custos adicionais pela utilização que não chegou no momento em que todos os serviços são interrompidos. Seguir os passos neste exemplo não garante que não gaste mais do que o seu orçamento. Se tiver uma quantidade limitada de fundos, defina o orçamento máximo abaixo dos fundos disponíveis para ter em conta os atrasos na faturação.

  • Não pode desativar a faturação num projeto que esteja bloqueado a uma conta de faturação. Para saber mais sobre o bloqueio e o desbloqueio de projetos, consulte o artigo Proteja a associação entre um projeto e a respetiva conta de faturação.

Antes de começar

Antes de começar, tem de concluir as seguintes tarefas:

  1. Ative a API Cloud Billing
  2. Crie um orçamento com âmbito num único projeto
  3. Configure notificações de orçamento programático

Configure uma função do Cloud Run

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

  1. Conclua os passos em Crie uma função do Cloud Run. Certifique-se de que o Tipo de acionador está definido para o mesmo tópico do Pub/Sub que o seu orçamento vai usar.
  2. Adicione as seguintes dependências:

    Node.js

    Copie o seguinte para o ficheiro 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 ficheiro requirements.txt:

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

  3. Copie o código seguinte para a sua 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

    # WARNING: The following action, if not in simulation mode, will disable billing
    # for the project, potentially stopping all services and causing outages.
    # Ensure thorough testing and understanding before enabling live deactivation.
    
    import base64
    import json
    import os
    import urllib.request
    
    from cloudevents.http.event import CloudEvent
    import functions_framework
    
    from google.api_core import exceptions
    from google.cloud import billing_v1
    from google.cloud import logging
    
    billing_client = billing_v1.CloudBillingClient()
    
    
    def get_project_id() -> str:
        """Retrieves the Google Cloud Project ID.
    
        This function first attempts to get the project ID from the
        `GOOGLE_CLOUD_PROJECT` environment variable. If the environment
        variable is not set or is None, it then attempts to retrieve the
        project ID from the Google Cloud metadata server.
    
        Returns:
            str: The Google Cloud Project ID.
    
        Raises:
            ValueError: If the project ID cannot be determined either from
                        the environment variable or the metadata server.
        """
    
        # Read the environment variable, usually set manually
        project_id = os.getenv("GOOGLE_CLOUD_PROJECT")
        if project_id is not None:
            return project_id
    
        # Otherwise, get the `project-id`` from the Metadata server
        url = "http://metadata.google.internal/computeMetadata/v1/project/project-id"
        req = urllib.request.Request(url)
        req.add_header("Metadata-Flavor", "Google")
        project_id = urllib.request.urlopen(req).read().decode()
    
        if project_id is None:
            raise ValueError("project-id metadata not found.")
    
        return project_id
    
    
    @functions_framework.cloud_event
    def stop_billing(cloud_event: CloudEvent) -> None:
        # TODO(developer): As stoping billing is a destructive action
        # for your project, change the following constant to False
        # after you validate with a test budget.
        SIMULATE_DEACTIVATION = True
    
        PROJECT_ID = get_project_id()
        PROJECT_NAME = f"projects/{PROJECT_ID}"
    
        event_data = base64.b64decode(
            cloud_event.data["message"]["data"]
        ).decode("utf-8")
    
        event_dict = json.loads(event_data)
        cost_amount = event_dict["costAmount"]
        budget_amount = event_dict["budgetAmount"]
        print(f"Cost: {cost_amount} Budget: {budget_amount}")
    
        if cost_amount <= budget_amount:
            print("No action required. Current cost is within budget.")
            return
    
        print(f"Disabling billing for project '{PROJECT_NAME}'...")
    
        is_billing_enabled = _is_billing_enabled(PROJECT_NAME)
    
        if is_billing_enabled:
            _disable_billing_for_project(
                PROJECT_NAME,
                SIMULATE_DEACTIVATION
            )
        else:
            print("Billing is already disabled.")
    
    
    def _is_billing_enabled(project_name: str) -> bool:
        """Determine whether billing is enabled for a project.
    
        Args:
            project_name: Name of project to check if billing is enabled.
    
        Returns:
            Whether project has billing enabled or not.
        """
        try:
            print(f"Getting billing info for project '{project_name}'...")
            response = billing_client.get_project_billing_info(name=project_name)
    
            return response.billing_enabled
        except Exception as e:
            print(f'Error getting billing info: {e}')
            print(
                "Unable to determine if billing is enabled on specified project, "
                "assuming billing is enabled."
            )
    
            return True
    
    
    def _disable_billing_for_project(
        project_name: str,
        simulate_deactivation: bool,
    ) -> None:
        """Disable billing for a project by removing its billing account.
    
        Args:
            project_name: Name of project to disable billing.
            simulate_deactivation:
                If True, it won't actually disable billing.
                Useful to validate with test budgets.
        """
    
        # Log this operation in Cloud Logging
        logging_client = logging.Client()
        logger = logging_client.logger(name="disable-billing")
    
        if simulate_deactivation:
            entry_text = "Billing disabled. (Simulated)"
            print(entry_text)
            logger.log_text(entry_text, severity="CRITICAL")
            return
    
        # Find more information about `updateBillingInfo` API method here:
        # https://cloud.google.com/billing/docs/reference/rest/v1/projects/updateBillingInfo
        try:
            # To disable billing set the `billing_account_name` field to empty
            project_billing_info = billing_v1.ProjectBillingInfo(
                billing_account_name=""
            )
    
            response = billing_client.update_project_billing_info(
                name=project_name,
                project_billing_info=project_billing_info
            )
    
            entry_text = f"Billing disabled: {response}"
            print(entry_text)
            logger.log_text(entry_text, severity="CRITICAL")
        except exceptions.PermissionDenied:
            print("Failed to disable billing, check permissions.")

  4. Defina o Ponto de entrada para a função correta a executar:

    Node.js

    Defina o Ponto de entrada como stopBilling.

    Python

    Defina o Ponto de entrada como stop_billing.

  5. Reveja a lista de variáveis de ambiente definidas automaticamente para determinar se tem de definir manualmente a variável GOOGLE_CLOUD_PROJECT para o projeto para o qual quer desativar o Cloud Billing.

  6. Clique em IMPLEMENTAR.

Configure as autorizações da conta de serviço

A sua função do Cloud Run é executada como uma conta de serviço criada automaticamente. Para desativar a faturação, tem de conceder autorizações da conta de serviço a quaisquer serviços no projeto que precise de modificar, concluindo os seguintes passos:

  1. Identifique a conta de serviço correta ao ver os detalhes da sua função do Cloud Run. A conta de serviço é apresentada na parte inferior da página.
  2. Aceda à página IAM na Google Cloud consola para definir as autorizações adequadas.

    Aceda à página IAM

  3. Para modificar as autorizações da conta de faturação, na Google Cloud consola, aceda à página Gestão da conta de faturação, adicione a conta de serviço como um princípio na conta do Cloud Billing e defina as autorizações da conta de faturação adequadas.

    Aceda à página Gestão de contas no Cloud Billing

Saiba como configurar autorizações para contas de faturação do Google Cloud.

Teste se o Cloud Billing está desativado

Quando o orçamento envia uma notificação, o projeto especificado deixa de ter uma conta do Cloud Billing associada. Para garantir que a sua função funciona como esperado, siga os passos em Teste uma função do Cloud Run.

Se tiver êxito, o projeto deixa de estar visível na conta do Cloud Billing e os recursos no projeto são desativados, incluindo a função do Cloud Run, se estiver no mesmo projeto.

Para continuar a usar os Google Cloud recursos no projeto, na Google Cloud consola, reative o Cloud Billing manualmente para o seu projeto.

O que se segue?

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