1. Einführung
Letzte Aktualisierung: 01.11.2024
Wie kann ich eine alte PHP-Anwendung für Google Cloud modernisieren?
(📽️ 7-minütige Einführungsvideo zu diesem Codelab ansehen)
Es ist üblich, dass On-Premise-Legacy-Anwendungen modernisiert werden müssen. Das bedeutet, dass sie skalierbar, sicher und in verschiedenen Umgebungen einsetzbar sein müssen.
In diesem Workshop lernen Sie Folgendes:
- Containerisieren Sie die PHP-Anwendung.
- Wechseln Sie zu einem verwalteten Datenbankdienst (Cloud SQL).
- In Cloud Run bereitstellen (Zero-Ops-Alternative zu GKE/Kubernetes)
- Sichern Sie die Anwendung mit Identity and Access Management (IAM) und Secret Manager.
- Definieren Sie eine CI/CD-Pipeline über Cloud Build. Cloud Build kann mit Ihrem Git-Repository verbunden werden, das bei gängigen Git-Anbietern wie GitHub oder GitLab gehostet wird. Es kann beispielsweise bei jedem Push auf „main“ ausgelöst werden.
- Hosten Sie die Bilder der Anwendung in Cloud Storage. Das geschieht durch das Bereitstellen und es ist kein Code erforderlich, um die App zu ändern.
- Einführung der Gen AI-Funktionen über Gemini, orchestriert über Cloud Functions (serverlos).
- Machen Sie sich mit SLOs und der Bedienung Ihrer aktualisierten App vertraut.
Wenn Sie diese Schritte ausführen, können Sie Ihre PHP-Anwendung schrittweise modernisieren und so ihre Skalierbarkeit, Sicherheit und Flexibilität bei der Bereitstellung verbessern. Außerdem können Sie mit der Umstellung auf Google Cloud die leistungsstarke Infrastruktur und die Dienste nutzen, um eine reibungslose Ausführung Ihrer Anwendung in einer cloudnativen Umgebung zu gewährleisten.
Wir sind davon überzeugt, dass Sie das, was Sie anhand dieser einfachen Schritte lernen, auf Ihre eigene Anwendung und Organisation mit einer anderen Sprache/einem anderen Stack und anderen Anwendungsfällen anwenden können.
Informationen zur App
Die Anwendung ( Code, MIT-Lizenz), die Sie forken, ist eine einfache PHP 5.7-Anwendung mit MySQL-Authentifizierung. Die App soll eine Plattform bieten, auf der Nutzer Fotos hochladen können und Administratoren unangemessene Bilder taggen können. Die Anwendung hat zwei Tabellen:
- Nutzer Vorkompilierte Version für Administratoren Neue Nutzer können sich registrieren.
- Bilder. Enthält einige Beispielbilder. Angemeldete Nutzer können neue Bilder hochladen. Wir fügen hier ein wenig Magie hinzu.
Dein Ziel
Wir möchten die alte Anwendung modernisieren, damit sie in Google Cloud verwendet werden kann. Wir werden die Tools und Dienste von Google Cloud nutzen, um die Skalierbarkeit zu verbessern, die Sicherheit zu erhöhen, die Infrastrukturverwaltung zu automatisieren und erweiterte Funktionen wie Bildverarbeitung, Monitoring und Datenspeicherung mithilfe von Diensten wie Cloud SQL, Cloud Run, Cloud Build und Secret Manager zu integrieren.
Noch wichtiger ist, dass wir es Schritt für Schritt machen, damit Sie erfahren können, was der Denkprozess hinter jedem Schritt ist. Normalerweise eröffnet jeder Schritt neue Möglichkeiten für die nächsten (z. B. Module 2 -> 3 und 6 -> 7).
Sie sind noch nicht überzeugt? Sehen Sie sich dieses 7-minütige Video auf YouTube an.
Voraussetzungen
- Ein mit dem Internet verbundener Computer mit Browser.
- Einige GCP-Guthaben. Frag einfach deinen lokalen Google-Fan. ;)
- Der Befehl
gcloud
funktioniert. - Wenn Sie lokal arbeiten, können Sie das Tool hier herunterladen. Außerdem brauchen Sie einen praktischen Editor (z.B. vscode oder intellij).
- Möchten Sie alles in der Cloud erledigen? Dann können Sie Cloud Shell verwenden.
- GitHub-Nutzer. Sie benötigen dies, um den ursprünglichen Code 🧑🏻💻 gdgpescara/app-mod-workshop in Ihrem eigenen Git-Repository zu verzweigen. Dies ist erforderlich, um eine eigene CI/CD-Pipeline (automatische Commits -> Build -> Bereitstellung) zu haben.
Beispiele für Lösungen:
- Repository des Autors: https://github.com/Friends-of-Ricc/app-mod-workshop
- Das ursprüngliche Workshop-Repository in
.solutions/
-Ordnern pro Kapitel.
Sie können diesen Workshop auf Ihrem lokalen Computer oder vollständig in einem Browser absolvieren.
2. Guthabeneinrichtung und Fork
Google Cloud-Guthaben einlösen und GCP-Umgebung einrichten [optional]
Für diesen Workshop benötigen Sie ein Rechnungskonto mit Guthaben. Wenn Sie bereits eine eigene Abrechnung haben, können Sie diesen Schritt überspringen.
Erstelle ein brandneues Google Gmail-Konto (*), das du mit deinem GCP-Guthaben verknüpfen kannst. Fragen Sie Ihren Kursleiter nach dem Link zum Einlösen des GCP-Guthabens oder verwenden Sie das Guthaben hier: bit.ly/PHP-Amarcord-credits .
Melden Sie sich mit dem neu erstellten Konto an und folgen Sie der Anleitung.
(
) Warum benötige ich ein komplett neues Gmail-Konto?*
Wir haben festgestellt, dass einige Teilnehmer das Codelab nicht bestehen, weil ihr Konto (insbesondere geschäftliche oder Studenten-E-Mail-Adressen) bereits mit GCP in Berührung gekommen ist und Organisationsrichtlinien die Teilnahme einschränken. Wir empfehlen, entweder ein neues Gmail-Konto zu erstellen oder ein vorhandenes Gmail-Konto (gmail.com) zu verwenden, das noch nicht mit GCP in Berührung gekommen ist.
Klicke auf die Schaltfläche, um den Gutschein einzulösen.
Füllen Sie das folgende Formular mit Ihrem Namen und Nachnamen aus und stimmen Sie den Nutzungsbedingungen zu.
Es kann einige Sekunden dauern, bis das Rechnungskonto hier angezeigt wird: https://console.cloud.google.com/billing
Öffnen Sie anschließend die Google Cloud Console und erstellen Sie ein neues Projekt. Klicken Sie dazu links oben im Drop-down-Menü, in dem „Keine Organisation“ angezeigt wird, auf die Projektauswahl. Siehe unten.
Erstellen Sie ein neues Projekt, falls Sie noch keins haben (siehe Screenshot unten). Oben rechts sehen Sie die Option „NEUES PROJEKT“.
Verknüpfen Sie das neue Projekt wie unten beschrieben mit dem Abrechnungskonto für den GCP-Testzeitraum.
Sie können jetzt die Google Cloud Platform verwenden. Wenn Sie ein Anfänger sind oder alles in einer Cloud-Umgebung erledigen möchten, können Sie über die folgende Schaltfläche links oben auf Cloud Shell und den Editor zugreifen (siehe Abbildung unten).
Das neue Projekt muss oben links ausgewählt sein:
Nicht ausgewählt (schlecht):
Ausgewählt (gut):
App von GitHub forken
- Demo-App aufrufen: https://github.com/gdgpescara/app-mod-workshop
- Klicke auf die Fork.
- Wenn Sie noch kein GitHub-Konto haben, müssen Sie ein neues erstellen.
- Bearbeiten Sie die Elemente nach Belieben.
- Klonen Sie den App-Code mit git clone https://github.com/<IHR-GITHUB-NUTZER>/<IHR-REPO-NAME>
- Öffnen Sie den Ordner des geklonten Projekts in Ihrem bevorzugten Editor. Wenn Sie Cloud Shell auswählen, klicken Sie wie unten gezeigt auf „Editor öffnen“.
Der Google Cloud Shell-Editor bietet alles, was Sie benötigen, wie die folgende Abbildung zeigt.
3. Modul 1: SQL-Instanz erstellen
Google Cloud SQL-Instanz erstellen
Unsere PHP-Anwendung stellt eine Verbindung zu einer MySQL-Datenbank her. Daher müssen wir sie für eine problemlose Migration zu Google Cloud replizieren. Cloud SQL ist die perfekte Lösung, da Sie damit eine vollständig verwaltete MySQL-Datenbank in der Cloud ausführen können. Gehen Sie dazu so vor:
- Rufen Sie die Cloud SQL-Seite auf: https://console.cloud.google.com/sql/instances
- Klicken Sie auf „Instanz erstellen“.
- Aktivieren Sie die API, falls erforderlich. Dies kann einige Sekunden dauern.
- Wählen Sie MySQL aus.
- (Wir versuchen, Ihnen die günstigste Version zu senden, damit sie länger hält):
- Version: Enterprise
- Voreinstellung: development (wir haben die Sandbox ausprobiert, aber sie hat nicht funktioniert)
- MySQL-Version: 5.7 (wow, pure Nostalgie!)
- Instanz-ID: Wählen Sie
appmod-phpapp
aus. Wenn Sie diese ändern, denken Sie daran, auch zukünftige Scripts und Lösungen entsprechend zu ändern. - Passwort: beliebig, aber notieren Sie es als CLOUDSQL_INSTANCE_PASSWORD
- Region: für die App genauso wie für den Rest der App beibehalten (z. B. Mailand =
europe-west8
) - Zonales Avail-Dokument: Einzelne Zone (wir sparen Geld für die Demo)
Klicken Sie auf die Schaltfläche „Instanz erstellen“, um die Cloud SQL-Datenbank bereitzustellen. ⌛ Die Bereitstellung dauert etwa 10 Minuten.⌛ In der Zwischenzeit können Sie die Dokumentation weiterlesen oder mit dem nächsten Modul („PHP-Anwendung containerisieren“) beginnen, da es keine Abhängigkeiten von diesem Modul im ersten Teil gibt (bis Sie die Datenbankverbindung korrigiert haben).
Hinweis: Diese Instanz sollte etwa 7$/Tag kosten. Vergessen Sie nicht, es nach dem Workshop auszubauen.
Datenbank „image_catalog“ und Nutzer in Cloud SQL erstellen
Das App-Projekt enthält den Ordner db/
mit zwei SQL-Dateien:
- 01_schema.sql: Enthält SQL-Code zum Erstellen von zwei Tabellen mit Daten zu Nutzern und Bildern.
- 02_seed.sql: Enthält SQL-Code zum Einfügen von Daten in die zuvor erstellten Tabellen.
Diese Dateien werden später verwendet, wenn die image_catalog
-Datenbank erstellt wird. Gehen Sie dazu so vor:
- Öffnen Sie Ihre Instanz und klicken Sie auf den Tab „Datenbanken“:
- Klicken Sie auf „Datenbank erstellen“.
- und nennen Sie sie
image_catalog
(wie in der PHP-Anwendungskonfiguration).
Dann erstellen wir den Datenbanknutzer. Damit können wir uns in der Datenbank „image_catalog“ authentifizieren.
- Klicken Sie jetzt auf den Tab Nutzer.
- Klicken Sie auf „Nutzerkonto hinzufügen“.
- Nutzer: Lassen Sie uns einen erstellen:
- Nutzername:
appmod-phpapp-user
- Passwort: Wählen Sie ein Passwort, das Sie sich merken können, oder klicken Sie auf „Generieren“.
- Behalten Sie die Option Jeden Host zulassen (%) bei.
- Klicken Sie auf „HINZUFÜGEN“.
Öffnen Sie die Datenbank für bekannte IP-Adressen.
Alle Datenbanken in Cloud SQL sind „isoliert“. Sie müssen explizit ein Netzwerk einrichten, von dem aus zugegriffen werden kann.
- Klicken Sie auf Ihre Instanz.
- Öffnen Sie das Menü „Verbindungen“.
- Klicken Sie auf den Tab „Netzwerk“.
- Klicken Sie unter „Autorisierte Netzwerke“ auf Fügen Sie nun ein Subnetz hinzu.
- Legen wir fürs Erste fest, dass die App unsicher ist, damit sie funktioniert:
- Name: „Alle auf der Welt – UNSICHER“ (Denken Sie daran, dass diese billige Lösung auch unsicher ist).
- Netzwerk: „0.0.0.0/0“ (Hinweis: unsicher)
Klicken Sie auf "Speichern".
Auf dem Bildschirm sollte Folgendes zu sehen sein:
Hinweis: Diese Lösung ist ein guter Kompromiss, um den Workshop in O(Stunden) abzuschließen. Wie Sie Ihre Lösung für die Produktion absichern, erfahren Sie jedoch im Dokument SICHERHEIT.
Testen Sie jetzt die Datenbankverbindung.
Sehen wir uns an, ob der zuvor erstellte Nutzer image_catalog
funktioniert. Rufen Sie Cloud SQL Studio in der Instanz auf und geben Sie die zu authentifizierenden Datenbank, den Nutzer und das Passwort ein (siehe Abbildung unten):
Öffnen Sie nun den SQL-Editor und fahren Sie mit dem nächsten Abschnitt fort.
Datenbank aus Codebasis importieren
Importieren Sie die Tabellen „image_catalog“ mit ihren Daten mit dem SQL-Editor. Rufen Sie den SQL-Code aus den SQL-Dateien im Repository ab und führen Sie ihn nacheinander aus. 01_schema.sql und dann 02_seed.sql.
Danach sollten Sie im „image_catalog“ zwei Tabellen sehen: users und images, wie unten dargestellt:
Sie können das testen, indem Sie im Editor Folgendes ausführen: select * from images;
Notieren Sie sich auch die öffentliche IP-Adresse, da Sie sie später benötigen.
4. Modul 2: PHP-Anwendung containerisieren
Wir möchten diese App für die Cloud entwickeln.
Dazu muss der Code in eine ZIP-Datei verpackt werden, die alle Informationen zur Ausführung in der Cloud enthält.
Es gibt verschiedene Möglichkeiten, das Paket zu verpacken:
- Docker Sehr beliebt, aber recht komplex in der Einrichtung.
- Buildpacks Weniger beliebt, aber neigt dazu, automatisch zu erraten, was erstellt und ausgeführt werden soll. Oft funktioniert es einfach!
In diesem Workshop gehen wir davon aus, dass Sie Docker verwenden.
Docker
Wenn Sie die Kontrolle behalten möchten, ist dies die richtige Lösung für Sie. Dies ist sinnvoll, wenn Sie bestimmte Bibliotheken konfigurieren und bestimmte nicht offensichtliche Verhaltensweisen einschleusen müssen (z. B. chmod bei Uploads oder eine nicht standardmäßige ausführbare Datei in Ihrer App).
Da wir unsere containerisierte Anwendung letztendlich in Cloud Run bereitstellen möchten, sehen Sie sich die folgende Dokumentation an und versuchen Sie, die Lücken zu füllen. Wir stellen nur das Wesentliche zur Verfügung, um die Dinge vorerst einfach zu halten. Ihr endgültiges Dockerfile sollte in etwa so aussehen:
# Use an official PHP image with Apache
# Pull a suitable php image
FROM __________# Define the env variable for the Apache listening port 8080
ENV __________
# Set working directory inside the container
WORKDIR __________
# Install required PHP extensions: PDO, MySQL, and other dependencies
RUN __________
# Copy all application files into the container
COPY __________
# Configure Apache to listen on port 8080. Use ‘sed' command to change the default listening port.
RUN __________
# When in doubt, always expose to port 8080
EXPOSE __________
# Start Apache in the foreground
CMD __________
Außerdem müssen wir die Datei config.php so ändern, dass unsere PHP-Anwendung eine Verbindung zur MYSQL-Datenbank herstellen kann, die in Google CloudSQL verfügbar ist, damit wir unsere Anwendung lokal testen können. Füllen Sie die Lücken aus.
- „Db_host“ ist die öffentliche IP-Adresse von Cloud SQL. Sie finden sie in der Console:
- Der Datenbankname sollte unverändert bleiben:
image_catalog
- Db_user sollte
appmod-phpapp-user
sein. - Db_pass ist ein von Ihnen ausgewählter Wert. Setzen Sie es in einfache Anführungszeichen und maskieren Sie es nach Bedarf.
<?php
// Database configuration
$db_host = '____________';
$db_name = '____________';
$db_user = '____________';
$db_pass = '____________';
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Errore di connessione: " . $e->getMessage());
}
session_start();
?>
Mit Gemini kannst du die wenigen italienischen Artikel auch ins Englische übersetzen.
Okay, jetzt haben Sie das Dockerfile und Ihre PHP-Anwendung so konfiguriert, dass eine Verbindung zur Datenbank hergestellt wird. Testen wir das Ganze!
Installieren Sie Docker, falls noch nicht geschehen (Link). Wenn Sie Cloud Shell verwenden, ist das nicht erforderlich.
Versuchen Sie jetzt, Ihre containerisierte PHP-Anwendung mit den entsprechenden Docker-Build- und -Run-Befehlen zu erstellen und auszuführen.
- docker build -t <IMAGE_TAG_NAME> .
- docker run -it -p <CONTAINER PORT>:<LOCAL MACHINE PORT> <IMAGE_TAG_NAME>
Wenn alles funktioniert, sollten Sie die folgende Webseite sehen, wenn Sie mit dem lokalen Host verbunden sind.
Wenn Sie mit Cloud Shell arbeiten, können Sie auch den lokalen Port (z. B. 8080) so in Ihren Browser exportieren:
docker build -t my-php-app-docker app-mod-workshop/ -f Dockerfile
docker run -it -p 8080:8080 my-php-app-docker
Sie wissen nun, dass Ihre App auf Port 8080 ausgeführt wird. Klicken Sie nun auf das Symbol „Webvorschau“ (ein Browser mit Auge) und dann auf Vorschau auf Port 8080 (oder auf „Port ändern“ für jeden anderen Port).
Ergebnis im Browser testen
Ihre Anwendung sollte jetzt in etwa so aussehen:
Wenn Sie sich mit Admin/admin123 anmelden, sollte in etwa Folgendes angezeigt werden:
Super! Es funktioniert 🎉🎉🎉
Wenn die Docker-Umgebung in Ordnung ist, die DB-Anmeldedaten aber falsch sind, erhalten Sie möglicherweise eine Meldung wie diese:
Du bist nah dran! Versuche es noch einmal.
Buildpacks [optional]
Mit Buildpacks wird die App automatisch erstellt. Leider haben Sie keine vollständige Kontrolle, sodass es zu unerwarteten Konfigurationen kommen kann.
- Weitere Informationen zu BuildPacks in der GCP finden Sie unter https://cloud.google.com/docs/buildpacks/build-application und in diesem Artikel.
pack
installieren: https://buildpacks.io/docs/for-platform-operators/how-to/integrate-ci/pack/- "buildpacks" in PHP: https://cloud.google.com/docs/buildpacks/php (hier wird erklärt, wie die PHP-Version eingerichtet wird)
- Sie können z. B. Folgendes versuchen, um das Container-Image automatisch zu erstellen:
pack build --builder=gcr.io/buildpacks/builder my-app-with-buildpacks
In Ihrer lokalen Umgebung sollte ein neues Docker-Image vorhanden sein. Sie können versuchen, einen Container dafür auszuführen. Da wir jedoch nicht vollständig kontrollieren können, wie das Image erstellt wurde, funktioniert die App möglicherweise nicht. Wir empfehlen dir auf jeden Fall, die Funktion auszuprobieren und uns deine Meinung mitzuteilen, falls sie erfolgreich ist.
In Artifact Registry speichern [optional]
Sie sollten jetzt eine funktionierende containerisierte PHP-Anwendung haben, die in der Cloud bereitgestellt werden kann. Als Nächstes benötigen wir einen Ort in der Cloud, an dem wir unser Docker-Image speichern und für die Bereitstellung in Google Cloud-Diensten wie Cloud Run zugänglich machen können. Diese Speicherlösung heißt Artifact Registry. Es handelt sich um einen vollständig verwalteten Google Cloud-Dienst zum Speichern von Anwendungsartefakten, einschließlich Docker-Container-Images, Maven-Paketen und npm-Modulen.
Erstellen wir mit der entsprechenden Schaltfläche ein Repository in Google Cloud Artifact Registry.
Wählen Sie einen gültigen Namen, das Format und die Region zum Speichern der Artefakte aus.
Kehren Sie zum Tag Ihrer lokalen Entwicklungsumgebung zurück und übertragen Sie das App-Container-Image in das gerade erstellte Artifact Registry-Repository. Führen Sie dazu die folgenden Befehle aus.
- docker tag QUELLIMAGE[:TAG] ZIELIMAGE[:TAG]
- docker push TARGET_IMAGE[:TAG]
Das Ergebnis sollte wie im folgenden Screenshot aussehen.
Herzlichen Glückwunsch! 🎉🎉🎉 Du kannst mit dem nächsten Level fortfahren.
Notiz. Versuchen Sie auch, ein Bild über den Endpunkt /upload.php
hochzuladen. Möglicherweise wird die Meldung „Berechtigung verweigert“ angezeigt. In diesem Fall müssen Sie im Dockerfile
einige chmod/chown
-Änderungen vornehmen.
5. Modul 3: Anwendung in Cloud Run bereitstellen
Warum Cloud Run?
Gute Frage! Vor Jahren hätten Sie sich sicherlich für die Google App Engine entschieden.
Cloud Run hat einen neueren Technologie-Stack, ist einfacher bereitzustellen, günstiger und wird auf null skaliert, wenn es nicht verwendet wird. Dank der Flexibilität für die Ausführung zustandsloser Container und der Einbindung in verschiedene Google Cloud-Dienste eignet es sich ideal für die Bereitstellung von Mikrodiensten und modernen Anwendungen mit minimalem Aufwand und maximaler Effizienz.
Cloud Run ist eine vollständig verwaltete Google Cloud-Plattform, mit der Sie zustandslose containerisierte Anwendungen in einer serverlosen Umgebung ausführen können. Er verwaltet die gesamte Infrastruktur automatisch und skaliert sie von null auf, um den eingehenden Traffic zu bewältigen, und wieder herunter, wenn er inaktiv ist. Das ist kostengünstig und effizient. Cloud Run unterstützt jede Sprache oder Bibliothek, sofern sie in einem Container verpackt sind. Das ermöglicht eine hohe Flexibilität bei der Entwicklung. Sie lässt sich gut in andere Google Cloud-Dienste einbinden und eignet sich zum Erstellen von Mikrodiensten, APIs, Websites und ereignisgesteuerten Anwendungen, ohne dass die Serverinfrastruktur verwaltet werden muss.
Voraussetzungen
Dazu muss gcloud
auf Ihrem lokalen Computer installiert sein. Falls nicht, finden Sie hier eine Anleitung. Wenn Sie Google Cloud Shell verwenden, müssen Sie nichts weiter unternehmen.
Vor der Bereitstellung
Wenn Sie in Ihrer lokalen Umgebung arbeiten, authentifizieren Sie sich mit dem folgenden Befehl bei Google Cloud:
$ gcloud auth login –update-adc # not needed in Cloud Shell
Damit sollten Sie über einen OAuth-Log-in in Ihrem Browser authentifiziert werden. Melden Sie sich über Chrome mit demselben Nutzer an (z. B. vattelapesca@gmail.com), der mit aktivierter Abrechnung in Google Cloud angemeldet ist.
Aktivieren Sie die Cloud Run API mit dem folgenden Befehl
$ gcloud services enable run.googleapis.com
Jetzt kann alles in Cloud Run bereitgestellt werden.
Anwendung über gcloud in Cloud Run bereitstellen
Mit dem Befehl gcloud run deploy
können Sie die Anwendung in Cloud Run bereitstellen. Es gibt mehrere Optionen, mit denen Sie Ihr Ziel erreichen können. Folgende Mindestanforderungen gelten:
- Name des Cloud Run-Dienstes, den Sie für Ihre App bereitstellen möchten. Ein Cloud Run-Dienst gibt eine URL zurück, die einen Endpunkt für Ihre App bereitstellt.
- Google Cloud-Region, in der Ihre App ausgeführt wird.
- Container-Image, das Ihre App umschließt.
- Umgebungsvariablen, die Ihre App während der Ausführung verwenden muss.
- Das Flag „Allow-Unauthenticated“, das es allen erlaubt, ohne weitere Authentifizierung auf Ihre App zuzugreifen
In der Dokumentation erfahren Sie, wie Sie diese Option auf Ihren Befehl anwenden. Die Bereitstellung dauert einige Minuten. Wenn alles richtig ist, sollte in der Google Cloud Console ungefähr Folgendes angezeigt werden:
Klicken Sie auf die von Cloud Run bereitgestellte URL und testen Sie Ihre Anwendung. Nach der Authentifizierung sollten Sie in etwa Folgendes sehen.
„gcloud run deploy“ mit „no questions“
Sie haben vielleicht bemerkt, dass gcloud run deploy
Ihnen die richtigen Fragen stellt und die Lücken füllt, die Sie hinterlassen haben. Das ist ja fantastisch!
In einigen Modulen fügen wir diesen Befehl jedoch einem Cloud Build-Trigger hinzu, sodass wir keine Fragen zulassen können. Wir müssen jede Option im Befehl ausfüllen. Sie möchten also die goldene gcloud run deploy --option1 blah --foo bar --region your-fav-region
erstellen. Wie gehst du es an?
- Wiederholen Sie die Schritte 2–3–4, bis gcloud keine Fragen mehr stellt:
- [LOOP]
gcloud run deploy
mit den bisher gefundenen Optionen - [LOOP] systems ask for option X
- [LOOP] In öffentlichen Dokumenten suchen, wie man X über die Befehlszeile einrichtet, und die Option
--my-option [my-value]
hinzufügen. - Kehren Sie jetzt zu Schritt 2 zurück, es sei denn, gcloud wird ohne weitere Fragen abgeschlossen.
- Dieser BLAH BLAH BLAH-Befehl von gcloud run deploy ist super! Speichern Sie den Befehl an einem Ort, da Sie ihn später für den Cloud Build-Schritt benötigen.
Eine mögliche Lösung finden Sie hier.
Super! 🎉🎉🎉 Sie haben Ihre App erfolgreich in Google Cloud bereitgestellt und damit den ersten Schritt zur Modernisierung abgeschlossen.
6. Modul 4: Passwort mit Secret Manager bereinigen
Im vorherigen Schritt konnten wir unsere App in Cloud Run bereitstellen und ausführen. Wir haben dabei jedoch eine schlechte Sicherheitspraxis angewendet: Wir haben einige Secrets im Klartext angegeben.
Erste Iteration: config.php für die Verwendung von ENV aktualisieren
Sie haben vielleicht bemerkt, dass wir das DB-Passwort direkt in den Code der Datei config.php eingefügt haben. Das ist in Ordnung, wenn Sie die App testen und prüfen möchten, ob sie funktioniert. In einer Produktionsumgebung können Sie jedoch keinen Code mithilfe eines Commits oder einer solchen Verwendung verwenden. Das Passwort (und andere Datenbankverbindungsparameter) sollten dynamisch gelesen und der App zur Laufzeit zur Verfügung gestellt werden. Ändern Sie die Datei config.php so, dass die DB-Parameter aus ENV-Variablen gelesen werden. Wenn das nicht funktioniert, sollten Sie Standardwerte festlegen. Das ist praktisch, falls das Laden von ENV fehlschlägt. In der Seitenausgabe sehen Sie dann, ob die Standardwerte verwendet werden. Ergänzen Sie die Lücken und ersetzen Sie den Code in config.php.
<?php
// Database configuration with ENV variables. Set default values as well
$db_host = getenv('DB_HOST') ?: _______;
$db_name = getenv('DB_NAME') ?: 'image_catalog';
$db_user = getenv('DB_USER') ?: 'appmod-phpapp-user';
$db_pass = getenv('DB_PASS') ?: _______;
// Note getenv() is PHP 5.3 compatible
try {
$pdo = new PDO("mysql:host=$db_host;dbname=$db_name", $db_user, $db_pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Errore di connessione: " . $e->getMessage());
}
session_start();
?>
Da Ihre App containerisiert ist, müssen Sie eine Möglichkeit bereitstellen, die ENV-Variablen der App zur Verfügung zu stellen. Dies kann auf verschiedene Arten erfolgen:
- Im Build im Dockerfile. Fügen Sie Ihrem vorherigen Dockerfile die vier Parameter mit der Syntax ENV DB_VAR=ENV_VAR_VALUE hinzu. Dadurch werden Standardwerte eingerichtet, die zur Laufzeit überschrieben werden können. Beispielsweise können "DB_NAME" und "DB_USER" nur hier festgelegt werden.
- Bei der Ausführung Sie können diese Variablen für Cloud Run einrichten, sowohl über die Befehlszeile als auch über die UI. Hier können Sie alle vier Variablen einfügen, es sei denn, Sie möchten die im Dockerfile festgelegten Standardwerte beibehalten.
Unter localhost können Sie Ihre ENV-Variablen in einer .env
-Datei speichern (siehe Ordner solutions).
Achten Sie außerdem darauf, dass die .env-Datei zu .gitignore
hinzugefügt wird, da Sie Ihre Secrets nicht an GitHub übertragen möchten.
echo .env >> .gitignore
Danach können Sie die Instanz lokal testen:
docker run -it -p 8080:8080 --env-file .env my-php-app-docker
Sie haben jetzt Folgendes erreicht:
- Die Variable wird dynamisch aus der ENV-Datei der App gelesen.
- Sie haben die Sicherheit verbessert, da Sie das DB-Passwort aus Ihrem Code entfernt haben.
Sie können jetzt eine neue Version in Cloud Run bereitstellen. Wechseln wir nun zur Benutzeroberfläche und legen die Umgebungsvariablen manuell fest:
- Rufen Sie https://console.cloud.google.com/run auf.
- Klicken Sie auf Ihre App.
- Klicken Sie auf „Neue Überarbeitung bearbeiten und bereitstellen“.
- Klicken Sie auf dem ersten Tab „Container“ auf den unteren Tab „Variablen und Secrets“.
- Klicken Sie auf „+ Variable hinzufügen“ und fügen Sie alle erforderlichen Variablen hinzu. Das Ergebnis sollte in etwa so aussehen:
Ist das perfekt? Nein. Ihr persönlicher Ausweis ist für die meisten Betreiber weiterhin sichtbar. Mit Google Cloud Secret Manager lässt sich dieses Risiko minimieren.
Zweite Iteration: Secret Manager
Deine Passwörter sind aus deinem Code verschwunden: Sieg! Aber sind wir schon sicher?
Ihre Passwörter sind weiterhin für alle sichtbar, die Zugriff auf die Google Cloud Console haben. Wenn Sie auf die Cloud Run-YAML-Bereitstellungsdatei zugreifen, können Sie sie abrufen. Wenn Sie versuchen, eine neue Cloud Run-Version zu bearbeiten oder bereitzustellen, wird das Passwort im Bereich „Variablen und Geheimnisse“ angezeigt, wie in den folgenden Screenshots zu sehen.
Secret Manager von Google Cloud ist ein sicherer, zentraler Dienst zum Verwalten vertraulicher Informationen wie API-Schlüssel, Passwörter, Zertifikate und andere Secrets.
Sie können damit Secrets mit präzisen Berechtigungen und robuster Verschlüsselung speichern, verwalten und darauf zugreifen. Durch die Einbindung in Identity and Access Management (IAM) von Google Cloud können Sie mit Secret Manager steuern, wer auf bestimmte Secrets zugreifen kann. So werden Datensicherheit und die Einhaltung gesetzlicher Vorschriften gewährleistet.
Außerdem unterstützt er die automatische Secret-Rotation und -Versionierung, wodurch die Verwaltung des Secret-Lebenszyklus vereinfacht und die Sicherheit in Anwendungen in allen Google Cloud-Diensten verbessert wird.
Um auf den Secret Manager zuzugreifen, klicken Sie im Dreistrich-Menü auf die Sicherheit und dann im Bereich Datenschutz auf den Secret Manager (siehe Abbildung unten).
Aktivieren Sie dort die Secret Manager API (siehe Abbildung unten).
- Klicken Sie nun auf Secret erstellen:
- Name:
php-amarcord-db-pass
- Secret-Wert: Ihr DB-Passwort (ignorieren Sie den Teil „upload file“).
- Anmerkung zu diesem geheimen Link, sollte so aussehen:
projects/123456789012/secrets/php-amarcord-db-pass
. Dies ist der eindeutige Zeiger auf Ihr Secret (für Terraform, Cloud Run und andere). Die Nummer ist Ihre eindeutige Projektnummer.
Tipp: Verwenden Sie einheitliche Namenskonventionen für Ihre Secrets, die von links nach rechts spezifiziert sind, z. B. cloud-devrel-phpamarcord-dbpass
.
- Organisation (mit dem Unternehmen)
- Team (innerhalb der Organisation)
- Bewerbung (innerhalb des Teams)
- Variablenname (in der App)
So können Sie mit einfachen regulären Ausdrücken alle Geheimnisse für eine einzelne App finden.
Neue Cloud Run-Version erstellen
Da wir jetzt ein neues Secret haben, müssen wir die ENV-Variable „DB_PASS“ entfernen und durch das neue Secret ersetzen. Das bedeutet:
- Über die Google Cloud Console auf Cloud Run zugreifen
- Wählen Sie die App aus.
- Klicken Sie auf „Neue Überarbeitung bearbeiten und bereitstellen“.
- Tab „Variablen und Secrets“ aus.
- Verwenden Sie die Schaltfläche "+ Secret-Referenz", um die ENV-Variable DB_PASS zurückzusetzen.
- Verwenden Sie denselben „DB_PASS“ für die referenzierten Secrets und verwenden Sie die neueste Version.
Danach sollte die folgende Fehlermeldung angezeigt werden:
Versuchen Sie, das Problem zu lösen. Um das Problem zu beheben, müssen Sie den Bereich IAM & Verwaltung aufrufen und die Berechtigungen ändern. Viel Spaß beim Debugging!
Wenn Sie das Problem gefunden haben, kehren Sie zu Cloud Run zurück und stellen Sie eine neue Version bereit. Das Ergebnis sollte so aussehen:
Tipp: Die Developer Console (UI) eignet sich hervorragend, um auf Berechtigungsprobleme hinzuweisen. Sehen Sie sich alle Links für Ihre Cloud-Entitäten an.
7. Modul 5: CI/CD mit Cloud Build einrichten
Warum eine CI/CD-Pipeline?
Inzwischen sollten Sie gcloud run deploy
schon ein paar Mal eingegeben haben und vielleicht immer wieder dieselbe Frage beantwortet haben.
Sie möchten Ihre App nicht mehr manuell mit „gcloud run deploy“ bereitstellen? Wäre es nicht toll, wenn Ihre App jedes Mal automatisch bereitgestellt werden könnte, wenn Sie eine neue Änderung in Ihr Git-Repository übertragen?
Für die Verwendung einer CI/CD-Pipeline sind zwei Dinge erforderlich:
- Ein privates Git-Repository: Glücklicherweise haben Sie in Schritt 2 bereits einen Fork des Workshop-Repositorys in Ihrem GitHub-Konto erstellt. Falls nicht, kehren Sie zurück und führen Sie diesen Schritt aus. Der Fork des Repositorys sollte so aussehen:
https://github.com/<YOUR_GITHUB_USER>/app-mod-workshop
- Cloud Build Mit diesem fantastischen und günstigen Dienst können Sie Build-Automatisierungen für so ziemlich alles konfigurieren: Terraform, dockerisierte Apps usw.
In diesem Abschnitt geht es hauptsächlich um die Einrichtung von Cloud Build.
Willkommen bei Cloud Build
Dazu verwenden wir Cloud Build:
- Quellcode mit Dockerfile erstellen Stellen Sie sich das als eine „große ZIP-Datei“ vor, die alles enthält, was Sie zum Erstellen und Ausführen benötigen (Ihr „Build-Artefakt“).
- Dieses Artefakt per Push in Artifact Registry (AR) übertragen.
- Führen Sie dann eine Bereitstellung von AR nach Cloud Run für die Anwendung „php-amarcord“ aus.
- Dadurch wird eine neue Version („Revision“) der vorhandenen App erstellt (denken Sie an eine Schicht mit dem neuen Code). Wir konfigurieren sie so, dass der Traffic bei erfolgreichem Push auf die neue Version umgeleitet wird.
Hier sind einige Builds für meine php-amarcord
-App:
Wie schaffen wir das alles?
- Eine perfekte YAML-Datei erstellen:
cloudbuild.yaml
- Erstellen Sie dazu einen Cloud Build-Trigger.
- Sie können über die Cloud Build-Benutzeroberfläche eine Verbindung zu unserem GitHub-Repository herstellen.
Trigger erstellen (und Repository verbinden)
- Rufen Sie https://console.cloud.google.com/cloud-build/triggers auf.
- Klicken Sie auf „Trigger erstellen“.
- Kompilieren:
- Name: Etwas Aussagekräftiges wie z. B. „
on-git-commit-build-php-app
“ - Ereignis: Push zum Branch
- Quelle: „Neues Repository verbinden“
- Daraufhin wird rechts das Fenster „Repository verbinden“ geöffnet.
- Quellanbieter: „Github“ (erster)
- „Weiter“
- Durch die Authentifizierung wird ein Fenster auf GitHub geöffnet, in dem eine Kreuzauthentifizierung durchgeführt wird. Folgen Sie der Anleitung und haben Sie etwas Geduld. Wenn Sie viele Repos haben, kann es eine Weile dauern.
- „Repository auswählen“: Wählen Sie Ihr Konto/Repository aus und klicken Sie das Kästchen „Ich habe verstanden…“ an.
- Wenn Sie die Fehlermeldung erhalten haben, dass die GitHub-App in keinem Ihrer Repositories installiert ist, klicken Sie auf „Google Cloud Build installieren“ und folgen Sie der Anleitung.
- Klicken Sie auf „Verbinden“.
- Bingo! Ihr Repository ist jetzt verbunden.
- Zurück zum Triggerteil…
- Konfiguration: Automatisch erkannt (*)
- Erweitert: Wählen Sie das Dienstkonto "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com" aus.
- xxxxx ist Ihre Projekt-ID.
- Das Standarddienstkonto von Compute ist für einen Lab-Ansatz in Ordnung, aber nicht für die Produktion geeignet. Weitere Informationen
- Lassen Sie alles andere unverändert.
- Klicken Sie auf die Schaltfläche „Erstellen“.
(*) Dies ist die einfachste Methode, da nach Dockerfile oder cloudbuild.yaml gesucht wird. Mit der cloudbuild.yaml
können Sie jedoch selbst entscheiden, was Sie in welchem Schritt tun möchten.
Ich habe die Power!
Der Trigger funktioniert jetzt nur, wenn Sie das Cloud Build-Dienstkonto angeben. Die E-Mail-Adresse eines „Roboters“, der in Ihrem Namen eine Aufgabe ausführt – in diesem Fall das Erstellen von Elementen in der Cloud.
Ihr Supportmitarbeiter kann den Build und die Bereitstellung nicht ausführen, es sei denn, Sie erteilen ihm die entsprechenden Berechtigungen. Zum Glück ist das ganz einfach.
- Gehen Sie zu „Cloud Build“ > Einstellungen.
- Dienstkonto "[PROJECT_NUMBER]- compute@developer.gserviceaccount.com"
- Klicken Sie diese Kästchen an:
- Cloud Run
- Secret Manager
- Dienstkonten
- Cloud Build
- Setzen Sie auch ein Häkchen bei „Als bevorzugtes Dienstkonto festlegen“.
Wo finde ich die Cloud Build-YAML-Datei?
Wir empfehlen Ihnen dringend, sich die Zeit zu nehmen, Ihre eigene Cloud Build-YAML-Datei zu erstellen.
Wenn Sie jedoch keine Zeit haben oder sich nicht die Zeit nehmen möchten, können Sie sich in diesem Lösungsordner inspirieren lassen: .solutions
Jetzt können Sie eine Änderung auf GitHub pushen und Cloud Build beobachten.
Die Einrichtung von Cloud Build kann kompliziert sein. Es kann etwas dauern, bis Sie eine Antwort erhalten:
- Logs unter https://console.cloud.google.com/cloud-build/builds;region=global prüfen
- Fehler finden
- Beheben Sie den Fehler im Code und führen Sie noch einmal „git commit“ und „git push“ aus.
- Manchmal liegt der Fehler nicht im Code, sondern in einer Konfiguration. In diesem Fall können Sie über die Benutzeroberfläche einen neuen Build ausführen (Cloud Build > „Trigger“ > „Ausführen“).
Wenn Sie diese Lösung verwenden, sind noch einige Aufgaben zu erledigen. Beispielsweise müssen Sie die ENV-Variablen für die neu erstellten Dev-/Prod-Endpunkte festlegen:
Dafür haben Sie die beiden folgenden Möglichkeiten:
- Über die Benutzeroberfläche: Legen Sie die ENV-Variablen noch einmal fest.
- Über die Befehlszeile, indem Sie das perfekte Script für sich erstellen. Ein Beispiel finden Sie hier: gcloud-run-deploy.sh. Sie müssen einige Dinge anpassen, z. B. den Endpunkt und die Projektnummer. Letztere finden Sie in der Cloud-Übersicht.
Wie kann ich Code auf GitHub committen?
Wie Sie git push
am besten auf GitHub hochladen, wird in diesem Workshop nicht behandelt. Wenn Sie in Cloud Shell nicht weiterkommen, haben Sie zwei Möglichkeiten:
- CLI. Fügen Sie lokal einen SSH-Schlüssel und ein Remote-Repository mit git@github.com:IHR_NUTZER/app-mod-workshop.git (anstelle von http) hinzu.
- VSCode Wenn Sie den Cloud Shell-Editor verwenden, können Sie den Tab „Versionskontrolle“ (Strg-Umschalt-G) aufrufen, auf „Änderungen synchronisieren“ klicken und der Anleitung folgen. Sie sollten in der Lage sein, Ihr GitHub-Konto in VSCode zu authentifizieren. Das Pullen und Pushen von dort aus ist dann ganz einfach.
Denken Sie daran, git add clodubuild.yaml
unter anderen Dateien zu speichern, da es sonst nicht funktioniert.
Tiefe und flache „Entwicklungs-/Produktionsparität“ [optional]
Wenn Sie die Modellversion von hier kopiert haben, haben Sie zwei identische DEV- und PROD-Versionen. Das ist cool und entspricht der 10. Regel der Twelve-Factor App.
Wir verwenden jedoch zwei verschiedene Webendpunkte, damit eine App auf dieselbe Datenbank verweist. Das ist für einen Workshop ausreichend. In der Praxis sollten Sie jedoch etwas Zeit darauf verwenden, eine richtige Produktionsumgebung zu erstellen. Das bedeutet, dass Sie zwei Datenbanken benötigen (eine für die Entwicklung und eine für die Produktion) und auch festlegen müssen, wo sie sich für die Notfallwiederherstellung und Hochverfügbarkeit befinden sollen. Dies geht über den Rahmen dieses Workshops hinaus, ist aber ein Denkanstoß.
Wenn Sie Zeit für eine „tiefe“ Produktionsversion haben, denken Sie an alle Ressourcen, die Sie duplizieren müssen, z. B.:
- Cloud SQL-Datenbank (und wahrscheinlich SQL-Instanz).
- GCS-Bucket
- Cloud Function.
- Sie könnten Gemini 1.5 Flash als Entwicklermodell (günstiger und schneller) und Gemini 1.5 Pro (leistungsstärker) verwenden.
Überlegen Sie sich jedes Mal, wenn Sie etwas an der App ändern, kritisch: Sollte dieser Wert auch in der Produktion gelten? Und wenn nicht, verdoppeln Sie Ihre Bemühungen. Mit Terraform ist das natürlich viel einfacher, da Sie die Umgebung (-dev, -prod) als Suffix in Ihre Ressourcen einfügen können.
8. Modul 6: Zu Google Cloud Storage wechseln
Speicher
Derzeit speichert die App den Status in einem Docker-Container. Wenn der Computer ausfällt, die App explodiert oder Sie einfach eine neue Version pushen, wird eine neue Version geplant und der Speicher zurückgesetzt (=> leer). 🙈
Wie können wir das Problem beheben? Dafür gibt es mehrere Ansätze.
- Bilder in der Datenbank speichern Das habe ich bei meiner vorherigen PHP-Anwendung getan. Das ist die einfachste Lösung, da sie nicht komplizierter wird. Aber es erhöht mit Sicherheit die Latenz und Belastung Ihrer Datenbank!
- Cloud Run-Anwendung zu einer speicherfreundlichen Lösung migrieren: GCE + nichtflüchtiger Speicher? Vielleicht GKE + Storage?
- Wechseln Sie zu GCS. Google Cloud Storage bietet erstklassigen Speicherplatz für die gesamte Google Cloud und ist die cloudtypischste Lösung. Dazu müssen wir uns jedoch mit PHP-Bibliotheken beschäftigen. Gibt es PHP 5.7-Bibliotheken für GCS? Unterstützt
PHP 5.7
überhauptComposer
? Anscheinend ist PHP 5.3.2 die älteste Version, die von Composer unterstützt wird. - Vielleicht ein Docker-Sidecar verwenden?
- Oder Sie verwenden Cloud Run-Volume-Bindungen in Google Cloud Storage. Das klingt fantastisch.
🤔 Speicher migrieren (offen)
[Offene Aufgabe] In dieser Übung sollen Sie eine Lösung finden, um Ihre Bilder so zu verschieben, dass sie auf irgendeine Weise erhalten bleiben.
Akzeptanztest
Ich möchte Ihnen nicht die Lösung verraten, aber ich möchte, dass Folgendes passiert:
- Du lädst
newpic.jpg
hoch. Du siehst es in der App. - Sie führen ein Upgrade der App auf eine neue Version durch.
newpic.jpg
ist noch da und sichtbar.
💡 Mögliche Lösung (GCS Cloud Run-Volume-Bindungen)
Dies ist eine sehr elegante Lösung, die es uns ermöglicht, zustandsorientierte Datei-Uploads zu erzielen, ohne den Code ALLES zu berühren (abgesehen von der Anzeige einer Bildbeschreibung, aber das ist trivial und nur zur Zufriedenheit der Augen).
Dadurch sollten Sie einen Ordner von Cloud Run in GCS bereitstellen können, also:
- Alle Uploads in GCS sind dann in Ihrer App sichtbar.
- Alle Uploads in Ihre App werden tatsächlich in GCS hochgeladen.
- Mit Objekten, die in GCS hochgeladen wurden, passiert etwas Magisches (Kapitel 7).
Hinweis: Bitte lies dir die FUSE-Haftungsausschlüsse durch. Das ist NICHT in Ordnung, wenn es ein Leistungsproblem gibt.
GCS-Bucket erstellen
GCS ist der allgegenwärtige Speicherdienst von Google Cloud. Er ist bewährt und wird von allen GCP-Diensten verwendet, die Speicherplatz benötigen.
Cloud Shell exportiert PROJECT_ID als GOOGLE_CLOUD_PROJECT:
$ export PROJECT_ID=$GOOGLE_CLOUD_PROJECT
#!/bin/bash
set -euo pipefail
# Your Cloud Run Service Name, eg php-amarcord-dev
SERVICE_NAME='php-amarcord-dev'
BUCKET="${PROJECT_ID}-public-images"
GS_BUCKET="gs://${BUCKET}"
# Create bucket
gsutil mb -l "$GCP_REGION" -p "$PROJECT_ID" "$GS_BUCKET/"
# Copy original pictures there - better if you add an image of YOURS before.
gsutil cp ./uploads/*.png "$GS_BUCKET/"
Cloud Run so konfigurieren, dass der Bucket im Ordner „/uploads/“ bereitgestellt wird
Jetzt kommen wir zum eleganten Teil. Wir erstellen ein Volume php_uploads
und weisen Cloud Run an, eine FUSE-Bereitstellung auf MOUNT_PATH
(z. B. /var/www/html/uploads/
) vorzunehmen:
#!/bin/bash
set -euo pipefail
# .. keep variables from previous script..
# Uploads folder within your docker container.
# Tweak it for your app code.
MOUNT_PATH='/var/www/html/uploads/'
# Inject a volume mount to your GCS bucket in the right folder.
gcloud --project "$PROJECT_ID" beta run services update "$SERVICE_NAME" \
--region $GCP_REGION \
--execution-environment gen2 \
--add-volume=name=php_uploads,type=cloud-storage,bucket="$BUCKET" \
--add-volume-mount=volume=php_uploads,mount-path="$MOUNT_PATH"
Wiederholen Sie diesen Schritt für alle Endpunkte, die auf Cloud Storage verweisen sollen.
Das geht auch über die Benutzeroberfläche.
- Erstellen Sie auf dem Tab „Volumes“ Volume-Bindungen vom Typ „Cloud Storage-Bucket“, die auf Ihren Bucket verweisen, z. B. mit dem Namen „php_uploads“.
- Unter Container > Volume-Bereitstellungen stellen Sie das gerade erstellte Volume am von Ihrer Anwendung angeforderten Volume-Punkt bereit. Das hängt vom Dockerfile ab, kann aber auch so aussehen:
var/www/html/uploads/
.
In beiden Fällen sollte beim Bearbeiten der neuen Cloud Run-Version in etwa Folgendes angezeigt werden:
Testen Sie jetzt die neue Anwendung, indem Sie ein neues Bild auf den /upload.php
-Endpunkt hochladen.
Die Bilder sollten nahtlos in GCS übertragen werden, ohne dass eine einzige PHP-Zeile geschrieben werden muss:
Was ist gerade passiert?
Etwas Magisches ist passiert.
Eine alte Anwendung mit altem Code wird noch ausgeführt. Mit einem neuen, modernisierten Stack können wir alle Bilder in unserer App bequem in einem zustandsorientierten Cloud-Bucket speichern. Jetzt sind Ihrer Kreativität keine Grenzen gesetzt:
- Möchten Sie jedes Mal eine E-Mail erhalten, wenn ein Bild mit dem Label „gefährlich“ oder „nackt“ erkannt wird? Sie können das tun, ohne den PHP-Code zu ändern.
- Möchten Sie jedes Mal ein multimodales Gemini-Modell verwenden, um es zu beschreiben, und die Datenbank mit der zugehörigen Beschreibung hochladen? Das können Sie tun, ohne den PHP-Code zu berühren. Sie glauben mir nicht? Lies weiter in Kapitel 7.
Wir haben hier gerade eine große Chance eröffnet.
9. Modul 7: App mit Google Gemini optimieren
Sie haben jetzt eine geniale, modernisierte, brandneue PHP-App (z. B. eine Fiat 126
von 2024) mit Cloudified-Speicher.
Was kann man damit machen?
Voraussetzungen
Im vorherigen Kapitel haben wir mit einer Modelllösung Bilder /uploads/
auf GCS bereitgestellt und so de facto die App-Logik vom Bildspeicher getrennt.
Für diese Übung benötigen Sie Folgendes:
- Übung in Kapitel 6 (Speicher) erfolgreich abgeschlossen
- Sie haben einen GCS-Bucket mit den Bilduploads, in dem Nutzer Bilder in Ihre App hochladen und die Bilder in Ihren Bucket übertragen werden.
Cloud Functions-Funktion einrichten (in Python)
Haben Sie sich schon einmal gefragt, wie Sie eine ereignisgesteuerte Anwendung implementieren? Etwa so:
- wenn <event> geschieht => E-Mail senden
- Wenn <Ereignis> eintritt => wenn <Bedingung> wahr ist, dann Datenbank aktualisieren.
Ein Ereignis kann alles sein, z. B. ein neuer Datensatz in BigQuery, ein neues Objekt, das in einem Ordner in GCS geändert wurde, oder eine neue Nachricht, die in einer Warteschlange in Pub/Sub wartet.
Google Cloud unterstützt mehrere Paradigmen, um dies zu erreichen. Insbesondere:
- EventArc Weitere Informationen zum Empfangen von GCS-Ereignissen Er eignet sich hervorragend zum Erstellen von DAGs und zum Orchestrating von Aktionen basierend auf Wenn-Dann-Bedingungen in der Cloud.
- Cloud Scheduler Das ist beispielsweise ideal für einen Cron-Job um Mitternacht in der Cloud.
- Cloud Workflows. Ähnlich wie bei Event Arc können Sie
- Cloud Run-Funktionen (auch
lambdas
genannt) - Cloud Composer: Die Google-Version von Apache Airflow, auch gut für DAGs geeignet.
In dieser Übung werden wir uns mit Cloud Functions befassen, um ein ziemlich spektakuläres Ergebnis zu erzielen. Außerdem bieten wir optionale Übungen an.
Beispielcode unter .solutions/
Cloud Functions-Funktion einrichten (🐍 Python)
Wir versuchen, ein sehr ehrgeiziges GCF zu schaffen.
- Wenn ein neues Image in GCS erstellt wird (wahrscheinlich, weil jemand es in der App hochgeladen hat, aber nicht nur)
- .. Gemini aufrufen, um eine Beschreibung des Bildes zu erhalten .. (Es wäre schön, den MIME-Typ zu prüfen und sicherzustellen, dass es sich um ein Bild und nicht um eine PDF-, MP3- oder Textdatei handelt.)
- … und aktualisieren Sie die Datenbank mit dieser Beschreibung. Möglicherweise muss die Datenbank korrigiert werden, um der
images
-Tabelle einedescription
-Spalte hinzuzufügen.
Datenbank mit Patch versehen, um description
zu Bildern hinzuzufügen
- Öffnen Sie Cloud SQL Studio:
- Nutzer und Passwort für die Image-Datenbank festlegen
- Injizieren Sie diesen SQL-Code, der eine Spalte für eine Bildbeschreibung hinzufügt:
ALTER TABLE images ADD COLUMN description TEXT;
Und Bingo! Prüfen Sie jetzt, ob es funktioniert hat:
SELECT * FROM images;
Sie sollten die neue Spalte „Beschreibung“ sehen:
Gemini-Funktion f(x) schreiben
Hinweis: Diese Funktion wurde mit Hilfe von Gemini Code Assist erstellt.
Hinweis: Beim Erstellen dieser Funktion können IAM-Berechtigungsfehler auftreten. Einige davon sind unten im Abschnitt „Mögliche Fehler“ aufgeführt.
- APIs aktivieren
- Rufen Sie https://console.cloud.google.com/functions/list auf.
- Klicken Sie auf „Funktion erstellen“.
- APIs über den API-Assistenten aktivieren:
Sie können den GCF entweder über die Benutzeroberfläche oder die Befehlszeile erstellen. Hier verwenden wir die Befehlszeile.
Einen möglichen Code finden Sie unter .solutions/
- Erstellen Sie einen Ordner zum Hosten Ihres Codes, z. B. „gcf/“. Öffnen Sie den Ordner.
requirements.txt
-Datei erstellen:
google-cloud-storage
google-cloud-aiplatform
pymysql
- Erstellen Sie eine Python-Funktion. Beispielcode hier: gcf/main.py.
#!/usr/bin/env python
"""Complete this"""
from google.cloud import storage
from google.cloud import aiplatform
import vertexai
from vertexai.generative_models import GenerativeModel, Part
import os
import pymysql
import pymysql.cursors
# Replace with your project ID
PROJECT_ID = "your-project-id"
GEMINI_MODEL = "gemini-1.5-pro-002"
DEFAULT_PROMPT = "Generate a caption for this image: "
def gemini_describe_image_from_gcs(gcs_url, image_prompt=DEFAULT_PROMPT):
pass
def update_db_with_description(image_filename, caption, db_user, db_pass, db_host, db_name):
pass
def generate_caption(event, context):
"""
Cloud Function triggered by a GCS event.
Args:
event (dict): The dictionary with data specific to this type of event.
context (google.cloud.functions.Context): The context parameter contains
event metadata such as event ID
and timestamp.
"""
pass
- Übertragen Sie die Funktion per Push. Sie können ein Skript wie dieses verwenden: gcf/push-to-gcf.sh.
Hinweis 1: Achten Sie darauf, die ENVs mit den richtigen Werten zu füllen oder fügen Sie sie einfach oben hinzu (GS_BUCKET=blah
, ..):
Hinweis 2: Dadurch wird der gesamte lokale Code (.
) gepusht. Achten Sie daher darauf, Ihren Code in einem bestimmten Ordner abzulegen und .gcloudignore
wie ein Profi zu verwenden, um das Pushen riesiger Bibliotheken zu vermeiden. ( Beispiel)
#!/bin/bash
set -euo pipefail
# add your logic here, for instance:
source .env || exit 2
echo "Pushing ☁️ f(x)☁ to 🪣 $GS_BUCKET, along with DB config.. (DB_PASS=$DB_PASS)"
gcloud --project "$PROJECT_ID" functions deploy php_amarcord_generate_caption \
--runtime python310 \
--region "$GCP_REGION" \
--trigger-event google.cloud.storage.object.v1.finalized \
--trigger-resource "$BUCKET" \
--set-env-vars "DB_HOST=$DB_HOST,DB_NAME=$DB_NAME,DB_PASS=$DB_PASS,DB_USER=$DB_USER" \
--source . \
--entry-point generate_caption \
--gen2
Hinweis: In diesem Beispiel ist generate_caption
die aufgerufene Methode und Cloud Functions übergibt das GCS-Ereignis mit allen relevanten Informationen (Bucket-Name, Objektname usw.). Nehmen Sie sich etwas Zeit, um Fehler in diesem Python-Wörterbuch zu beheben.
Funktion testen
Unit Tests
Die Funktion besteht aus vielen sich bewegenden Teilen. Es empfiehlt sich, alle einzelnen zu testen.
Ein Beispiel finden Sie in gcf/test.py.
Cloud Functions-Benutzeroberfläche
Nehmen Sie sich auch Zeit, um sich Ihre Funktion auf der Benutzeroberfläche anzusehen. Jeder Tab ist einen Blick wert, insbesondere Source
(mein Favorit), Variables
, Trigger
und Logs
. Auf dem Tab Logs
verbringen Sie viel Zeit, um Fehler zu beheben (siehe auch mögliche Fehler unten auf dieser Seite). Sehen Sie sich auch Permissions
an.
E2E-Test
Jetzt ist es an der Zeit, die Funktion manuell zu testen.
- Rufen Sie die App auf und melden Sie sich an.
- Laden Sie ein Bild hoch (nicht zu groß, es gibt Probleme mit großen Bildern)
- Prüfen Sie auf der Benutzeroberfläche, ob das Bild hochgeladen wurde.
- Prüfen Sie in Cloud SQL Studio, ob die Beschreibung aktualisiert wurde. Melden Sie sich an und führen Sie diese Abfrage aus:
SELECT * FROM images
.
Die Ergebnisse sprechen für sich: Außerdem sollten wir das Frontend aktualisieren, damit diese Beschreibung angezeigt wird.
PHP-Anzeige aktualisieren [optional]
Wir haben nachgewiesen, dass die App funktioniert. Es wäre jedoch schön, wenn die Nutzer diese Beschreibung auch sehen könnten.
Wir müssen keine PHP-Experten sein, um die Beschreibung der index.php
hinzuzufügen. Dieser Code sollte funktionieren (ja, Gemini hat ihn auch für mich geschrieben):
<?php if (!empty($image['description'])): ?>
<p class="font-bold">Gemini Caption:</p>
<p class="italic"><?php echo $image['description']; ?></p>
<?php endif; ?>
Platzieren Sie diesen Code nach Belieben innerhalb des foreach
.
In den nächsten Schritten sehen wir auch eine schönere Benutzeroberfläche, dank Gemini Code Assist. Eine ansprechende Version könnte so aussehen:
Fazit
Sie haben eine Cloud-Funktion, die bei neuen Objekten ausgelöst wird, die in GCS landen. Diese Funktion kann den Inhalt des Bildes wie ein Mensch annotieren und die Datenbank automatisch aktualisieren. Wow!
Nächste Schritte Mit derselben Begründung können Sie zwei nützliche Funktionen erzielen.
[optional] Weitere Cloud Functions hinzufügen [offen]
Mir fallen ein paar weitere Funktionen ein.
📩 E-Mail-Trigger
Ein E-Mail-Trigger, der Ihnen jedes Mal eine E-Mail sendet, wenn jemand ein Bild sendet.
- Zu oft? Fügen Sie eine weitere Einschränkung hinzu: Ein großes Bild oder ein Bild, dessen Gemini-Inhalte die Wörter „nackt/Nacktheit/gewalttätig“ enthalten.
- Weitere Informationen finden Sie unter
EventArc
.
🚫 Unangemessene Bilder automatisch moderieren
Derzeit kennzeichnen Administratoren Bilder als „unangemessen“. Wie wäre es, wenn Gemini die Arbeit übernimmt und den Gruppenbereich moderiert? Fügen Sie einen Test hinzu, um unangemessene Triggerinhalte zu melden und die Datenbank zu aktualisieren, wie wir in der vorherigen Funktion gelernt haben. Das bedeutet, dass wir die vorherige Funktion übernehmen, den Prompt ändern und die Datenbank basierend auf der Antwort aktualisieren.
Warnung: Die Ergebnisse generativer KI sind nicht vorhersehbar. Achten Sie darauf, dass die „Creative-Ausgabe“ von Gemini „auf Schienen“ steht. Sie können eine deterministische Antwort wie einen Konfidenzwert von 0 bis 1, eine JSON-Datei usw. geben, z. B.: * Python-Bibliotheken verwenden pydantic
, langchain
, ... * Verwenden Sie die strukturierte Gemini-Ausgabe.
Tipp: Sie können mehrere Funktionen oder einen einzelnen Prompt haben, der eine JSON-Antwort erzwingt (funktioniert gut mit „Gemini Structured Output“, wie oben hervorgehoben), z. B.:
Welchen Prompt würden Sie verwenden, um das zu generieren?
{
"description": "This is the picture of an arrosticino",
"suitable": TRUE
}
Sie können dem Prompt zusätzliche Felder hinzufügen, um Informationen wie „Gibt es etwas Gutes daran?“ zu erhalten. Ist das schlecht? Kennen Sie den Ort? Enthält das Bild Text? Die optische Zeichenerkennung war noch nie so einfach:
goods
: „Es sieht nach leckerem Essen aus.“bads
: „Sieht aus wie ungesundes Essen“OCR
: „Da consumare preferibilmente prima del 10 Novembre 2024“location
: „Pescara, Lungomare“
Normalerweise ist es besser, eine N-Funktion für N-Ergebnisse zu haben, aber es ist unglaublich befriedigend, eine Funktion zu erstellen, die zehn Dinge erledigt. In diesem Artikel von Riccardo erfährst du, wie das geht.
Mögliche Fehler (vor allem IAM-/Berechtigungsfehler)
Als ich diese Lösung entwickelt habe, stieß ich auf einige Probleme mit IAM-Berechtigungen. Ich werde sie hier aus Empathie zeigen und einige Ideen zu deren Behebung geben.
Fehler: Nicht genügend Berechtigungen für das Dienstkonto
- Wenn Sie eine GCF-Funktion bereitstellen, die auf einen GCS-Bucket hört, müssen Sie dem Dienstkonto, das Sie für den Job verwenden, die entsprechenden Berechtigungen zuweisen, wie in der Abbildung dargestellt:
Möglicherweise müssen Sie auch die EventArc APIs aktivieren. Es kann einige Minuten dauern, bis sie vollständig verfügbar sind.
Fehler: Fehlender Cloud Run-Invoker
- Ein weiterer Kommentar zur Benutzeroberfläche für die GCF-Berechtigungen lautet: Cloud Run-Aufruferrolle:
Dieser Fehler kann behoben werden, indem der Befehl im Image ausgeführt wird, das fix-permissions.sh ähnelt.
Dieses Problem wird hier beschrieben: https://cloud.google.com/functions/docs/securing/authenticating
Fehler: Speicherlimit überschritten
Beim ersten Ausführen stand in den Protokollen: „Speicherlimit von 244 MiB überschritten, 270 MiB belegt. Erhöhen Sie gegebenenfalls das Arbeitsspeicherlimit. Weitere Informationen finden Sie unter https://cloud.google.com/functions/docs/configuring/memory'". Fügen Sie Ihrem GCF wieder RAM hinzu. Das geht ganz einfach über die Benutzeroberfläche. Hier ist ein möglicher Anstieg:
Alternativ können Sie auch das Bereitstellungsskript für die Cloud Run-Bereitstellung korrigieren, um MEM/CPU zu verschieben. Das dauert etwas länger.
Fehler: PubSub-Nachricht veröffentlicht
Beim Erstellen eines Triggers mit GCF v1 gab es folgenden Fehler:
Auch dieses Problem lässt sich ganz einfach beheben, indem Sie IAM aufrufen und Ihrem Dienstkonto die Rolle "Pub/Sub-Publisher" zuweisen.
Fehler: Vertex AI wurde nicht verwendet
Wenn diese Fehlermeldung angezeigt wird:
Berechtigung verweigert: Die Vertex AI API 403 wurde noch nicht im Projekt „YOUR_PROJECT“ verwendet oder ist deaktiviert. Aktivieren Sie sie unter https://console.developers.google.com/apis/api/aiplatform.googleapis.com/overview?project=YOR_PROJECT.
Sie müssen nur die Vertex AI APIs aktivieren. So aktivieren Sie am einfachsten ALLE erforderlichen APIs:
- https://console.cloud.google.com/vertex-ai
- Klicken Sie auf „Alle empfohlenen APIs aktivieren“.
Fehler: EventArc-Trigger nicht gefunden
Stellen Sie die Funktion in diesem Fall noch einmal bereit.
Fehler: 400 Kundenservicemitarbeiter werden bereitgestellt
400 Dienst-Agents werden bereitgestellt ( https://cloud.google.com/vertex-ai/docs/general/access-control#service-agents). Dienst-Agents sind erforderlich, um die bereitgestellte Cloud Storage-Datei zu lesen. Bitte versuchen Sie es in ein paar Minuten noch einmal.
Warten Sie in diesem Fall einige Zeit oder wenden Sie sich an einen Google-Mitarbeiter.
10. Modul 8: Verfügbarkeits-SLOs erstellen
Im Kapitel versuchen wir, dies zu erreichen:
- SLIs erstellen
- SLOs anhand der SLIs erstellen
- Benachrichtigungen basierend auf SLOs erstellen
Dieses Thema liegt dem Autor sehr am Herzen, da Riccardo im Bereich SRE / DevOps von Google Cloud arbeitet.
(offen) SLIs und SLOs für diese App erstellen
Wie gut ist eine App, wenn Sie nicht erkennen können, wann sie ausgefallen ist?
Was ist ein SLO?
Meine Güte! Google hat SLOs erfunden. Um mehr darüber zu erfahren, kann ich Folgendes vorschlagen:
- SRE-Buch – Kapitel 2 – SLOs implementieren ( 👉 weitere SRE-Bücher)
- Art der SLOs ( tolles Video). In dieser Schulung erfahren Sie, wie Sie ein perfektes SLO für Ihren Dienst erstellen.
- SRE-Kurs auf Coursera Ich habe dazu beigetragen!
Schritt 1: SLI/SLO für die Verfügbarkeit erstellen
Beginnen wir mit dem Verfügbarkeits-SLA, da es das einfachste und möglicherweise wichtigste Messkriterium ist.
Glücklicherweise bietet Cloud Run dank Istio vordefinierte Unterstützung für SLOs.
Sobald Ihre Anwendung in Cloud Run läuft, ist das ganz einfach. Ich brauche nur 30 Sekunden.
- Rufen Sie Ihre Cloud Run-Seite auf.
- Klicken Sie auf Ihre App.
- Wählen Sie den Tab
SLOs
aus. - Klicken Sie auf „+ SLO erstellen“.
- Verfügbarkeit, anfragebasiert
- Weiter
- Kalendermonat / 99 %
- Klicken Sie auf „SLO erstellen“.
Schritt 2: Benachrichtigungen für diesen SLO einrichten
Ich empfehle, zwei Benachrichtigungen zu erstellen:
- Eine mit einer niedrigen Burnrate („Slowburn“), um Sie per E-Mail zu benachrichtigen (simuliert ein Ticket mit niedriger Priorität).
- Eine mit hoher Verbrennungsrate („Fastburn“), um Sie per SMS zu benachrichtigen (simuliert ein Ticket mit hoher Priorität / Pager)
Rufen Sie Ihr SLO tab
auf.
Führen Sie diesen Schritt zweimal aus:
- Klicken Sie auf „SLO-Benachrichtigung erstellen“ (die Schaltfläche 🔔 mit einem Pluszeichen rechts).
- Lookback-Dauer, Burn-Rate-Grenzwert:
- [FAST]. Erste:
60
Min./10
x - [LANGSAM]. Sekunde:
720
Minuten /2
x - Benachrichtigungskanal: Klicken Sie auf „Benachrichtigungskanäle verwalten“.
- Zuerst „E-Mail“ -> „Neu hinzufügen“ -> ..
- Zweitens: „SMS“ -> „Neu hinzufügen“ -> „Auf dem Smartphone bestätigen“.
- Tipp: Ich verwende gerne Emojis in den Namen. Demos macht Spaß.
- Klicken Sie zum Schluss rechts oben auf das große X.
- Wählen Sie zuerst das Telefon (schnell) und dann die E-Mail-Adresse (langsam) aus.
- Fügen Sie einige Beispieldokumente hinzu, z. B.:
[PHP Amarcord] Riccardo told me to type sudo reboot or to check documentation in http://example.com/playbooks/1.php but I guess he was joking
.
Bingo!
Endergebnis
Wir können diese Übung als abgeschlossen betrachten, sobald Sie 1 funktionierendes SLO + 2-fache Benachrichtigungen für Ihre Verfügbarkeit haben und eine Benachrichtigung an Ihre E-Mail-Adresse und Ihr Smartphone gesendet wurde.
Sie können eine Latenz hinzufügen (was ich Ihnen dringend empfehle) oder sogar eine komplexere. Wählen Sie für die Latenz einen Wert aus, der Ihrer Meinung nach angemessen ist. Im Zweifelsfall wählen Sie 200 ms aus.
11. Nächste Schritte
Sie haben ALLES erledigt. Was fehlt noch?
Ein kleiner Denkanstoß:
Mit Gemini spielen
Gemini gibt es in zwei Varianten:
- Vertex AI Der „Enterprise-Weg“, der eng mit Ihrer GCP verknüpft ist, den wir in Kapitel 7 (GCF + Gemini) behandelt haben. Die Authentifizierung funktioniert wie von Zauberhand und die Dienste sind perfekt miteinander verbunden.
- Google AI Der „Consumer Way“ Hier erhalten Sie einen Gemini API-Schlüssel und können kleine Skripts erstellen, die an Ihre bereits vorhandene Arbeitslast (z. B. proprietäre Arbeit, andere Clouds, localhost) gebunden werden können. Ersetzen Sie einfach Ihren API-Schlüssel und der Code funktioniert wie von Zauberhand.
Wir empfehlen Ihnen, (2) mit Ihren eigenen Hobbyprojekten zu erkunden.
UI-Optimierung
Ich bin nicht gut in UIs. Aber Gemini ist es! Sie können einfach eine einzelne PHP-Seite verwenden und so etwas sagen:
I have a VERY old PHP application. I want to touch it as little as possible. Can you help me:
1. add some nice CSS to it, a single static include for tailwind or similar, whatever you prefer
2. Transform the image print with description into cards, which fit 4 per line in the canvas?
Here's the code:
-----------------------------------
[Paste your PHP page, for instance index.php - mind the token limit!]
Das geht ganz einfach und dauert weniger als fünf Minuten. :)
Die Antwort von Gemini war perfekt (das heißt, ich musste nichts ändern):
Und hier ist das neue Layout in der persönlichen App des Autors:
Hinweis: Der Code wird als Bild eingefügt, da wir Sie nicht dazu anregen möchten, den Code zu verwenden, sondern Gemini bitten, den Code für Sie zu schreiben, unter Berücksichtigung Ihrer eigenen Creative-UI-/Frontend-Einschränkungen. Versprochen, Sie müssen danach nur noch sehr wenige Änderungen vornehmen.
Sicherheit
Die richtige Sicherung dieser App ist in diesem vierstündigen Workshop kein Ziel.
Einige Ideen finden Sie unter SECURITY
doc
.
12. Glückwunsch!
Herzlichen Glückwunsch! 🎉🎉🎉 Sie haben Ihre alte PHP-Anwendung mit Google Cloud erfolgreich modernisiert.
In diesem Codelab haben Sie Folgendes gelernt:
- Hier erfahren Sie, wie Sie eine MYSQL-Datenbank in Google Cloud SQL bereitstellen und eine vorhandene Datenbank dorthin migrieren.
- PHP-Anwendung mit Docker und Buildpacks containerisieren und das Image in der Google Cloud Artifact Registry speichern
- Containeranwendung in Cloud Run bereitstellen und mit Cloud SQL ausführen
- Vertrauliche Konfigurationsparameter (z. B. ein Datenbankpasswort) mit Google Secret Manager geheim speichern/verwenden
- Informationen zum Einrichten Ihrer CI/CD-Pipeline mit Google Cloud Build, um Ihre PHP-Anwendung bei jedem Codepush in Ihr GitHub-Repository automatisch zu erstellen und bereitzustellen.
- App-Ressourcen mit Cloud Storage in die Cloud verschieben
- Informationen dazu, wie Sie mit serverlosen Technologien in Google Cloud beeindruckende Workflows erstellen, ohne Ihren App-Code zu ändern.
- Verwenden Sie die multimodalen Funktionen von Gemini für einen passenden Anwendungsfall.
Dies ist ein guter Anfang auf Ihrem Weg zur Anwendungsmodernisierung mit Google Cloud.
🔁 Feedback
Wenn du uns deine Erfahrungen mit diesem Workshop mitteilen möchtest, kannst du das Feedback-Formular nutzen.
Wir freuen uns über Feedback und PRs für Code, auf den Sie besonders stolz sind.
🙏 Viele Grüße
Der Autor möchte sich bei Mirko Gilioli und Maurizio Ipsale von Datatonic für die Unterstützung beim Schreiben und Testen der Lösung bedanken.