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 herkömmlichen webbasierten Python-Anwendungen 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
Optimieren Sie das Container-Image, um die Lade- und Startzeiten zu reduzieren. Verwenden Sie dazu die folgenden Methoden:
- Beim Start geladene Dateien minimieren
- WSGI-Server optimieren
Beim Start geladene Dateien minimieren
Um die Startzeit zu optimieren, sollten Sie beim Start nur die erforderlichen Dateien laden und ihre Größe reduzieren. Bei großen Dateien haben Sie folgende Möglichkeiten:
Speichern Sie große Dateien wie KI-Modelle in Ihrem Container, um schneller darauf zugreifen zu können. Laden Sie diese Dateien nach dem Start oder zur Laufzeit.
Erwägen Sie, Cloud Storage-Volume-Bereitstellungen für große Dateien zu konfigurieren, die beim Start nicht kritisch sind, z. B. Medien-Assets.
Importieren Sie nur die erforderlichen Untermodule aus umfangreichen Abhängigkeiten oder importieren Sie Module bei Bedarf in Ihrem Code, anstatt sie beim Start der Anwendung zu laden.
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 Dockerfile
das folgende CMD
hinzu, 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 überwacht den angegebenen Port, noch bevor Ihr 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 für Ihren Cloud Run-Dienst die instanzbasierte Abrechnung fest. Dadurch können Sie Hintergrundaktivitäten außerhalb von Anfragen ausführen und haben dennoch Zugriff auf die CPU.
Startaufgaben reduzieren
Webbasierte Python-Anwendungen können während des Startvorgangs viele Aufgaben ausführen, z. B. Daten vorab laden, Cache vorbereiten und Verbindungspools einrichten. Wenn diese Aufgaben sequenziell ausgeführt werden, kann das lange dauern. Wenn sie jedoch parallel ausgeführt werden sollen, sollten Sie die Anzahl der CPU-Kerne erhöhen.
Cloud Run sendet eine echte Nutzeranfrage, um eine Kaltstartinstanz auszulösen. Bei Nutzern, denen eine Anfrage einer neu gestarteten Instanz zugewiesen wurde, kann es zu langen Verzögerungen kommen.
Sicherheit mit schlanken Basis-Images verbessern
Um die Sicherheit Ihrer Anwendung zu verbessern, sollten Sie ein schlankes Basis-Image mit weniger Paketen und Bibliotheken verwenden.
Wenn Sie Python nicht aus der Quelle in Ihren Containern installieren möchten, verwenden Sie ein offizielles Python-Basis-Image von Docker Hub. Diese Images 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 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. Einige dieser Images enthalten möglicherweise nicht die Elemente, 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 finden Sie unter Systempakete in Cloud Run verwenden.
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.
Umgebungsvariable PYTHONUNBUFFERED
für die Protokollierung verwenden
Wenn Sie ungepufferte Logs Ihrer Python-Anwendung sehen möchten, legen Sie die Umgebungsvariable PYTHONUNBUFFERED
fest. Wenn Sie diese Variable festlegen, sind stdout
- und stderr
-Daten sofort in den Containerlogs sichtbar. Andernfalls werden sie in einem Puffer gespeichert, bis eine bestimmte Datenmenge erreicht ist oder der Stream geschlossen wird.
Nächste Schritte
Weitere Tipps finden Sie unter