Disattivare l'utilizzo della fatturazione con le notifiche

Questo documento spiega come disattivare automaticamente la fatturazione di un progetto quando i costi raggiungono o superano il budget del progetto. Quando disattivi la fatturazione per un progetto, termini tutti i servizi Google Cloud nel progetto, inclusi i servizi del livello gratuito. Per una risposta più sfumata alle notifiche di budget, vedi Controllare l'utilizzo delle risorse con le notifiche.

Potresti limitare i costi perché hai un importo massimo di denaro che puoi spendere per Google Cloud. In questi casi, quando viene raggiunto il limite di budget, potresti voler chiudere tutti i tuoi servizi Google Cloud e il tuo utilizzo per interrompere l'accumulo di costi. La disattivazione della fatturazione per il progetto è un metodo efficace per interrompere l'accumulo di costi nel progetto.

Limitazioni

  • Esiste un ritardo tra il momento in cui vengono sostenuti i costi e quello in cui vengono ricevute le notifiche relative al budget, pertanto potresti sostenere costi aggiuntivi per l'utilizzo che non è stato ancora addebitato al momento dell'interruzione di tutti i servizi. Seguire i passaggi di questo esempio non garantisce che non spenderai più del budget. Se disponi di un importo limitato, imposta il budget massimo al di sotto dei fondi disponibili per tenere conto dei ritardi di fatturazione.

  • Non puoi disattivare la fatturazione per un progetto bloccato su un account di fatturazione. Per scoprire di più sul blocco e sullo sblocco dei progetti, consulta Proteggere il collegamento tra un progetto e il relativo account di fatturazione.

Prima di iniziare

Prima di iniziare, devi completare le seguenti attività:

  1. Abilita l'API Cloud Billing
  2. Crea un budget limitato a un singolo progetto
  3. Configurare le notifiche di budget programmatico

Configura una funzione Cloud Run

Per disattivare la fatturazione Cloud per un progetto, crea una funzione Cloud Run e configurala per chiamare l'API Cloud Billing.

  1. Completa i passaggi descritti in Crea una funzione Cloud Run. Assicurati che il Tipo di trigger sia impostato sullo stesso argomento Pub/Sub che verrà utilizzato dal budget.
  2. Aggiungi le seguenti dipendenze:

    Node.js

    Copia quanto segue nel file 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

    Copia quanto segue nel file requirements.txt:

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

  3. Copia il seguente codice nella funzione 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. Imposta l'entry point sulla funzione corretta da eseguire:

    Node.js

    Imposta il punto di ingresso su stopBilling.

    Python

    Imposta il punto di ingresso su stop_billing.

  5. Esamina l'elenco delle variabili di ambiente impostate automaticamente per determinare se devi impostare manualmente la variabile GOOGLE_CLOUD_PROJECT sul progetto per cui vuoi disattivare la fatturazione Cloud.

  6. Fai clic su DEPLOY.

Configura le autorizzazioni degli account di servizio

La tua funzione Cloud Run viene eseguita come service account creato automaticamente. Per disattivare la fatturazione, devi concedere all'account di servizio le autorizzazioni per qualsiasi servizio del progetto che deve modificare completando i seguenti passaggi:

  1. Identifica il account di servizio corretto visualizzando i dettagli della tua funzione Cloud Run. L'account di servizio è elencato in fondo alla pagina.
  2. Vai alla pagina IAM nella console Google Cloud per impostare le autorizzazioni appropriate.

    Vai alla pagina IAM

  3. Per modificare le autorizzazioni dell'account di fatturazione, nella console Google Cloud , vai alla pagina Gestione account di fatturazione, aggiungi il account di servizio come entità nell'account di fatturazione Cloud e imposta le autorizzazioni dell'account di fatturazione appropriate.

    Vai alla pagina Gestione account in Fatturazione Cloud

Scopri di più su come configurare le autorizzazioni per gli account di fatturazione Cloud.

Verifica che fatturazione Cloud sia disattivato

Quando il budget invia una notifica, il progetto specificato non avrà più un account di fatturazione Cloud associato. Per assicurarti che la funzione funzioni come previsto, segui i passaggi descritti in Testare una funzione Cloud Run.

Se l'operazione va a buon fine, il progetto non è più visibile nell'account di fatturazione Cloud e le risorse del progetto sono disattivate, inclusa la funzione Cloud Run, se si trova nello stesso progetto.

Per continuare a utilizzare le risorse Google Cloud nel progetto, nella consoleGoogle Cloud , riattiva manualmente la fatturazione Cloud per il tuo progetto.

Passaggi successivi

Esamina altri esempi di notifiche programmatiche per scoprire come: