Ressourcennutzung mit Benachrichtigungen steuern

In diesem Dokument wird erläutert, wie Sie mit Budgetbenachrichtigungen die Ressourcennutzung selektiv steuern können.

Wenn Sie die Abrechnung für ein Projekt deaktivieren, werden alle Dienste beendet und alle Ressourcen werden schließlich gelöscht. Wenn Sie eine differenziertere Rückmeldung benötigen, können Sie Ressourcen selektiv steuern. Sie können beispielsweise einige Compute Engine-Ressourcen beenden, Cloud Storage-Ressourcen aber intakt lassen. Wenn Sie nur einige Ressourcen beenden, senken Sie Ihre Kosten, ohne Ihre Umgebung vollständig zu deaktivieren.

Im folgenden Beispiel führt das Projekt eine Untersuchung mit einer Reihe von virtuellen Compute Engine-Maschinen (VMs) durch und speichert die Ergebnisse in Cloud Storage-Buckets. Mit den Budgetbenachrichtigungen als Trigger werden nach Überschreiten des Budgets alle Compute Engine-Instanzen von dieser Cloud Run-Funktion heruntergefahren. Die gespeicherten Ergebnisse bleiben davon unberührt.

Hinweise

Führen Sie die folgenden Aufgaben aus, bevor Sie beginnen:

  1. Cloud Billing API aktivieren
  2. Budget erstellen
  3. Programmatische Budgetbenachrichtigungen einrichten

Cloud Run-Funktion einrichten

  1. Führen Sie die Schritte unter Cloud Run-Funktion erstellen aus. Achten Sie darauf, dass Sie den Triggertyp auf dasselbe Pub/Sub-Thema festlegen, das für Ihr Budget verwendet wird.
  2. Fügen Sie die folgenden Abhängigkeiten hinzu:

    Node.js

    Kopieren Sie Folgendes in Ihre package.json-Datei:

    {
      "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

    Kopieren Sie Folgendes in Ihre requirements.txt-Datei:

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

  3. Kopieren Sie den folgenden Code in Ihre Cloud Run-Funktion:

    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. Legen Sie als Einstiegspunkt die richtige Funktion fest, die ausgeführt werden soll:

    Node.js

    Legen Sie als Einstiegspunkt limitUse fest.

    Python

    Legen Sie als Einstiegspunkt limit_use fest.

  5. Prüfen Sie die Liste der automatisch festgelegten Umgebungsvariablen und legen Sie fest, ob die Variable GCP_PROJECT manuell für das Projekt festgelegt werden muss, in dem die virtuellen Maschinen ausgeführt werden.

  6. Legen Sie den Parameter ZONE fest. Dieser Parameter ist die Zone, in der Instanzen beendet werden, wenn das Budget überschritten wird.

  7. Klicken Sie auf DEPLOY (Bereitstellen).

Dienstkontoberechtigungen konfigurieren

Ihre Cloud Run-Funktion wird als automatisch erstelltes Dienstkonto ausgeführt. Wenn Sie die Nutzung steuern möchten, müssen Sie dem Dienstkonto Berechtigungen für alle Dienste im Projekt erteilen, die es ändern muss. Gehen Sie dazu so vor:

  1. Sehen Sie sich die Details Ihrer Cloud Run-Funktion an, um das richtige Dienstkonto zu ermitteln. Das Dienstkonto ist am Ende der Seite aufgeführt.
  2. Rufen Sie in der Google Cloud Console die Seite IAM auf, um die entsprechenden Berechtigungen festzulegen.

    Zur IAM-Seite

Prüfen, ob Instanzen beendet wurden

Führen Sie die Schritte unter Cloud Run-Funktionen testen aus, um sicherzustellen, dass Ihre Funktion wie erwartet funktioniert.

Wenn der Vorgang erfolgreich war, werden Ihre Compute Engine-VMs in der Google Cloud Console angehalten.

Nächste Schritte

In anderen Beispielen für programmatische Benachrichtigungen erfahren Sie, wie Sie Folgendes tun: