Python-Anwendungen für Cloud Run optimieren

In diesem Leitfaden werden Optimierungen für Cloud Run-Dienste beschrieben, die in der Programmiersprache Python geschrieben sind. Außerdem finden Sie hier Hintergrundinformationen, damit Sie die Vor- und Nachteile einiger Optimierungen nachvollziehen können. Die Informationen auf dieser Seite ergänzen die allgemeinen Optimierungstipps, die auch für Python gelten.

Viele der Best Practices und Optimierungen in dieser herkömmlichen webbasierten Python-Anwendung beziehen sich auf folgende Probleme:

  • Verarbeiten gleichzeitiger Anfragen (sowohl thread-basierter als auch nicht blockierender E/A-Vorgänge)
  • Reduzieren der Antwortlatenz durch Verbindungspooling und Stapelverarbeitung nicht kritischer Funktionen, wie z. B. das Senden von Traces und Messwerten an Hintergrundaufgaben.

Container-Image optimieren

Durch die Optimierung des Container-Images können Sie die Lade- und Startzeiten reduzieren. Sie können das Image so optimieren:

  • Nur in den Container einfügen, was Ihre Anwendung zur Laufzeit benötigt
  • WSGI-Server optimieren

Nur in den Container einfügen, was Ihre Anwendung zur Laufzeit benötigt

Überlegen Sie, welche Komponenten im Container enthalten sind und ob sie für die Ausführung des Dienstes erforderlich sind. Es gibt mehrere Möglichkeiten, das Container-Image zu minimieren:

  • Kleineres Basis-Image verwenden
  • Große Dateien aus dem Container heraus verschieben

Kleineres Basis-Image verwenden

Docker Hub bietet eine Reihe von offiziellen Python-Basis-Images, die Sie verwenden können, wenn Sie Python nicht aus der Quelle in Ihren Containern installieren möchten. Diese basieren auf dem Debian-Betriebssystem.

Wenn Sie das python-Image von Docker Hub verwenden, sollten Sie die slim-Version verwenden. Diese Images sind kleiner, da sie nicht eine Reihe von Paketen enthalten, die zum Erstellen von Wheels verwendet werden, die für Ihre Anwendung vielleicht nicht erforderlich sind. Das Python-Image enthält beispielsweise den GNU-C-Compiler, den Präprozessor und Kerndienstprogramme.

Zum Ermitteln der zehn größten Pakete in einem Basis-Image können Sie den folgenden Befehl ausführen:

DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'

Da weniger dieser untergeordneten Pakete vorhanden sind, bieten die slim-basierten Images auch weniger Angriffsfläche für potenzielle Sicherheitslücken. Beachten Sie, dass diese Images möglicherweise nicht die Elemente enthalten, die zur Erstellung von Wheels aus der Quelle erforderlich sind.

Sie können bestimmte Pakete wieder hinzufügen, indem Sie dem Dockerfile eine Zeile RUN apt install hinzufügen. Weitere Informationen zur Verwendung vonSystempaketen in Cloud Run

Es sind auch Optionen für Container verfügbar, die nicht auf Debian basieren. Die Option python:alpine kann zu einem wesentlich kleineren Container führen. Viele Python-Pakete haben jedoch möglicherweise keine vorkompilierten Wheels, die Alpine-basierte Systeme unterstützen. Die Unterstützung wird verbessert (siehe PEP-656), ist aber immer noch unterschiedlich. Sie können auch das distroless base image verwenden, das keine Paketmanager, Shells oder sonstigen Programme enthält.

Große Dateien aus dem Container heraus verschieben

Große Dateien wie Medien-Assets müssen nicht im Basiscontainer enthalten sein.

Google Cloud bietet mehrere Hostingoptionen wie Cloud Storage zum Speichern dieser großen Elemente. Verschieben Sie große Assets in diese Dienste und verweisen Sie dann zur Laufzeit von der Anwendung aus darauf.

WSGI-Server optimieren

Python hat die Art und Weise, wie Anwendungen mit Webservern interagieren können, durch Implementierung des WSGI-Standards PEP-3333 standardisiert. Einer der gängigsten WSGI-Server ist gunicorn, der in einem Großteil der Beispieldokumentation verwendet wird.

gunicorn optimieren

Fügen Sie dem Dockerfile Folgendes hinzu CMD, um den Aufruf von gunicorn zu optimieren:

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

Wenn Sie diese Einstellungen ändern möchten, passen Sie die Anzahl der Worker und Threads pro Anwendung an. Versuchen Sie beispielsweise, eine Anzahl von Workern zu verwenden, die den verfügbaren Kernen entspricht, vergewissern Sie sich, dass es eine Leistungsverbesserung gibt, und passen Sie dann die Anzahl der Threads an. Das Festlegen von zu vielen Workern oder Threads kann sich negativ auf die Kaltstartlatenz, den verbrauchten Arbeitsspeicher, die Anfragen pro Sekunde usw. auswirken.

Standardmäßig startet gunicorn beim Starten Worker und wartet auf den angegebenen Port, noch bevor der Anwendungscode ausgewertet wird. In diesem Fall sollten Sie benutzerdefinierte Startprüfungen für Ihren Dienst einrichten, da die Standardstartprüfung von Cloud Run eine Containerinstanz sofort als fehlerfrei kennzeichnet, sobald sie $PORT überwacht.

Wenn Sie dieses Verhalten ändern möchten, können Sie gunicorn mit der Einstellung --preload aufrufen, um Ihren Anwendungscode vor der Überwachung zu prüfen. Das kann helfen bei:

  • Schweregrad der Laufzeitfehler bei der Bereitstellung ermitteln
  • Speicherressourcen sparen

Sie sollten überlegen, was Ihre Anwendung vorab lädt, bevor Sie dies hinzufügen.

Andere WSGI-Server

Sie sind nicht auf die Verwendung von gunicorn zum Ausführen von Python in Containern beschränkt. Sie können einen beliebigen WSGI- oder ASGI-Webserver verwenden, solange der Container den HTTP-Port $PORT gemäß dem Containerlaufzeitvertrag überwacht.

Gängige Alternativen sind uwsgi, uvicorn und waitress.

Bei der folgenden Datei namens main.py mit dem app-Objekt würden die folgenden Aufrufe einen WSGI-Server starten:

# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app

# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app

# waitress: pip install waitress
waitress-serve --port $PORT main:app

Diese können entweder als CMD exec-Zeile in einem Dockerfile oder als web:-Eintrag in Procfile hinzugefügt werden, wenn Google Cloud Buildpacks verwendet wird.

Anwendungen optimieren

In Ihrem Cloud Run-Dienstcode können Sie auch die Startzeiten und die Arbeitsspeichernutzung optimieren.

Threads reduzieren

Sie können den Arbeitsspeicher optimieren, indem Sie die Anzahl der Threads reduzieren, indem Sie nicht blockierende reaktive Strategien verwenden und Hintergrundaktivitäten vermeiden. Vermeiden Sie außerdem das Schreiben in das Dateisystem, wie auf der Seite Allgemeine Tipps beschrieben.

Wenn Sie Hintergrundaktivitäten in Ihrem Cloud Run-Dienst unterstützen möchten, legen Sie die CPU des Cloud Run-Dienstes so fest, dass sie immer zugewiesen wird. Dadurch können Sie Hintergrundaktivitäten außerhalb von Anfragen ausführen und dennoch Zugriff auf die CPU haben.

Startaufgaben reduzieren

Webbasierte Python-Anwendungen können während des Startvorgangs viele Aufgaben ausführen, z. B. Daten vorab laden, Cache vorbereiten, Verbindungspools einrichten usw. Diese Aufgaben können, wenn sie nacheinander ausgeführt werden, langsam sein. Wenn sie jedoch parallel ausgeführt werden sollen, sollten Sie die Anzahl der CPU-Kerne erhöhen.

Cloud Run sendet derzeit eine echte Nutzeranfrage, um eine Kaltstartinstanz auszulösen. Bei Nutzern, bei denen eine Anfrage einer neu gestarteten Instanz zugewiesen wurde, kann es zu langen Verzögerungen kommen. Cloud Run verfügt derzeit über keine Bereitschaftsprüfung, um zu verhindern, dass Anfragen an nicht bereite Anwendungen gesendet werden.

Nächste Schritte

Weitere Tipps finden Sie unter