Abrechnung der Nutzung mit Benachrichtigungen deaktivieren

In diesem Dokument wird erläutert, wie Sie die Abrechnung für ein Projekt automatisch deaktivieren, wenn Ihre Kosten das Projektbudget erreichen oder überschreiten. Wenn Sie die Abrechnung für ein Projekt deaktivieren, werden alle Google Cloud Dienste im Projekt beendet, einschließlich der Dienste im kostenlosen Kontingent. Eine differenziertere Reaktion auf Budgetbenachrichtigungen finden Sie unter Ressourcennutzung mit Benachrichtigungen steuern.

Möglicherweise sind Sie gezwungen, die Kosten zu deckeln, weil Sie bei Ihren Ausgaben für Google Cloudeine feste Obergrenze einhalten müssen. In diesen Fällen möchten Sie die Ausgaben begrenzen und sind möglicherweise bereit, alle Ihre Google Cloud -Dienste und deren Nutzung zu beenden, wenn das Budgetlimit erreicht ist. Wenn Sie die Abrechnung für Ihr Projekt deaktivieren, können Sie effizient verhindern, dass in diesem Projekt Kosten anfallen.

Beschränkungen

  • Zwischen dem Zeitpunkt, zu dem die Kosten anfallen, und dem Empfang von Budgetbenachrichtigungen verzögert sich Budgetbenachrichtigungen. Daher können zusätzliche Kosten für die Nutzung anfallen, da zu dem Zeitpunkt, zu dem alle Dienste beendet wurden, eventuell noch keine vollständige Abrechnung erfolgt ist. Wenn Sie den Schritten in diesem Beispiel folgen, ist das keine Garantie dafür, dass die Ausgaben das Budget nicht überschreiten. Wenn Sie ein begrenztes Budget haben, legen Sie das maximale Budget unterhalb des verfügbaren Guthabens fest, um Verzögerungen bei der Abrechnung zu berücksichtigen.

  • Die Abrechnung kann für ein Projekt, das mit einem Rechnungskonto verknüpft ist, nicht deaktiviert werden. Weitere Informationen zum Sperren und Entsperren von Projekten finden Sie unter Verknüpfung zwischen einem Projekt und seinem Rechnungskonto sichern.

Hinweise

Bevor Sie beginnen, müssen Sie die folgenden Aufgaben erledigen:

  1. Cloud Billing API aktivieren
  2. Budget erstellen, das auf ein einzelnes Projekt beschränkt ist
  3. Programmatische Budgetbenachrichtigungen einrichten

Cloud Run-Funktion einrichten

Wenn Sie Cloud Billing für ein Projekt deaktivieren möchten, erstellen Sie eine Cloud Run-Funktion und konfigurieren Sie sie so, dass sie die Cloud Billing API aufruft.

  1. Führen Sie die Schritte unter Cloud Run-Funktion erstellen aus. Achten Sie darauf, dass der Triggertyp auf dasselbe Pub/Sub-Thema gesetzt ist, das vom 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 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. Legen Sie den Einstiegspunkt auf die richtige auszuführende Funktion fest:

    Node.js

    Legen Sie den Einstiegspunkt auf stopBilling fest.

    Python

    Legen Sie den Einstiegspunkt auf stop_billing fest.

  5. Prüfen Sie die Liste der automatisch festgelegten Umgebungsvariablen, um festzustellen, ob Sie die Variable GOOGLE_CLOUD_PROJECT manuell für das Projekt festlegen müssen, für das Sie Cloud Billing deaktivieren möchten.

  6. Klicken Sie auf DEPLOY (Bereitstellen).

Dienstkontoberechtigungen konfigurieren

Ihre Cloud Run-Funktion wird als automatisch erstelltes Dienstkonto ausgeführt. Damit die Abrechnung deaktiviert werden kann, müssen Sie dem Dienstkonto Berechtigungen für alle Dienste im Projekt erteilen, die geändert werden müssen. Führen Sie dazu die folgenden Schritte aus:

  1. Sehen Sie sich die Details der 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

  3. Wenn Sie die Berechtigungen für das Rechnungskonto ändern möchten, rufen Sie in der Google Cloud Console die Seite Kontoverwaltung für die Abrechnung auf, fügen Sie das Dienstkonto als Prinzipal im Cloud-Rechnungskonto hinzu und legen Sie die entsprechenden Berechtigungen für das Rechnungskonto fest.

    Zur Seite „Kontoverwaltung“ in Cloud Billing

Weitere Informationen zum Konfigurieren von Berechtigungen für Cloud-Rechnungskonten

Deaktivierung von Cloud Billing testen

Wenn das Budget eine Benachrichtigung sendet, ist dem angegebenen Projekt kein Cloud-Rechnungskonto mehr zugeordnet. Damit Ihre Funktion wie erwartet funktioniert, folgen Sie der Anleitung unter Cloud Run-Funktion testen.

Wenn der Vorgang erfolgreich ist, wird das Projekt nicht mehr unter dem Cloud-Rechnungskonto angezeigt und die Ressourcen im Projekt sind deaktiviert. Dies gilt auch für die Cloud Run-Funktion, wenn sie sich im selben Projekt befindet.

Wenn Sie die Google Cloud Ressourcen im Projekt weiterhin verwenden möchten, müssen Sie in derGoogle Cloud Console Cloud Billing für Ihr Projekt manuell wieder aktivieren.

Nächste Schritte

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