Ressourcennutzung mit Benachrichtigungen steuern

In diesem Dokument wird erläutert, wie Sie Budgetbenachrichtigungen verwenden, um die Ressourcennutzung selektiv zu steuern.

Wenn Sie die Abrechnung für ein Projekt deaktivieren, werden alle Dienste beendet und alle Ressourcen schließlich gelöscht. Wenn Sie eine differenziertere Antwort benötigen, können Sie Ressourcen selektiv steuern. Sie können beispielsweise einige Compute Engine-Ressourcen beenden, während Cloud Storage-Ressourcen intakt bleiben. Wenn Sie nur einige Ressourcen beenden, werden Ihre Kosten gesenkt, ohne dass Ihre Umgebung vollständig deaktiviert wird.

Im folgenden Beispiel werden im Projekt Forschungsarbeiten mit einer Reihe von Compute Engine-VMs ausgeführt und die Ergebnisse in Cloud Storage-Buckets gespeichert. Wenn das Budget überschritten wird, werden die Budgetbenachrichtigungen als Trigger verwendet und diese Cloud Run-Funktion fährt alle Compute Engine-Instanzen herunter. Die gespeicherten Ergebnisse sind davon jedoch nicht betroffen.

Hinweis

Die folgenden Voraussetzungen müssen erfüllt sein:

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

Cloud Run-Funktion einrichten

  1. Folgen Sie der Anleitung unter Cloud Run-Funktion erstellen. Achten Sie darauf, dass Sie für Triggertyp dasselbe Pub/Sub-Thema festlegen, das Ihr Budget verwendet.
  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": ">=20.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": "^5.0.0",
        "@google-cloud/compute": "^6.0.0",
        "@slack/web-api": "^8.0.0",
        "google-auth-library": "^9.0.0",
        "googleapis": "^143.0.0"
      },
      "devDependencies": {
        "@google-cloud/functions-framework": "^5.0.0",
        "c8": "^12.0.0",
        "gaxios": "^7.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 den Einstiegspunkt auf die richtige auszuführende Funktion fest:

    Node.js

    Legen Sie für Einstiegspunkt den Wert limitUse fest.

    Python

    Legen Sie für Einstiegspunkt den Wert 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. Ermitteln Sie das richtige Dienstkonto, indem Sie sich die Details Ihrer Cloud Run-Funktion ansehen. Das Dienstkonto ist am Ende der Seite aufgeführt.
  2. Rufen Sie in der Google Cloud Console die Seite IAM auf, um geeignete Berechtigungen festzulegen.

    Zur IAM-Seite

Prüfen, ob Instanzen beendet wurden

Damit Ihre Funktion wie erwartet funktioniert, folgen Sie der Anleitung unter Cloud Run-Funktion testen.

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

Nächste Schritte

In den folgenden Beispielen für programmatische Benachrichtigungen erfahren Sie, wie Sie: