Controllare l'utilizzo delle risorse con le notifiche

Questo documento spiega come utilizzare le notifiche del budget per controllare selettivamente l'utilizzo delle risorse.

Quando disattivi la fatturazione per un progetto, tutti i servizi vengono interrotti e tutte le risorse vengono eliminate. Se hai bisogno di una risposta più sfumata, puoi controllare selettivamente le risorse. Ad esempio, puoi arrestare alcune risorse Compute Engine lasciando intatte le risorse Cloud Storage. L'arresto solo di alcune risorse riduce i costi senza disattivare completamente l'ambiente.

Nell'esempio seguente, il progetto esegue ricerche con una serie di macchine virtuali (VM) Compute Engine e archivia i risultati in bucket Cloud Storage. Utilizzando le notifiche del budget come trigger, dopo il superamento del budget, questa funzione Cloud Run arresta tutte le istanze Compute Engine, ma non influisce sui risultati archiviati.

Prima di iniziare

Prima di iniziare, devi completare le seguenti attività:

  1. Abilita l'API Cloud Billing
  2. Creare un budget
  3. Configurare le notifiche di budget programmatico

Configura una funzione Cloud Run

  1. Completa i passaggi descritti in Crea una funzione Cloud Run. Assicurati di impostare Tipo di trigger 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 instancesClient = new InstancesClient();
    const ZONE = 'us-central1-a';
    
    exports.limitUse = 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})`;
      }
    
      const instanceNames = await _listRunningInstances(PROJECT_ID, ZONE);
      if (!instanceNames.length) {
        return 'No running instances were found.';
      }
    
      await _stopInstances(PROJECT_ID, ZONE, instanceNames);
      return `${instanceNames.length} instance(s) stopped successfully.`;
    };
    
    /**
     * @return {Promise} Array of names of running instances
     */
    const _listRunningInstances = async (projectId, zone) => {
      const [instances] = await instancesClient.list({
        project: projectId,
        zone: zone,
      });
      return instances
        .filter(item => item.status === 'RUNNING')
        .map(item => item.name);
    };
    
    /**
     * @param {Array} instanceNames Names of instance to stop
     * @return {Promise} Response from stopping instances
     */
    const _stopInstances = async (projectId, zone, instanceNames) => {
      await Promise.all(
        instanceNames.map(instanceName => {
          return instancesClient
            .stop({
              project: projectId,
              zone: zone,
              instance: instanceName,
            })
            .then(() => {
              console.log(`Instance stopped successfully: ${instanceName}`);
            });
        })
      );
    };

    Python

    import base64
    import json
    import os
    from googleapiclient import discovery
    PROJECT_ID = os.getenv("GCP_PROJECT")
    PROJECT_NAME = f"projects/{PROJECT_ID}"
    ZONE = "us-west1-b"
    
    
    def limit_use(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
    
        compute = discovery.build(
            "compute",
            "v1",
            cache_discovery=False,
        )
        instances = compute.instances()
    
        instance_names = __list_running_instances(PROJECT_ID, ZONE, instances)
        __stop_instances(PROJECT_ID, ZONE, instance_names, instances)
    
    
    def __list_running_instances(project_id, zone, instances):
        """
        @param {string} project_id ID of project that contains instances to stop
        @param {string} zone Zone that contains instances to stop
        @return {Promise} Array of names of running instances
        """
        res = instances.list(project=project_id, zone=zone).execute()
    
        if "items" not in res:
            return []
    
        items = res["items"]
        running_names = [i["name"] for i in items if i["status"] == "RUNNING"]
        return running_names
    
    
    def __stop_instances(project_id, zone, instance_names, instances):
        """
        @param {string} project_id ID of project that contains instances to stop
        @param {string} zone Zone that contains instances to stop
        @param {Array} instance_names Names of instance to stop
        @return {Promise} Response from stopping instances
        """
        if not len(instance_names):
            print("No running instances were found.")
            return
    
        for name in instance_names:
            instances.stop(project=project_id, zone=zone, instance=name).execute()
            print(f"Instance stopped successfully: {name}")
    
    

  4. Imposta l'entry point sulla funzione corretta da eseguire:

    Node.js

    Imposta Punto di ingresso su limitUse.

    Python

    Imposta Punto di ingresso su limit_use.

  5. Esamina l'elenco delle variabili di ambiente impostate automaticamente e determina se devi impostare manualmente la variabile GCP_PROJECT sul progetto che esegue le macchine virtuali.

  6. Imposta il parametro ZONE. Questo parametro è la zona in cui le istanze vengono arrestate quando il budget viene superato.

  7. Fai clic su DEPLOY.

Configura le autorizzazioni degli account di servizio

La funzione Cloud Run viene eseguita come service account creato automaticamente. Per controllare l'utilizzo, devi concedere al 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 nella parte inferiore della pagina.
  2. Vai alla pagina IAM nella console Google Cloud per impostare le autorizzazioni appropriate.

    Vai alla pagina IAM

Verifica che le istanze siano arrestate

Per assicurarti che la funzione funzioni come previsto, segui i passaggi descritti in Testare una funzione Cloud Run.

In caso di esito positivo, le VM Compute Engine nella console Google Cloud vengono arrestate.

Passaggi successivi

Esamina altri esempi di notifiche programmatiche per scoprire come: