1. Übersicht
In diesem Lab werden Funktionen und Möglichkeiten vorgestellt, die den Entwicklungsablauf für Softwareentwickler optimieren sollen, die Java-Anwendungen in einer Containerumgebung entwickeln. Für die typische Containerentwicklung muss der Nutzer Details zu Containern und zum Container-Build-Prozess kennen. Außerdem müssen Entwickler ihren Workflow in der Regel unterbrechen und ihre IDE verlassen, um ihre Anwendungen in Remote-Umgebungen zu testen und zu debuggen. Mit den in diesem Tutorial erwähnten Tools und Technologien können Entwickler effektiv mit containerisierten Anwendungen arbeiten, ohne ihre IDE zu verlassen.
Lerninhalte
In diesem Lab lernen Sie Methoden für die Entwicklung mit Containern in der GCP kennen, darunter:
- Einrichtung und Anforderungen
- Neue Java-Starteranwendung erstellen
- Entwicklungsprozess durchlaufen
- Einfachen CRUD-REST-Dienst entwickeln
- Bereinigen
2. Einrichtung und Anforderungen
Umgebung zum selbstbestimmten Lernen einrichten
- Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.



- Der Projektname ist der Anzeigename für die Teilnehmer dieses Projekts. Es handelt sich um einen String, der nicht von Google APIs verwendet wird und den Sie jederzeit aktualisieren können.
- Die Projekt-ID muss für alle Google Cloud-Projekte eindeutig sein und ist unveränderlich (kann nach der Festlegung nicht mehr geändert werden). In der Cloud Console wird automatisch ein eindeutiger String generiert. Normalerweise ist es nicht wichtig, wie dieser aussieht. In den meisten Codelabs müssen Sie auf die Projekt-ID verweisen (die in der Regel als
PROJECT_IDangegeben wird). Wenn Ihnen die ID nicht gefällt, können Sie eine andere zufällige ID generieren oder eine eigene ID ausprobieren und sehen, ob sie verfügbar ist. Nachdem das Projekt erstellt wurde, wird es „eingefroren“. - Es gibt einen dritten Wert, die Projektnummer, die von einigen APIs verwendet wird. Weitere Informationen zu diesen drei Werten
- Als Nächstes müssen Sie die Abrechnung in der Cloud Console aktivieren, um Cloud-Ressourcen/-APIs verwenden zu können. Die Durchführung dieses Codelabs sollte keine oder nur geringe Kosten verursachen. Wenn Sie Ressourcen herunterfahren möchten, damit nach Abschluss dieses Codelabs keine Gebühren anfallen, folgen Sie den Bereinigungsanweisungen am Ende des Codelabs. Neue Nutzer von Google Cloud kommen für das Programm für kostenlose Testversionen mit einem Guthaben von 300$ infrage.
Cloud Shell-Editor starten
Dieses Lab wurde für die Verwendung mit Google Cloud Shell Editor entwickelt und getestet. So greifen Sie auf den Editor zu:
- Rufen Sie Ihr Google-Projekt unter https://console.cloud.google.com auf.
- Klicken Sie oben rechts auf das Cloud Shell Editor-Symbol.

- Unten im Fenster wird ein neuer Bereich geöffnet.
- Klicken Sie auf die Schaltfläche „Editor öffnen“.

- Der Editor wird mit einem Explorer auf der rechten Seite und dem Editor im mittleren Bereich geöffnet.
- Unten auf dem Bildschirm sollte auch ein Terminalbereich verfügbar sein.
- Wenn das Terminal NICHT geöffnet ist, verwenden Sie die Tastenkombination „Strg+``“, um ein neues Terminalfenster zu öffnen.
gcloud einrichten
Legen Sie in Cloud Shell Ihre Projekt-ID und die Region fest, in der Sie Ihre Anwendung bereitstellen möchten. Speichern Sie diese als die Variablen PROJECT_ID und REGION ab.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Quellcode abrufen
Der Quellcode für dieses Lab befindet sich im Repository „container-developer-workshop“ in der Organisation „GoogleCloudPlatform“ auf GitHub. Klonen Sie das Repository mit dem folgenden Befehl und wechseln Sie dann in das Verzeichnis.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Die in diesem Lab verwendete Infrastruktur bereitstellen
In diesem Lab stellen Sie Code in GKE bereit und greifen auf Daten zu, die in einer Cloud SQL-Datenbank gespeichert sind. Das folgende Setupscript bereitet diese Infrastruktur für Sie vor. Der Bereitstellungsprozess dauert länger als 10 Minuten. Sie können mit den nächsten Schritten fortfahren, während die Einrichtung verarbeitet wird.
./setup.sh
3. Neue Java-Starteranwendung erstellen
In diesem Abschnitt erstellen Sie eine neue Java Spring Boot-Anwendung von Grund auf. Dazu verwenden Sie eine Beispielanwendung von spring.io.
Beispielanwendung klonen
- Startanwendung erstellen
curl https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=11 -d packageName=com.example.springboot -o sample-app.zip
- Anwendung entzippen
unzip sample-app.zip -d sample-app
- Wechseln Sie in das Beispielanwendungsverzeichnis und öffnen Sie den Ordner im Cloud Shell IDE-Arbeitsbereich.
cd sample-app && cloudshell workspace .
spring-boot-devtools und Jib hinzufügen
Um die Spring Boot DevTools zu aktivieren, suchen Sie im Explorer Ihres Editors nach der Datei „pom.xml“ und öffnen Sie sie. Fügen Sie den folgenden Code nach der Beschreibungszeile <description>Demo project for Spring Boot</description> ein.
- „spring-boot-devtools“ in „pom.xml“ hinzufügen
Öffnen Sie die Datei „pom.xml“ im Stammverzeichnis des Projekts. Fügen Sie die folgende Konfiguration nach dem Eintrag Description hinzu.
pom.xml
<!-- Spring profiles-->
<profiles>
<profile>
<id>sync</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
- jib-maven-plugin in pom.xml aktivieren
Jib ist ein Open-Source-Tool von Google zum Containerisieren von Java-Anwendungen, mit dem Java-Entwickler Container mit den Java-Tools erstellen können, die sie kennen. Jib ist ein schneller und einfacher Container-Image-Builder, der alle Schritte beim Packen Ihrer Anwendung in ein Container-Image übernimmt. Sie müssen kein Dockerfile schreiben und Docker muss nicht installiert sein. Außerdem ist das Tool direkt in Maven und Gradle integriert.
Scrollen Sie in der Datei „pom.xml“ nach unten und aktualisieren Sie den Abschnitt Build, sodass er das Jib-Plug-in enthält. Der Build-Abschnitt sollte nach Abschluss so aussehen.
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Jib Plugin-->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<!-- Maven Resources Plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
Wählen Sie Always aus, wenn Sie aufgefordert werden, die Build-Datei zu ändern.

Manifeste generieren
Skaffold bietet integrierte Tools, um die Containerentwicklung zu vereinfachen. In diesem Schritt initialisieren Sie Skaffold, wodurch automatisch grundlegende Kubernetes-YAML-Dateien erstellt werden. Dabei wird versucht, Verzeichnisse mit Container-Image-Definitionen wie Dockerfile zu identifizieren. Anschließend wird für jedes Verzeichnis ein Bereitstellungs- und Dienstmanifest erstellt.
Führen Sie den folgenden Befehl aus, um den Vorgang zu starten.
- Führen Sie im Terminal den folgenden Befehl aus.
skaffold init --generate-manifests
- Tun Sie Folgendes, wenn Sie dazu aufgefordert werden:
- Verwende die Pfeile, um den Cursor zu
Jib Maven Pluginzu bewegen. - Drücken Sie die Leertaste, um die Option auszuwählen.
- Zum Fortfahren die Eingabetaste drücken
- Geben Sie 8080 für den Port ein.
- Geben Sie y ein, um die Konfiguration zu speichern.
Der Arbeitsbereichsvisualisierung werden zwei Dateien hinzugefügt: skaffold.yaml und deployment.yaml.
App-Name aktualisieren
Die in der Konfiguration enthaltenen Standardwerte stimmen derzeit nicht mit dem Namen Ihrer Anwendung überein. Aktualisieren Sie die Dateien so, dass sie auf Ihren Anwendungsnamen und nicht auf die Standardwerte verweisen.
- Einträge in der Skaffold-Konfiguration ändern
skaffold.yamlöffnen- Wählen Sie den Bildnamen aus, der derzeit als
pom-xml-imagefestgelegt ist. - Klicken Sie mit der rechten Maustaste und wählen Sie „Alle Vorkommen ändern“ aus.
- Geben Sie den neuen Namen als
demo-appein.
- Einträge in der Kubernetes-Konfiguration ändern
deployment.yaml-Datei öffnen- Wählen Sie den Bildnamen aus, der derzeit als
pom-xml-imagefestgelegt ist. - Klicken Sie mit der rechten Maustaste und wählen Sie „Alle Vorkommen ändern“ aus.
- Geben Sie den neuen Namen als
demo-appein.
Hot-Sync aktivieren
Um ein optimiertes Hot-Reload-Erlebnis zu ermöglichen, verwenden Sie die von Jib bereitgestellte Synchronisierungsfunktion. In diesem Schritt konfigurieren Sie Skaffold so, dass diese Funktion im Build-Prozess verwendet wird.
Das Profil „sync“, das Sie in der Skaffold-Konfiguration konfigurieren, nutzt das Spring-Profil „sync“, das Sie im vorherigen Schritt konfiguriert haben, in dem Sie die Unterstützung für spring-dev-tools aktiviert haben.
- Skaffold-Konfiguration aktualisieren
Ersetzen Sie in der Datei „skaffold.yaml“ den gesamten Build-Abschnitt durch die folgende Spezifikation. Ändern Sie keine anderen Abschnitte der Datei.
skaffold.yaml
build:
artifacts:
- image: demo-app
jib:
project: com.example:demo
type: maven
args:
- --no-transfer-progress
- -Psync
fromImage: gcr.io/distroless/java:debug
sync:
auto: true
Standardroute hinzufügen
Erstellen Sie eine Datei mit dem Namen „HelloController.java“ unter „/src/main/java/com/example/springboot/“.
Fügen Sie den folgenden Inhalt in die Datei ein, um eine Standard-HTTP-Route zu erstellen.
HelloController.java
package com.example.springboot;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
@RestController
public class HelloController {
@Value("${target:local}")
String target;
@GetMapping("/")
public String hello()
{
return String.format("Hello from your %s environment!", target);
}
}
4. Entwicklungsprozess durchlaufen
In diesem Abschnitt führen Sie einige Schritte mit dem Cloud Code-Plug-in aus, um die grundlegenden Prozesse kennenzulernen und die Konfiguration und Einrichtung Ihrer Starteranwendung zu validieren.
Cloud Code lässt sich in Skaffold einbinden, um den Entwicklungsprozess zu optimieren. Wenn Sie Ihre Anwendung in den folgenden Schritten in GKE bereitstellen, erstellen Cloud Code und Skaffold automatisch Ihr Container-Image, übertragen es per Push in eine Container Registry und stellen Ihre Anwendung dann in GKE bereit. Dies geschieht im Hintergrund und die Details werden vom Entwicklerfluss abstrahiert. Cloud Code verbessert Ihren Entwicklungsprozess auch durch die Bereitstellung herkömmlicher Debugging- und Hotsync-Funktionen für die containerbasierte Entwicklung.
In Kubernetes bereitstellen
- Wählen Sie im Bereich unten im Cloud Shell-Editor Cloud Code  aus.

- Wählen Sie im Feld, das oben angezeigt wird, „Debug on Kubernetes“ aus. Wenn Sie dazu aufgefordert werden, wählen Sie „Ja“ aus, um den aktuellen Kubernetes-Kontext zu verwenden.

- Wenn Sie den Befehl zum ersten Mal ausführen, wird oben auf dem Bildschirm eine Aufforderung angezeigt, in der Sie gefragt werden, ob Sie den aktuellen Kubernetes-Kontext verwenden möchten. Wählen Sie „Ja“ aus, um den aktuellen Kontext zu akzeptieren und zu verwenden.

- Als Nächstes werden Sie gefragt, welche Container Registry Sie verwenden möchten. Drücken Sie die Eingabetaste, um den angegebenen Standardwert zu übernehmen.

- Wählen Sie im unteren Bereich den Tab „Ausgabe“ aus, um den Fortschritt und Benachrichtigungen zu sehen.

- Wählen Sie im Drop-down-Menü rechts „Kubernetes: Run/Debug – Detailed“ (Kubernetes: Ausführen/Debuggen – Detailliert) aus, um zusätzliche Details und Logs zu sehen, die live aus den Containern gestreamt werden.

- Kehren Sie zur vereinfachten Ansicht zurück, indem Sie im Drop-down-Menü „Kubernetes: Ausführen/Debuggen“ auswählen.
- Wenn der Build und die Tests abgeschlossen sind, wird auf dem Tab „Ausgabe“
Resource deployment/demo-app status completed successfullyangezeigt und es wird eine URL aufgeführt: „Weitergeleitete URL vom Dienst demo-app: http://localhost:8080“. - Bewegen Sie den Mauszeiger im Cloud Code-Terminal auf die URL in der Ausgabe (http://localhost:8080) und wählen Sie dann in der angezeigten Kurzinfo die Option „Webvorschau öffnen“ aus.
Die Antwort lautet:
Hello from your local environment!
Haltepunkte verwenden
- Öffnen Sie die Anwendung „HelloController.java“ unter /src/main/java/com/example/springboot/HelloController.java.
- Suchen Sie die Return-Anweisung für den Root-Pfad, die
return String.format("Hello from your %s environment!", target);lautet. - Fügen Sie der Zeile einen Haltepunkt hinzu, indem Sie auf den leeren Bereich links neben der Zeilennummer klicken. Eine rote Markierung zeigt an, dass der Haltepunkt festgelegt ist.
- Laden Sie den Browser neu. Der Debugger hält den Prozess am Haltepunkt an und ermöglicht es Ihnen, den Variablen- und Anwendungsstatus zu untersuchen, der remote in GKE ausgeführt wird.
- Klicken Sie im Bereich „Variablen“ nach unten, bis Sie die Variable „Ziel“ finden.
- Der aktuelle Wert ist „local“.
- Doppelklicken Sie auf den Variablennamen „target“ und ändern Sie den Wert im Pop-up-Fenster in einen anderen Wert, z. B. „Cloud“.
- Klicken Sie im Debugging-Steuerfeld auf die Schaltfläche „Weiter“.
- Sehen Sie sich die Antwort in Ihrem Browser an. Dort wird jetzt der aktualisierte Wert angezeigt, den Sie gerade eingegeben haben.
Hot Reload
- Ändern Sie die Anweisung so, dass ein anderer Wert zurückgegeben wird, z. B. „Hallo von %s Code“.
- Die Datei wird automatisch gespeichert und mit den Remote-Containern in GKE synchronisiert.
- Aktualisieren Sie Ihren Browser, um die aktualisierten Ergebnisse zu sehen.
- Beenden Sie die Debugging-Sitzung, indem Sie in der Debug-Symbolleiste auf das rote Quadrat
klicken.
5. Einfachen CRUD-REST-Dienst entwickeln
Ihre Anwendung ist jetzt vollständig für die containerisierte Entwicklung konfiguriert und Sie haben den grundlegenden Entwicklungs-Workflow mit Cloud Code durchlaufen. In den folgenden Abschnitten wenden Sie das Gelernte an, indem Sie REST-Dienstendpunkte hinzufügen, die eine Verbindung zu einer verwalteten Datenbank in Google Cloud herstellen.
Abhängigkeiten konfigurieren
Im Anwendungscode wird eine Datenbank verwendet, um die Daten des REST-Dienstes zu speichern. Achten Sie darauf, dass die Abhängigkeiten verfügbar sind, indem Sie Folgendes in die Datei „pom.xml“ einfügen:
- Öffnen Sie die Datei
pom.xmlund fügen Sie Folgendes in den Abschnitt „dependencies“ der Konfiguration ein:
pom.xml
<!-- Database dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
Rest-Dienst programmieren
Quote.java
Erstellen Sie eine Datei mit dem Namen „Quote.java“ in „/src/main/java/com/example/springboot/“ und kopieren Sie den folgenden Code hinein. Hiermit wird das Entitätsmodell für das in der Anwendung verwendete Angebotsobjekt definiert.
package com.example.springboot;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
Erstellen Sie eine Datei mit dem Namen „QuoteRepository.java“ unter „src/main/java/com/example/springboot“ und kopieren Sie den folgenden Code hinein.
package com.example.springboot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface QuoteRepository extends JpaRepository<Quote,Integer> {
@Query( nativeQuery = true, value =
"SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
Quote findRandomQuote();
}
In diesem Code wird JPA zum Speichern der Daten verwendet. Die Klasse erweitert die Spring-Schnittstelle JPARepository und ermöglicht die Erstellung von benutzerdefiniertem Code. Im Code haben Sie eine benutzerdefinierte findRandomQuote-Methode hinzugefügt.
QuoteController.java
Um den Endpunkt für den Dienst verfügbar zu machen, stellt eine QuoteController-Klasse diese Funktionalität bereit.
Erstellen Sie eine Datei mit dem Namen QuoteController.java unter src/main/java/com/example/springboot und kopieren Sie den folgenden Inhalt hinein.
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
try {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Datenbankkonfigurationen hinzufügen
application.yaml
Fügen Sie die Konfiguration für die Backend-Datenbank hinzu, auf die der Dienst zugreift. Bearbeiten Sie die Datei application.yaml unter src/main/resources (oder erstellen Sie sie, falls sie nicht vorhanden ist) und fügen Sie eine parametrisierte Spring-Konfiguration für das Backend hinzu.
target: local
spring:
config:
activate:
on-profile: cloud-dev
datasource:
url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
username: '${DB_USER:user}'
password: '${DB_PASS:password}'
jpa:
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
Datenbankmigration hinzufügen
Erstellen Sie einen Ordner unter „src/main/resources/db/migration/“.
SQL-Datei erstellen: V1__create_quotes_table.sql
Fügen Sie den folgenden Inhalt in die Datei ein.
V1__create_quotes_table.sql
CREATE TABLE quotes(
id INTEGER PRIMARY KEY,
quote VARCHAR(1024),
author VARCHAR(256)
);
INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');
Kubernetes-Konfiguration
Durch die folgenden Ergänzungen der Datei deployment.yaml kann die Anwendung eine Verbindung zu den Cloud SQL-Instanzen herstellen.
- TARGET: Konfiguriert die Variable, um die Umgebung anzugeben, in der die App ausgeführt wird.
- SPRING_PROFILES_ACTIVE: Zeigt das aktive Spring-Profil an, das für
cloud-devkonfiguriert wird. - DB_HOST: Die private IP-Adresse für die Datenbank, die beim Erstellen der Datenbankinstanz notiert wurde oder durch Klicken auf
SQLim Navigationsmenü der Google Cloud Console angezeigt wird. Bitte ändern Sie den Wert. - DB_USER und DB_PASS – wie in der CloudSQL-Instanzkonfiguration festgelegt, als Secret in GCP gespeichert
Aktualisieren Sie Ihre Datei „deployment.yaml“ mit dem folgenden Inhalt.
deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-app
labels:
app: demo-app
spec:
ports:
- port: 8080
protocol: TCP
clusterIP: None
selector:
app: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: demo-app
env:
- name: PORT
value: "8080"
- name: TARGET
value: "Local Dev - CloudSQL Database - K8s Cluster"
- name: SPRING_PROFILES_ACTIVE
value: cloud-dev
- name: DB_HOST
value: ${DB_INSTANCE_IP}
- name: DB_PORT
value: "5432"
- name: DB_USER
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: password
- name: DB_NAME
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: database
Ersetzen Sie den DB_HOST-Wert durch die Adresse Ihrer Datenbank.
export DB_INSTANCE_IP=$(gcloud sql instances describe quote-db-instance \
--format=json | jq \
--raw-output ".ipAddresses[].ipAddress")
envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml
Anwendung bereitstellen und validieren
- Wählen Sie im Bereich unten im Cloud Shell-Editor „Cloud Code“ und dann oben auf dem Bildschirm „Debug on Kubernetes“ aus.
- Wenn der Build und die Tests abgeschlossen sind, wird auf dem Tab „Ausgabe“
Resource deployment/demo-app status completed successfullyangezeigt und es wird eine URL aufgeführt: „Weitergeleitete URL vom Dienst demo-app: http://localhost:8080“. - Zufällige Zitate ansehen
Führen Sie den folgenden Befehl im Cloud Shell-Terminal mehrmals für den Endpunkt „random-quote“ aus. Wiederholte Aufrufe geben unterschiedliche Angebote zurück
curl -v 127.0.0.1:8080/random-quote
- Zitat hinzufügen
Erstellen Sie mit dem unten aufgeführten Befehl ein neues Angebot mit der ID 6 und beobachten Sie, wie die Anfrage zurückgegeben wird.
curl -v -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST 127.0.0.1:8080/quotes
- Angebot löschen
Löschen Sie nun das gerade hinzugefügte Zitat mit der Methode „delete“ und beobachten Sie den Antwortcode HTTP/1.1 204.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Serverfehler
Fehlerzustand durch erneutes Ausführen der letzten Anfrage, nachdem der Eintrag bereits gelöscht wurde
curl -v -X DELETE 127.0.0.1:8080/quotes/6
Beachten Sie, dass die Antwort eine HTTP:500 Internal Server Error zurückgibt.
Anwendung debuggen
Im vorherigen Abschnitt haben Sie einen Fehlerzustand in der Anwendung gefunden, als Sie versucht haben, einen Eintrag zu löschen, der nicht in der Datenbank vorhanden war. In diesem Abschnitt setzen Sie einen Haltepunkt, um das Problem zu finden. Der Fehler ist beim DELETE-Vorgang aufgetreten. Sie arbeiten also mit der QuoteController-Klasse.
- Öffnen Sie src.main.java.com.example.springboot.QuoteController.java.
deleteQuote()-Methode finden- Suchen Sie die Zeile, in der ein Element aus der Datenbank gelöscht wird:
quoteRepository.deleteById(id); - Setzen Sie einen Haltepunkt in dieser Zeile, indem Sie auf den leeren Bereich links neben der Zeilennummer klicken.
- Ein roter Indikator wird angezeigt, der darauf hinweist, dass der Haltepunkt festgelegt ist.
- Führen Sie den Befehl
deletenoch einmal aus.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Klicken Sie auf das Symbol in der linken Spalte, um zur Debug-Ansicht zurückzukehren.
- Beobachten Sie, dass die Debugzeile in der Klasse „QuoteController“ angehalten wurde.
- Klicken Sie im Debugger auf das Symbol
step over
. Es wird eine Ausnahme ausgelöst. - Beachten Sie, dass ein sehr allgemeiner
RuntimeException was caught.einen HTTP-Fehler 500 (Interner Serverfehler) an den Client zurückgibt, was nicht ideal ist.
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 500 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
Code aktualisieren
Der Code ist falsch und der Ausnahmeblock sollte so umgestaltet werden, dass die EmptyResultDataAccessException-Ausnahme abgefangen und ein HTTP-Statuscode 404 (nicht gefunden) zurückgegeben wird.
Korrigieren Sie den Fehler.
- Während die Debugging-Sitzung noch läuft, schließen Sie die Anfrage ab, indem Sie in der Debugging-Steuerung auf die Schaltfläche „Weiter“ klicken.
- Fügen Sie als Nächstes den folgenden Block in den Code ein:
} catch (EmptyResultDataAccessException e){
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Die Methode sollte so aussehen:
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
try {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch(EmptyResultDataAccessException e){
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
- Löschbefehl noch einmal ausführen
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Gehen Sie den Debugger durch und beobachten Sie, wie die
EmptyResultDataAccessExceptionabgefangen und der HTTP-Fehler 404 „Not Found“ an den Aufrufer zurückgegeben wird.
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 404 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
- Beenden Sie die Debugging-Sitzung, indem Sie in der Debug-Symbolleiste auf das rote Quadrat
klicken.
6. Bereinigen
Glückwunsch! In diesem Lab haben Sie eine neue Java-Anwendung von Grund auf erstellt und so konfiguriert, dass sie effektiv mit Containern zusammenarbeitet. Anschließend haben Sie Ihre Anwendung in einem Remote-GKE-Cluster bereitgestellt und dort Fehler behoben. Dabei haben Sie denselben Entwickler-Workflow verwendet, der auch bei herkömmlichen Anwendungsstacks zum Einsatz kommt.
So bereinigen Sie die Umgebung nach Abschluss des Labs:
- Im Lab verwendete Dateien löschen
cd ~ && rm -rf container-developer-workshop
- Projekt löschen, um alle zugehörigen Infrastrukturen und Ressourcen zu entfernen