Allgemeine Tipps zur Entwicklung

Diese Anleitung enthält Best Practices zum Entwerfen, Implementieren, Testen und Bereitstellen eines Cloud Run-Dienstes. Weitere Tipps finden Sie unter Vorhandenen Dienst migrieren.

Effektive Dienste schreiben

In diesem Abschnitt werden allgemeine Best Practices für das Erstellen und Implementieren eines Cloud Run-Dienstes beschrieben.

Hintergrundaktivität

Als Hintergrundaktivität werden alle Aktivitäten bezeichnet, die nach Eingang der HTTP-Antwort erfolgen. Prüfen Sie Ihre Logs auf Punkte, die nach dem Eintrag für die HTTP-Anfrage protokolliert wurden, um festzustellen, ob Hintergrundaktivitäten in Ihrem Dienst vorhanden sind.

Instanzbasierte Abrechnung für die Verwendung von Hintergrundaktivitäten konfigurieren

Wenn Sie Hintergrundaktivitäten in Ihrem Cloud Run-Dienst unterstützen möchten, legen Sie für Ihren Cloud Run-Dienst die instanzbasierte Abrechnung fest. Dadurch können Sie Hintergrundaktivitäten außerhalb von Anfragen ausführen und dennoch Zugriff auf die CPU haben.

Hintergrundaktivitäten vermeiden, wenn die abrechnungsbasierte Abrechnung verwendet wird

Wenn Sie Ihren Dienst so einstellen müssen, dass die Abrechnung anfragebasiert erfolgt, wird der Zugriff der Instanz auf die CPU deaktiviert, wenn der Cloud Run-Dienst die Verarbeitung einer Anfrage beendet oder stark eingeschränkt. Sie sollten keine Hintergrundthreads oder Routinen starten, die außerhalb des Bereichs der Anfrage-Handler ausgeführt werden, wenn Sie diese Art von Abrechnung verwenden.

Prüfen Sie den Code, um sicherzustellen, dass alle asynchronen Vorgänge abgeschlossen sind, bevor Sie eine Antwort senden.

Das Ausführen von Hintergrundthreads mit aktivierter anfragebasierter Abrechnung kann zu unerwartetem Verhalten führen, da jede nachfolgende Anfrage an dieselbe Containerinstanz angehaltene Hintergrundaktivitäten fortsetzt.

Temporäre Dateien löschen

In der Cloud Run-Umgebung werden die Daten in einem In-Memory-Dateisystem gespeichert. In das System geschriebene Dateien belegen Speicher, der ansonsten für den Dienst verfügbar ist. Die Dateien können zwischen Aufrufen bestehen bleiben. Werden sie nicht explizit gelöscht, kann es zu einem Fehler aufgrund Speichermangels und zu einem anschließenden langsamen Containerstart kommen.

Berichtsfehler

Behandeln Sie alle Ausnahmen und lassen Sie den Dienst bei Fehlern nicht abstürzen. Ein Absturz führt zu einem langsamen Containerstart und etwaiger Traffic wird bei einer Ersatzinstanz in die Warteschlange gestellt.

Informationen zur ordnungsgemäßen Erstellung von Fehlerberichten finden Sie im Error Reporting-Leitfaden.

Leistung optimieren

In diesem Abschnitt erfahren Sie mehr über die Best Practices zur Optimierung der Leistung.

Container schnell starten

Da Instanzen nach Bedarf skaliert werden, wirkt sich die Startzeit auf die Latenz Ihres Dienstes aus. Cloud Run entkoppelt den Instanzstart und die Anfrageverarbeitung. In einigen Fällen muss daher auf den Start einer neuen Instanz gewartet werden, bevor die Anfrage verarbeitet wird. Das passiert häufig, wenn ein Dienst von null skaliert wird.

Die Startroutine besteht aus:

  • Container-Image herunterladen (mithilfe der Container-Image-Streaming-Technologie von Cloud Run)
  • Starten des Containers durch Ausführen des entrypoint-Befehls
  • Warten, bis der Container den konfigurierten Port überwacht

Durch die Optimierung der Container-Startgeschwindigkeit wird die Latenz der Anfrageverarbeitung minimiert.

CPU-Boost beim Starten zum Reduzieren der Startlatenz verwenden

Sie können den CPU-Boost beim Start aktivieren, um die CPU-Zuweisung während des Starts einer Instanz vorübergehend zu erhöhen, um die Startlatenz zu reduzieren.

Mindestanzahl von Instanzen zur Verringerung von Container-Startzeiten verwenden

Sie können Mindestinstanzen und Nebenläufigkeit konfigurieren, um die Startzeiten von Containern zu minimieren. Wenn Sie beispielsweise mindestens 1 Instanz verwenden, kann Ihr Dienst die für Ihren Dienst konfigurierte Anzahl gleichzeitiger Anfragen empfangen, ohne eine neue Instanz starten zu müssen. Wenn Sie Mindestinstanzen verwenden, sollten Sie Systembeendigungen vermeiden, die eine Instanz herunterfahren und möglicherweise die Anzahl der Kaltstarts erhöhen.

Eine Anfrage, die auf den Start einer Instanz wartet, wird wie folgt in einer Warteschlange aufbewahrt:

Anfragen bleiben bis zum 3,5-Fachen der durchschnittlichen Startzeit von Containerinstanzen dieses Dienstes oder bis zu 10 Sekunden ausstehend, je nachdem, welcher Wert größer ist.

Abhängigkeiten mit Bedacht verwenden

Wenn Sie eine dynamische Sprache mit abhängigen Bibliotheken verwenden, z. B. Module in Node.js importieren, erhöht die Ladezeit für diese Module die Startlatenz.

Reduzieren Sie die Startverzögerung auf folgende Weise:

  • Minimieren Sie die Anzahl und Größe der Abhängigkeiten, um einen schlanken Service zu erstellen.
  • Laden Sie selten verwendeten Code erst bei Bedarf, sofern Ihre Sprache dies unterstützt.
  • Verwenden Sie Codeladeoptimierungen wie die Composer-Autoloader-Optimierung von PHP.

Globale Variablen verwenden

In Cloud Run können Sie nicht davon ausgehen, dass der Dienststatus zwischen den Anfragen beibehalten wird. Tatsache ist aber, dass Cloud Run die einzelnen Instanzen zur Verarbeitung des laufenden Traffics wiederverwendet. Deshalb können Sie eine globale Variable deklarieren, deren Wert in nachfolgenden Aufrufen wiederverwendet wird. Jedoch kann nicht vorhergesagt werden, ob später eine der Anfragen von dieser Wiederverwendung profitiert.

Sie können Objekte auch im Speicher zwischenspeichern, wenn deren Neuerstellung bei jeder Dienstleistungsanfrage zu ressourcenintensiv wäre. Wenn Sie diese Funktion aus der Anfragelogik in den globalen Bereich verschieben, wird die Leistung verbessert.

Node.js

const functions = require('@google-cloud/functions-framework');

// TODO(developer): Define your own computations
const {lightComputation, heavyComputation} = require('./computations');

// Global (instance-wide) scope
// This computation runs once (at instance cold-start)
const instanceVar = heavyComputation();

/**
 * HTTP function that declares a variable.
 *
 * @param {Object} req request context.
 * @param {Object} res response context.
 */
functions.http('scopeDemo', (req, res) => {
  // Per-function scope
  // This computation runs every time this function is called
  const functionVar = lightComputation();

  res.send(`Per instance: ${instanceVar}, per function: ${functionVar}`);
});

Python

import time

import functions_framework


# Placeholder
def heavy_computation():
    return time.time()


# Placeholder
def light_computation():
    return time.time()


# Global (instance-wide) scope
# This computation runs at instance cold-start
instance_var = heavy_computation()


@functions_framework.http
def scope_demo(request):
    """
    HTTP Cloud Function that declares a variable.
    Args:
        request (flask.Request): The request object.
        <http://flask.pocoo.org/docs/1.0/api/#flask.Request>
    Returns:
        The response text, or any set of values that can be turned into a
        Response object using `make_response`
        <http://flask.pocoo.org/docs/1.0/api/#flask.Flask.make_response>.
    """

    # Per-function scope
    # This computation runs every time this function is called
    function_var = light_computation()
    return f"Instance: {instance_var}; function: {function_var}"

Go


// h is in the global (instance-wide) scope.
var h string

// init runs during package initialization. So, this will only run during an
// an instance's cold start.
func init() {
	h = heavyComputation()
	functions