Mit Cloud Workstations und Cloud Code entwickeln

1. Übersicht

In diesem Lab werden Funktionen und Fähigkeiten vorgestellt, mit denen der Entwicklungsworkflow für Softwareentwickler optimiert werden kann, die mit der Entwicklung von Java-Anwendungen in einer Containerumgebung betraut sind. Bei der typischen Containerentwicklung muss der Nutzer die Details von Containern und den Container-Build-Prozess genau kennen. Außerdem müssen Entwickler in der Regel den Ablauf unterbrechen, um ihre Anwendungen in Remote-Umgebungen zu testen und zu debuggen. Mit den in dieser Anleitung erwähnten Tools und Technologien können Entwickler effektiv mit Containeranwendungen arbeiten, ohne ihre IDE zu verlassen.

Lerninhalte

In diesem Lab lernen Sie Methoden für die Entwicklung mit Containern in GCP kennen, darunter:

  • InnerLoop-Entwicklung mit Cloud Workstations
  • Neue Java-Startanwendung erstellen
  • Der Entwicklungsprozess
  • Entwickeln eines einfachen CRUD Rest-Dienstes
  • Anwendung im GKE-Cluster debuggen
  • Anwendung mit Cloud SQL-Datenbank verbinden

58a4cdd3ed7a123a.png

2. Einrichtung und Anforderungen

Umgebung zum selbstbestimmten Lernen einrichten

  1. Melden Sie sich in der Google Cloud Console an und erstellen Sie ein neues Projekt oder verwenden Sie ein vorhandenes Projekt. Wenn Sie noch kein Gmail- oder Google Workspace-Konto haben, müssen Sie eines erstellen.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • Der Projektname ist der Anzeigename für die Projektteilnehmer. Es handelt sich um eine Zeichenfolge, die von Google APIs nicht verwendet wird. Sie können sie jederzeit aktualisieren.
  • Die Projekt-ID ist für alle Google Cloud-Projekte eindeutig und unveränderlich. Sie kann nach dem Festlegen nicht mehr geändert werden. Die Cloud Console generiert automatisch einen eindeutigen String. ist Ihnen meist egal, was es ist. In den meisten Codelabs musst du dich auf die Projekt-ID beziehen, die üblicherweise als PROJECT_ID gekennzeichnet ist. Wenn Ihnen die generierte ID nicht gefällt, können Sie eine weitere zufällige ID erstellen. Alternativ können Sie einen eigenen verwenden und nachsehen, ob er verfügbar ist. Sie kann nach diesem Schritt nicht mehr geändert werden und bleibt für die Dauer des Projekts bestehen.
  • Zur Information gibt es noch einen dritten Wert, die Projektnummer, die von manchen APIs verwendet wird. Weitere Informationen zu allen drei Werten finden Sie in der Dokumentation.
  1. Als Nächstes müssen Sie in der Cloud Console die Abrechnung aktivieren, um Cloud-Ressourcen/APIs verwenden zu können. Dieses Codelab sollte ohne großen Aufwand betrieben werden. Wenn Sie Ressourcen herunterfahren möchten, um über diese Anleitung hinaus keine Kosten zu verursachen, können Sie die von Ihnen erstellten Ressourcen oder das gesamte Projekt löschen. Neue Google Cloud-Nutzer haben Anspruch auf eine kostenlose Testversion von 300$.

Cloud Shell-Editor starten

Dieses Lab wurde für die Verwendung mit dem Google Cloud Shell-Editor entwickelt und getestet. So greifen Sie auf den Editor zu:

  1. Rufen Sie Ihr Google-Projekt unter https://console.cloud.google.com auf.
  2. Klicken Sie rechts oben auf das Symbol für den Cloud Shell-Editor

8560cc8d45e8c112.png

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

9e504cb98a6a8005.png

  1. Der Editor wird mit einem Explorer auf der rechten Seite und dem Editor im mittleren Bereich geöffnet.
  2. Unten auf dem Bildschirm sollte außerdem ein Terminalbereich verfügbar sein.
  3. Wenn das Terminal NICHT geöffnet ist, verwende 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 die Anwendung bereitstellen möchten. Speichern Sie sie als Variablen des Typs PROJECT_ID und REGION.

export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')

Quellcode klonen

Den Quellcode für dieses Lab finden Sie auf GitHub im Container-Entwicklerworkshop der Google Cloud Platform. Klonen Sie es 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

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. Mit dem Einrichtungsskript unten wird diese Infrastruktur für Sie vorbereitet. Die Bereitstellung dauert über 25 Minuten. Warten Sie, bis das Skript abgeschlossen ist, bevor Sie mit dem nächsten Abschnitt fortfahren.

./setup_with_cw.sh &

Cloud Workstations-Cluster

Öffnen Sie Cloud Workstations in der Cloud Console. Warten Sie, bis der Cluster den Status READY hat.

305e1a3d63ac7ff6.png

Workstationkonfiguration erstellen

Wenn die Verbindung zu Ihrer Cloud Shell-Sitzung getrennt wurde, klicken Sie auf „Wieder verbinden“. und führen Sie dann den Befehl „gcloud cli“ aus, um die Projekt-ID festzulegen. Ersetzen Sie die Beispiel-Projekt-ID unten durch Ihre Qwiklabs-Projekt-ID, bevor Sie den Befehl ausführen.

gcloud config set project qwiklabs-gcp-project-id

Führen Sie das folgende Skript im Terminal aus, um die Cloud Workstations-Konfiguration zu erstellen.

cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh

Überprüfen Sie die Ergebnisse im Abschnitt „Konfigurationen“. Es dauert zwei Minuten, bis der Status BEREIT aktiviert ist.

7a6af5aa2807a5f2.png

Öffnen Sie Cloud Workstations in der Console und erstellen Sie eine neue Instanz.

a53adeeac81a78c8.png

Ändern Sie den Namen in my-workstation und wählen Sie die vorhandene Konfiguration aus: codeoss-java.

f21c216997746097.png

Prüfen Sie die Ergebnisse im Abschnitt „Workstations“.

66a9fc8b20543e32.png

Workstation starten

Starten und starten Sie die Workstation.

c91bb69b61ec8635.png

Du kannst Cookies von Drittanbietern zulassen, indem du auf das Symbol in der Adressleiste klickst. 1b8923e2943f9bc4.png

fcf9405b6957b7d7.png

Klicken Sie auf „Website funktioniert nicht?“.

36a84c0e2e3b85b.png

Klicke auf „Cookies zulassen“.

2259694328628fba.png

Sobald die Workstation gestartet wurde, wird Code OSS IDE angezeigt. Klicken Sie auf „Als erledigt markieren“. auf der Seite „Erste Schritte“.

94874fba9b74cc22.png

3. Neue Java-Startanwendung erstellen

In diesem Abschnitt erstellen Sie mithilfe einer von spring.io bereitgestellten Beispielanwendung von Grund auf eine Java Spring Boot-Anwendung. Öffnen Sie ein neues Terminal.

c31d48f2e4938c38.png

Beispielanwendung klonen

  1. Startanwendung erstellen
curl  https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=17 -d packageName=com.example.springboot -o sample-app.zip

Wenn diese Meldung angezeigt wird, klicken Sie auf die Schaltfläche „Zulassen“, damit Sie das Einfügen auf die Workstation kopieren können.

58149777e5cc350a.png

  1. Anwendung entpacken
unzip sample-app.zip -d sample-app
  1. Öffnen Sie die Beispiel-App. Ordner
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

Spring-Boot-Entwicklertools hinzufügen und Jib

Um die Spring Boot-Entwicklertools zu aktivieren, suchen und öffnen Sie die Datei pom.xml im Explorer in Ihrem Editor. Fügen Sie als Nächstes den folgenden Code nach der Textzeile ein, die <description>Demo project for Spring Boot</description> lautet.

  1. Spring-boot-devtools in pom.xml hinzufügen

Öffnen Sie pom.xml im Stammverzeichnis des Projekts. Fügen Sie nach dem Eintrag Description die folgende Konfiguration 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>
  1. jib-maven-plugin in pom.xml aktivieren

Jib ist ein Open-Source-Tool zur Java-Containerisierung von Google, mit dem Java-Entwickler Container mit den ihnen bekannten Java-Tools erstellen können. Jib ist ein schneller und einfacher Container-Image-Builder, der alle Schritte zum Verpacken Ihrer Anwendung in ein Container-Image übernimmt. Sie müssen kein Dockerfile schreiben und Docker ist auch nicht direkt in Maven und Gradle eingebunden.

Scrollen Sie in der Datei pom.xml nach unten und aktualisieren Sie den Abschnitt Build, um das Jib-Plug-in einzubinden. Der Build-Abschnitt sollte nach Fertigstellung wie folgt 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>

Manifeste generieren

Skaffold bietet integrierte Tools zur Vereinfachung der Containerentwicklung. In diesem Schritt initialisieren Sie Skaffold, wodurch automatisch grundlegende Kubernetes-YAML-Dateien erstellt werden. Der Prozess versucht, Verzeichnisse mit Container-Image-Definitionen wie ein Dockerfile zu ermitteln, und erstellt dann für jedes Verzeichnis ein Deployment und ein Dienstmanifest.

Führen Sie den folgenden Befehl im Terminal aus, um den Prozess zu starten.

d869e0cd38e983d7.png

  1. Führen Sie den folgenden Befehl im Terminal aus
skaffold init --generate-manifests
  1. Tun Sie Folgendes, wenn Sie dazu aufgefordert werden:
  • Bewegen Sie den Cursor mit den Pfeilen auf Jib Maven Plugin
  • Drücken Sie die Leertaste, um die Option auszuwählen.
  • Drücken Sie die Eingabetaste, um fortzufahren
  1. Geben Sie 8080 als Port ein.
  2. Geben Sie y ein, um die Konfiguration zu speichern

Zwei Dateien werden dem Arbeitsbereich „skaffold.yaml“ und „deployment.yaml“ hinzugefügt

Skaffold-Ausgabe:

b33cc1e0c2077ab8.png

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 den Namen Ihrer Anwendung anstatt auf die Standardwerte verweisen.

  1. Einträge in der Skaffold-Konfiguration ändern
  • skaffold.yaml“ öffnen
  • Wählen Sie den Image-Namen aus, der derzeit als pom-xml-image festgelegt ist
  • Klicken Sie mit der rechten Maustaste und wählen Sie „Alle Vorkommen ändern“ aus.
  • Geben Sie den neuen Namen als demo-app ein.
  1. Einträge in der Kubernetes-Konfiguration ändern
  • Datei „deployment.yaml“ öffnen
  • Wählen Sie den Image-Namen aus, der derzeit als pom-xml-image festgelegt ist
  • Klicken Sie mit der rechten Maustaste und wählen Sie „Alle Vorkommen ändern“ aus.
  • Geben Sie den neuen Namen als demo-app ein.

Automatischen Synchronisierungsmodus aktivieren

Um die Funktion „Hot Refresh“ zu optimieren, verwenden Sie die Synchronisierungsfunktion von Jib. In diesem Schritt konfigurieren Sie Skaffold, um dieses Feature im Build-Prozess zu verwenden.

Beachten Sie, dass das Symbol „Sync“ Profil, das Sie in der Skaffold-Konfiguration konfigurieren, nutzt die Spring-Synchronisierung. Profil, das Sie im vorherigen Schritt konfiguriert haben, in dem Sie die Unterstützung für spring-dev-tools aktiviert haben.

  1. Skaffold-Konfiguration aktualisieren

Ersetzen Sie in der Datei skaffold.yaml den gesamten Build-Abschnitt der Datei 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/java17-debian11:debug
    sync:
      auto: true

Standardroute hinzufügen

Erstellen Sie im Ordner „/src/main/java/com/example/springboot/“ eine Datei mit dem Namen „HelloController.java“.

a624f5dd0c477c09.png

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. Der Entwicklungsprozess

In diesem Abschnitt führen Sie einige Schritte mit dem Cloud Code-Plug-in durch, um die grundlegenden Prozesse kennenzulernen und die Konfiguration und Einrichtung Ihrer Startanwendung zu validieren.

Cloud Code lässt sich in Skaffold einbinden, um den Entwicklungsprozess zu optimieren. Wenn Sie in den folgenden Schritten ein Deployment in GKE ausführen, erstellen Cloud Code und Skaffold automatisch das Container-Image, übertragen es per Push in eine Container Registry und stellen die Anwendung dann in GKE bereit. Das geschieht im Hintergrund und abstrahiert die Details außerhalb des Entwicklungsablaufs. Cloud Code verbessert außerdem Ihren Entwicklungsprozess, indem es herkömmliche Debugging- und Hotsync-Funktionen für die containerbasierte Entwicklung bereitstellt.

In Google Cloud anmelden

Klicken Sie auf das Cloud Code-Symbol und wählen Sie „In Google Cloud anmelden“ aus:

1769afd39be372ff.png

Klicken Sie auf „Zur Anmeldung fortfahren“.

923bb1c8f63160f9.png

Prüfen Sie die Ausgabe im Terminal und öffnen Sie den Link:

517fdd579c34aa21.png

Melden Sie sich mit den Anmeldedaten Ihrer Qwiklabs-Teilnehmer an.

db99b345f7a8e72c.png

Wählen Sie „Zulassen“ aus:

a5376553c430ac84.png

Kopieren Sie den Bestätigungscode und kehren Sie zum Tab „Workstation“ zurück.

6719421277b92eac.png

Füge den Bestätigungscode ein und drücke die Eingabetaste.

e9847cfe3fa8a2ce.png

Kubernetes-Cluster hinzufügen

  1. Cluster hinzufügen

62a3b97bdbb427e5.png

  1. Wählen Sie Google Kubernetes Engine aus:

9577de423568bbaa.png

  1. Projekt auswählen.

c5202fcbeebcd41c.png

  1. Wählen Sie „quote-cluster“ aus. die bei der Ersteinrichtung erstellt wurde.

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

Aktuelle Projekt-ID mit gcloud cli festlegen

Kopieren Sie die Projekt-ID für dieses Lab von der Qwiklabs-Seite.

fcff2d10007ec5bc.png

Führen Sie den Befehl „gcloud cli“ aus, um die Projekt-ID festzulegen. Ersetzen Sie die Beispielprojekt-ID, bevor Sie den Befehl ausführen.

gcloud config set project qwiklabs-gcp-project-id

Beispielausgabe:

f1c03d01b7ac112c.png

Fehlerbehebung in Kubernetes

  1. Wählen Sie unten im linken Bereich Cloud Code aus.

60b8e4e95868b561.png

  1. Wählen Sie im Bereich, der unter DEVELOPMENT SESSIONS (Entwicklungssitzungen) angezeigt wird, die Option „Debug on Kubernetes“ aus.

Scrollen Sie nach unten, falls die Option nicht angezeigt wird.

7d30833d96632ca0.png

  1. Wähle „Ja“ aus. aktuellen Kontext zu verwenden.

a024a69b64de7e9e.png

  1. Wählen Sie „quote-cluster“ aus. die bei der Ersteinrichtung erstellt wurde.

faebabf372e3caf0.png

  1. Wählen Sie das Container-Repository aus.

fabc6dce48bae1b4.png

  1. Wähle im unteren Bereich den Tab „Output“ (Ausgabe) aus, um den Fortschritt und die Benachrichtigungen anzusehen
  2. Wählen Sie „Kubernetes: Run/Debug – Detail“ aus. im Drop-down-Menü „Kanal“ rechts, um zusätzliche Details und Logs aufzurufen, die live aus den Containern gestreamt werden

86b44c59db58f8f3.png

Warten Sie, bis die Anwendung bereitgestellt wurde.

9f37706a752829fe.png

  1. Prüfen Sie die in GKE bereitgestellte Anwendung in der Cloud Console.

6ad220e5d1980756.png

  1. Kehren Sie zur vereinfachten Ansicht zurück, indem Sie „Kubernetes: Run/Debug“ auswählen. Dropdown-Menü auf dem Tab AUSGABE aus.
  2. Wenn der Build und die Tests abgeschlossen sind, wird auf dem Tab „Ausgabe“ Resource deployment/demo-app status completed successfully angezeigt und die URL wird aufgeführt: „Forwarded URL from service demo-app: http://localhost:8080“
  3. Bewegen Sie den Mauszeiger im Cloud Code-Terminal auf die URL in der Ausgabe (http://localhost:8080) und wählen Sie in der angezeigten Kurzinfo „Link folgen“ aus.

28c5539880194a8e.png

Ein neuer Tab wird geöffnet und die Ausgabe sieht so aus:

d67253ca16238f49.png

Haltepunkte verwenden

  1. Öffnen Sie die HelloController.java App unter /src/main/java/com/example/springboot/HelloController.java.
  2. Suchen Sie die Rückgabeanweisung für den Stammpfad, der return String.format("Hello from your %s environment!", target); lautet.
  3. Fügen Sie dieser Zeile einen Haltepunkt hinzu, indem Sie auf den leeren Bereich links neben der Zeilennummer klicken. Ein roter Indikator wird angezeigt, um darauf hinzuweisen, dass der Haltepunkt eingerichtet ist.

5027dc6da2618a39.png

  1. Laden Sie Ihren Browser neu. Der Debugger stoppt den Prozess am Haltepunkt und ermöglicht es Ihnen, die Variablen und den Status der Anwendung zu untersuchen, die remote in GKE ausgeführt wird.

71acfb426623cec2.png

  1. Klicken Sie im Bereich „Variablen“ so lange auf „Ziel“, .
  2. Aktuellen Wert als „lokal“ beobachten

a1160d2ed2bb5c82.png

  1. Doppelklicken Sie auf den Variablennamen „target“. und im Pop-up

Ändern Sie den Wert in „Cloud Workstations“

e597a556a5c53f32.png

  1. Klicken Sie im Steuerfeld zur Fehlerbehebung auf die Schaltfläche Weiter .

ec17086191770d0d.png

  1. Sehen Sie sich die Antwort in Ihrem Browser an. Sie enthält jetzt den aktualisierten Wert, den Sie gerade eingegeben haben.

6698a9db9e729925.png

  1. Entfernen Sie den Breakpoint, indem Sie auf den roten Indikator links neben der Zeilennummer klicken. Dadurch wird verhindert, dass die Ausführung des Codes in dieser Zeile gestoppt wird, während Sie in diesem Lab fortfahren.

Heiße Aufladung

  1. Ändern Sie die Anweisung, sodass ein anderer Wert zurückgegeben wird, z. B. "Hello from %s Code".
  2. Die Datei wird automatisch gespeichert und mit den Remote-Containern in GKE synchronisiert
  3. Aktualisieren Sie Ihren Browser, um die aktualisierten Ergebnisse zu sehen.
  4. Beenden Sie die Debugging-Sitzung, indem Sie auf das rote Quadrat in der Debugging-Symbolleiste klicken.

a541f928ec8f430e.png c2752bb28d82af86.png

Wählen Sie „Ja, nach jeder Ausführung bereinigen“ aus.

984eb2fa34867d70.png

5. Entwickeln eines einfachen CRUD Rest-Dienstes

Jetzt ist Ihre Anwendung vollständig für die Containerentwicklung konfiguriert und Sie haben den grundlegenden Entwicklungsworkflow mit Cloud Code durchgegangen. In den folgenden Abschnitten wenden Sie das Gelernte an. Dazu fügen Sie REST-Dienstendpunkte hinzu, die eine Verbindung zu einer verwalteten Datenbank in Google Cloud herstellen.

Abhängigkeiten konfigurieren

Der Anwendungscode verwendet eine Datenbank, um die übrigen Dienstdaten zu speichern. Stellen Sie sicher, dass die Abhängigkeiten verfügbar sind, indem Sie Folgendes in die Datei pom.xl einfügen.

  1. Öffnen Sie die Datei pom.xml und fügen Sie Folgendes in den Abschnitt „Abhängigkeiten“ 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>
    <dependency>
      <groupId>javax.persistence</groupId>
      <artifactId>javax.persistence-api</artifactId>
      <version>2.2</version>
    </dependency>

Code REST-Dienst

Quote.java

Erstellen Sie in /src/main/java/com/example/springboot/ eine Datei mit dem Namen Quote.java und kopieren Sie den Code unten. Definiert das Entitätsmodell für das in der Anwendung verwendete Quote-Objekt.

package com.example.springboot;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.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 unter src/main/java/com/example/springboot eine Datei mit dem Namen QuoteRepository.java und kopieren Sie den folgenden Code

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();
}

Dieser Code verwendet JPA für die dauerhafte Speicherung der Daten. Die Klasse erweitert die Spring-JPARepository-Schnittstelle und ermöglicht das Erstellen von benutzerdefiniertem Code. Sie haben dem Code die benutzerdefinierte Methode findRandomQuote hinzugefügt.

QuoteController.java

Eine QuoteController-Klasse stellt diese Funktion bereit, um den Endpunkt für den Dienst freizugeben.

Erstellen Sie unter src/main/java/com/example/springboot eine Datei mit dem Namen QuoteController.java und kopieren Sie den folgenden Inhalt

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) {
        Optional<Quote> quote = quoteRepository.findById(id);
        if (quote.isPresent()) {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } else {
            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

}

Datenbankkonfigurationen hinzufügen

application.yaml

Fügen Sie die Konfiguration für die Back-End-Datenbank hinzu, auf die der Dienst zugreift. Bearbeiten oder erstellen Sie die Datei application.yaml unter src/main/resources und fügen Sie eine parametrisierte Spring-Konfiguration für das Back-End 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

Ordner „db/migration“ unter „src/main/resources“ erstellen

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 als cloud-dev konfiguriert wird
  • DB_HOST – die private IP-Adresse für die Datenbank, die angezeigt wird, wenn die Datenbankinstanz erstellt wurde, oder indem Sie im Navigationsmenü der Google Cloud Console auf SQL klicken – ändern Sie den Wert.
  • DB_USER und DB_PASS – wie in der Cloud SQL-Instanzkonfiguration festgelegt, als Secret in der GCP gespeichert

Aktualisieren Sie die Datei „deployment.yaml“ mit dem Inhalt unten.

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 Wert DB_HOST durch die Adresse Ihrer Datenbank, indem Sie die folgenden Befehle im Terminal ausführen:

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

Öffnen Siedeployment.yaml und prüfen Sie, ob der Wert DB_HOST mit der Instanz-IP aktualisiert wurde.

fd63c0aede14beba.png

Anwendung bereitstellen und validieren

  1. Wählen Sie unten im Cloud Shell-Editor den Bereich „Cloud Code“ und dann oben auf dem Bildschirm „Debug on Kubernetes“ aus.

33a5cf41aae91adb.png

  1. Wenn der Build und die Tests abgeschlossen sind, wird auf dem Tab „Ausgabe“ Resource deployment/demo-app status completed successfully angezeigt und die URL wird aufgeführt: „Forwarded URL from service demo-app: http://localhost:8080“. Beachten Sie, dass der Port manchmal anders sein kann, z. B. 8081. Ist dies der Fall, legen Sie den entsprechenden Wert fest. Wert für URL im Terminal festlegen
export URL=localhost:8080
  1. Zufällige Zitate ansehen

Führen Sie im Terminal den folgenden Befehl mehrmals für den Endpunkt mit zufälligen Anführungszeichen aus. Wiederholten Aufruf mit unterschiedlichen Angeboten beobachten

curl $URL/random-quote | jq
  1. Angebot hinzufügen

Erstellen Sie mit dem unten aufgeführten Befehl ein neues Zitat mit id=6 und beobachten Sie, wie die Anfrage zurückgegeben wird

curl -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 $URL/quotes
  1. Angebot löschen

Löschen Sie jetzt das Zitat, das Sie mit der Methode "delete" hinzugefügt haben, und beobachten Sie den Antwortcode HTTP/1.1 204.

curl -v -X DELETE $URL/quotes/6
  1. Serverfehler

Wenn die letzte Anfrage noch einmal ausgeführt wird, nachdem der Eintrag bereits gelöscht wurde, tritt ein Fehlerstatus auf

curl -v -X DELETE $URL/quotes/6

Beachten Sie, dass die Antwort ein HTTP:500 Internal Server Error zurückgibt.

Fehler in der Anwendung beheben

Im vorherigen Abschnitt haben Sie in der Anwendung einen Fehlerstatus festgestellt, als Sie versucht haben, einen Eintrag zu löschen, der sich nicht in der Datenbank befand. In diesem Abschnitt legen Sie einen Haltepunkt fest, um das Problem zu lokalisieren. Der Fehler ist im DELETE-Vorgang aufgetreten, daher arbeiten Sie mit der QuoteController-Klasse.

  1. src/main/java/com/example/springboot/QuoteController.java“ öffnen
  2. Methode deleteQuote() finden
  3. Finde die folgende Zeile: Optional<Quote> quote = quoteRepository.findById(id);
  4. Legen Sie einen Haltepunkt für diese Zeile fest, indem Sie auf die leere Fläche links neben der Zeilennummer klicken.
  5. Ein roter Indikator wird angezeigt, der darauf hinweist, dass der Breakpoint festgelegt ist.
  6. Führen Sie den Befehl delete noch einmal aus.
curl -v -X DELETE $URL/quotes/6
  1. Wechseln Sie zurück zur Debug-Ansicht, indem Sie auf das Symbol in der linken Spalte klicken.
  2. Sehen Sie sich an, wie die Debug-Zeile in der QuoteController-Klasse gestoppt wurde.
  3. Klicken Sie im Debugger auf das step over-Symbol b814d39b2e5f3d9e.png.
  4. Beachten Sie, dass ein Code den internen Serverfehler HTTP 500 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 Block else sollte so refaktoriert werden, dass der Statuscode „HTTP 404 nicht gefunden“ zurückgegeben wird.

Korrigieren Sie den Fehler.

  1. Während die Fehlerbehebungssitzung noch läuft, schließen Sie die Anfrage ab, indem Sie auf „Weiter“ klicken im Steuerfeld zur Fehlerbehebung.
  2. Ändern Sie als Nächstes den else-Block in den Code:
       else {
                return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
            }

Die Methode sollte so aussehen:

@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
        Optional<Quote> quote = quoteRepository.findById(id);
        if (quote.isPresent()) {
            quoteRepository.deleteById(id);
            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
        } else {
            return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
        }
    }
  1. Löschbefehl noch einmal ausführen
curl -v -X DELETE $URL/quotes/6
  1. Gehen Sie den Debugger durch und beobachten Sie, wie der Fehler "HTTP 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
  1. Beenden Sie die Debugging-Sitzung, indem Sie auf das rote Quadrat in der Debugging-Symbolleiste klicken.

12bc3c82f63dcd8a.png

6f19c0f855832407.png

6. Glückwunsch

Glückwunsch! In diesem Lab haben Sie eine komplett neue Java-Anwendung erstellt und so konfiguriert, dass sie effektiv mit Containern funktioniert. Anschließend haben Sie Ihre Anwendung in einem Remote-GKE-Cluster bereitgestellt und entsprechende Fehler behoben. Dabei folgten Sie dem Entwicklerablauf, der auch in herkömmlichen Anwendungspaketen verwendet wird.

Was Sie gelernt haben

  • InnerLoop-Entwicklung mit Cloud Workstations
  • Neue Java-Startanwendung erstellen
  • Der Entwicklungsprozess
  • Einfachen CRUD REST-Dienst entwickeln
  • Anwendung im GKE-Cluster debuggen
  • Anwendung mit Cloud SQL-Datenbank verbinden