1. Omówienie
W tym module przedstawiamy funkcje i możliwości zaprojektowane w celu usprawnienia procesu programowania dla inżynierów, których zadaniem jest tworzenie aplikacji w języku Java w skonteneryzowanym środowisku. Typowe tworzenie kontenerów wymaga, aby użytkownik znał szczegóły kontenerów i proces ich tworzenia. Poza tym deweloperzy zwykle muszą przerwać przepływ pracy, wychodząc z IDE, aby przetestować i debugować aplikacje w środowiskach zdalnych. Dzięki narzędziom i technologiom wspomnianym w tym samouczku deweloperzy mogą wydajnie pracować z aplikacjami skonteneryzowanymi bez opuszczania IDE.
Czego się nauczysz
W tym module nauczysz się, jak tworzyć aplikacje z wykorzystaniem kontenerów w GCP, takich jak:
- Programowanie InnerLoop z użyciem Cloud Workstations
- Tworzenie nowej aplikacji startowej Java
- Omówienie procesu programowania
- Opracowanie prostej usługi CRUD Rest Service
- Debugowanie aplikacji w klastrze GKE
- Łączę aplikację z bazą danych Cloud SQL
2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub wykorzystaj już istniejący. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.
- Nazwa projektu jest wyświetlaną nazwą uczestników tego projektu. To ciąg znaków, który nie jest używany przez interfejsy API Google. W każdej chwili możesz ją zmienić.
- Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić (po jego ustawieniu nie można go zmienić). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń z programowania konieczne jest odwołanie się do identyfikatora projektu (zwykle nazywa się on
PROJECT_ID
). Jeśli nie podoba Ci się wygenerowany identyfikator, możesz wygenerować kolejny losowy. Możesz też spróbować własnych sił i sprawdzić, czy jest dostępna. Potem nie będzie można go zmienić. Pozostanie ono przez czas trwania projektu. - Dostępna jest trzecia wartość, numer projektu, z którego korzystają niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w Cloud Console, aby korzystać z zasobów Cloud/interfejsów API. Ukończenie tego ćwiczenia z programowania nie powinno kosztować zbyt wiele. Aby wyłączyć zasoby, aby nie naliczać opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z programu bezpłatnego okresu próbnego o wartości 300 USD.
Uruchom edytor Cloud Shell
Ten moduł został opracowany i przetestowany pod kątem użycia z edytorem Google Cloud Shell. Aby uzyskać dostęp do edytora:
- wejdź na stronę swojego projektu Google na https://console.cloud.google.com.
- W prawym górnym rogu kliknij ikonę edytora Cloud Shell.
- Na dole okna otworzy się nowy panel
- Kliknij przycisk Otwórz edytor
- Edytor otworzy się z eksploratorem po prawej stronie i edytorem w obszarze środkowym.
- Okienko terminala powinno być też dostępne u dołu ekranu
- Jeśli terminal NIE jest otwarty, użyj kombinacji klawiszy „Ctrl+”, aby otworzyć nowe okno terminala
Konfigurowanie gcloud
W Cloud Shell ustaw identyfikator projektu i region, w którym chcesz wdrożyć aplikację. Zapisz je jako zmienne PROJECT_ID
i REGION
.
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Klonowanie kodu źródłowego
Kod źródłowy tego modułu znajduje się w module container-developer-workshop w GoogleCloudPlatform na GitHubie. Skopiuj go za pomocą poniższego polecenia, a następnie przejdź do katalogu.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Udostępnij infrastrukturę używaną w tym module
W tym module wdrożysz kod w GKE i uzyskasz dostęp do danych przechowywanych w bazie danych Cloud SQL. Poniższy skrypt konfiguracji przygotowuje dla Ciebie tę infrastrukturę. Proces obsługi administracyjnej potrwa ponad 25 minut. Zanim przejdziesz do następnej sekcji, poczekaj, aż skrypt się zakończy.
./setup_with_cw.sh &
Klaster Cloud Workstations
Otwórz Cloud Workstations w konsoli Cloud. Poczekaj, aż klaster zmieni stan na READY
.
Tworzenie konfiguracji stacji roboczych
Jeśli sesja Cloud Shell została odłączona, kliknij „Połącz ponownie” a następnie uruchom polecenie wiersza poleceń gcloud, aby ustawić identyfikator projektu. Zanim uruchomisz polecenie, zastąp przykładowy identyfikator projektu poniżej identyfikatorem projektu qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
Aby utworzyć konfigurację Cloud Workstations, uruchom poniższy skrypt w terminalu.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Sprawdź wyniki w sekcji Konfiguracje. Przejście w stan GOTOWY zajmie 2 minuty.
Otwórz w konsoli Cloud Workstations i utwórz nową instancję.
Zmień nazwę na my-workstation
i wybierz istniejącą konfigurację: codeoss-java
.
Sprawdź wyniki w sekcji Stacje robocze.
Uruchom stację roboczą
Uruchom i uruchom stację roboczą.
Zezwalaj na pliki cookie innych firm, klikając ikonę na pasku adresu.
Kliknij „Witryna nie działa?”.
Kliknij przycisk „Zezwalaj na pliki cookie”.
Po uruchomieniu stacji roboczej pojawi się okno Code OSS IDE. Kliknij „Oznacz jako gotowe”. na pierwszej stronie wprowadzającej do IDE stacji roboczej
3. Tworzenie nowej aplikacji startowej Java
W tej sekcji utworzysz od podstaw nową aplikację Java Spring Boot, korzystając z przykładowej aplikacji udostępnionej przez spring.io. Otwórz nowy terminal.
Klonowanie przykładowej aplikacji
- Tworzenie aplikacji startowej
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
Jeśli widzisz ten komunikat, kliknij przycisk Zezwalaj, aby skopiować i wkleić do stacji roboczej.
- Rozpakuj aplikację
unzip sample-app.zip -d sample-app
- Otwórz „sample-app”. folder
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Dodaj narzędzia deweloperskie i Jib
Aby włączyć Spring Boot DevTools, odszukaj i otwórz plik pom.xml w eksploratorze w edytorze. Następnie wklej następujący kod po linii tekstu, która brzmi <description>Demo project for Spring Boot</description>
- Dodaj narzędzie spring-boot-devtools w pliku pom.xml
Otwórz pom.xml
w katalogu głównym projektu. Dodaj poniższą konfigurację po wpisie Description
.
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>
- Włącz jib-maven-plugin w pom.xml
Jib to oferowane przez Google narzędzie do konteneryzacji języka Java, które umożliwia programistom Java tworzenie kontenerów przy użyciu znanych im narzędzi Java. Jib to szybkie i proste narzędzie do tworzenia obrazów kontenerów, które wykonuje wszystkie etapy pakowania aplikacji w obraz kontenera. Nie wymaga napisania pliku Dockerfile ani zainstalowania Dockera. Usługa jest bezpośrednio zintegrowana z Mavenem i Gradle.
Przewiń plik pom.xml
w dół i zaktualizuj sekcję Build
, aby uwzględnić wtyczkę Jib. Po zakończeniu sekcja kompilacji powinna być zgodna z podanymi niżej informacjami.
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>
Generuj pliki manifestu
Skaffold udostępnia zintegrowane narzędzia, które upraszczają tworzenie kontenerów. W tym kroku zainicjujesz Skaffold, który automatycznie utworzy podstawowe pliki YAML Kubernetes. Proces ten próbuje zidentyfikować katalogi z definicjami obrazów kontenerów, na przykład plik Dockerfile, a następnie tworzy dla każdego z nich plik manifestu wdrożenia i usługi.
Aby rozpocząć proces, wykonaj poniższe polecenie w terminalu.
- Wykonaj w terminalu to polecenie
skaffold init --generate-manifests
- Gdy pojawi się komunikat:
- Użyj strzałek, by przenieść kursor do:
Jib Maven Plugin
- Naciśnij spację, aby wybrać opcję.
- Aby kontynuować, naciśnij Enter
- Wpisz 8080 jako numer portu
- Wpisz y, aby zapisać konfigurację.
Do obszarów roboczych skaffold.yaml
i deployment.yaml
zostaną dodane 2 pliki
Dane wyjściowe Skaffold:
Zaktualizuj nazwę aplikacji
Domyślne wartości uwzględnione w konfiguracji nie są obecnie zgodne z nazwą Twojej aplikacji. Zaktualizuj pliki, aby odwoływały się do nazwy aplikacji zamiast do wartości domyślnych.
- Zmień wpisy w konfiguracji Skaffold
- Otwórz:
skaffold.yaml
- Wybierz nazwę obrazu, który jest obecnie ustawiony jako
pom-xml-image
- Kliknij prawym przyciskiem myszy i wybierz Zmień wszystkie wystąpienia
- Wpisz nową nazwę jako
demo-app
- Zmień wpisy w konfiguracji Kubernetes
- Otwórz plik
deployment.yaml
- Wybierz nazwę obrazu, który jest obecnie ustawiony jako
pom-xml-image
- Kliknij prawym przyciskiem myszy i wybierz Zmień wszystkie wystąpienia
- Wpisz nową nazwę jako
demo-app
Włącz tryb automatycznej synchronizacji
Aby zoptymalizować proces ponownego ładowania z pamięci, użyjesz funkcji synchronizacji dostarczanej przez Jib. W tym kroku skonfigurujesz Skaffold tak, aby używał tej funkcji w procesie kompilacji.
Pamiętaj, że funkcja synchronizacji profil konfigurowany w konfiguracji Skaffold korzysta ze springowej synchronizacji Skonfigurowany w poprzednim kroku profil z włączoną obsługą narzędzi wiosennych dla programistów.
- Aktualizowanie konfiguracji Skaffold
W pliku skaffold.yaml
zastąp całą sekcję kompilacji pliku podaną poniżej. Nie zmieniaj innych sekcji pliku.
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
Dodaj trasę domyślną
Utwórz plik o nazwie HelloController.java
w folderze /src/main/java/com/example/springboot/
.
Wklej poniższą zawartość w pliku, aby utworzyć domyślną trasę HTTP.
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. Omówienie procesu programowania
W tej sekcji zapoznasz się z kilkoma krokami korzystania z wtyczki Cloud Code, które pozwolą Ci poznać podstawowe procesy i sprawdzić konfigurację oraz konfigurację aplikacji startowej.
Cloud Code integruje się ze Skaffold, aby usprawnić proces programowania. Gdy wdrożysz obraz kontenera w GKE w poniższych krokach, Cloud Code i Skaffold automatycznie skompilują obraz kontenera, wypchnie go do Container Registry, a następnie wdroży aplikację w GKE. Dzieje się to za kulisami, odbierając szczegóły od procesu deweloperskiego. Cloud Code usprawnia też proces programowania, udostępniając tradycyjne funkcje debugowania i synchronizacji przy użyciu kontenerów podczas programowania.
Logowanie się do Google Cloud
Kliknij ikonę Cloud Code i wybierz opcję „Sign in to Google Cloud” (Zaloguj się w Google Cloud):
Kliknij „Przejdź do logowania”.
Sprawdź dane wyjściowe w terminalu i otwórz link:
Zaloguj się danymi logowania uczniów Qwiklabs.
Wybierz „Zezwól”:
Skopiuj kod weryfikacyjny i wróć na kartę Workstations.
Wklej kod weryfikacyjny i naciśnij Enter.
Dodaj klaster Kubernetes
- Dodaj klaster
- Wybierz Google Kubernetes Engine:
- Wybierz projekt.
- Wybierz „quote-cluster” utworzony podczas początkowej konfiguracji.
Ustaw identyfikator bieżącego projektu za pomocą wiersza poleceń gcloud
Skopiuj identyfikator projektu powiązany z tym modułem ze strony qwiklabs.
Aby ustawić identyfikator projektu, uruchom polecenie interfejsu wiersza poleceń gcloud. Zastąp przykładowy identyfikator projektu przed uruchomieniem polecenia.
gcloud config set project qwiklabs-gcp-project-id
Przykładowe dane wyjściowe:
Debuguj w Kubernetes
- W panelu po lewej stronie u dołu kliknij Cloud Code.
- W panelu, który pojawi się w sekcji DEVELOPMENT SESSIONS, wybierz Debug on Kubernetes.
Przewiń w dół, jeśli ta opcja nie jest widoczna.
- Wybierz „Tak”. aby użyć bieżącego kontekstu.
- Wybierz „quote-cluster” utworzony podczas początkowej konfiguracji.
- Wybierz Repozytorium kontenerów.
- Wybierz kartę Wyniki w dolnym panelu, aby zobaczyć postęp i powiadomienia.
- Wybierz „Kubernetes: Run/Debug - detail”. w menu kanału po prawej stronie, aby wyświetlić dodatkowe szczegóły i logi, które są przesyłane na żywo z kontenerów.
Poczekaj na wdrożenie aplikacji.
- Sprawdź wdrożoną aplikację w GKE w konsoli Cloud.
- Wróć do widoku uproszczonego, wybierając „Kubernetes: Run/Debug”. z menu na karcie OUTPUT (WYJŚCIE).
- Po zakończeniu kompilacji i testów na karcie Dane wyjściowe będzie widoczny komunikat
Resource deployment/demo-app status completed successfully
oraz adres URL: „Przekierowany adres URL z aplikacji demonstracyjnej usługi: http://localhost:8080”. - W terminalu Cloud Code najedź kursorem na adres URL w danych wyjściowych (http://localhost:8080), a następnie we wskazówce narzędzia kliknij link Obserwuj.
Nowa karta zostanie otwarta, a dane wyjściowe zobaczysz poniżej:
Wykorzystuj punkty przerwania
- Otwórz aplikację
HelloController.java
pod adresem/src/main/java/com/example/springboot/HelloController.java
. - Znajdź instrukcję zwrotną dla ścieżki głównej, która brzmi:
return String.format("Hello from your %s environment!", target);
- Dodaj do tego wiersza punkt przerwania, klikając puste miejsce po lewej stronie numeru wiersza. Pojawi się czerwony wskaźnik informujący o ustawieniu punktu przerwania
- Załaduj ponownie przeglądarkę. Pamiętaj, że debuger zatrzymuje proces w punkcie przerwania i umożliwia zbadanie zmiennych oraz stanu aplikacji uruchomionej zdalnie w GKE.
- Przejdź do sekcji zmiennych, aż znajdziesz sekcję „Wartość docelowa” .
- Obserwuj obecną wartość jako „local”
- Kliknij dwukrotnie zmienną o nazwie „target”. a w wyskakującym okienku
zmień wartość na „Cloud Workstations”
- Kliknij przycisk Dalej w panelu sterowania debugowania.
- Sprawdź odpowiedź w przeglądarce, w której wyświetla się wprowadzona przed chwilą zaktualizowana wartość.
- Usuń punkt przerwania, klikając czerwony wskaźnik po lewej stronie numeru wiersza. Dzięki temu kod nie zatrzyma wykonywania w tym wierszu w dalszej części tego modułu.
Ponowne załadowanie „na gorąco”
- Zmień instrukcję tak, aby zwracała inną wartość, na przykład „Cześć od %s Code”
- Plik jest automatycznie zapisywany i synchronizowany z kontenerami zdalnymi w GKE
- Aby zobaczyć zaktualizowane wyniki, odśwież przeglądarkę.
- Zatrzymaj sesję debugowania, klikając czerwony kwadrat na pasku narzędzi debugowania.
Wybierz „Tak wyczyść po każdym uruchomieniu”.
5. Opracowanie prostej usługi CRUD Rest Service
Na tym etapie Twoja aplikacja jest w pełni skonfigurowana do programowania skonteneryzowanego i masz już za sobą podstawowy przepływ pracy programistyczny w Cloud Code. W kolejnych sekcjach przećwiczysz zdobyte informacje, dodając punkty końcowe usługi REST łączące się z zarządzaną bazą danych w Google Cloud.
Skonfiguruj zależności
Kod aplikacji używa bazy danych do utrwalania danych usługi reszty. Sprawdź, czy zależności są dostępne, dodając do pliku pom.xl ten kod
- Otwórz plik
pom.xml
i dodaj poniższy kod w sekcji zależności konfiguracji
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>
Usługa REST kodu
Quote.java
Utwórz w usłudze /src/main/java/com/example/springboot/
plik o nazwie Quote.java
i skopiuj poniższy kod. Definiuje model encji dla obiektu Offer używanego w aplikacji.
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
Utwórz plik o nazwie QuoteRepository.java
na stronie src/main/java/com/example/springboot
i skopiuj ten kod
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();
}
Ten kod wykorzystuje JPA do utrwalania danych. Klasa rozszerza interfejs Spring JPARepository
i umożliwia tworzenie kodu niestandardowego. W kodzie dodano niestandardową metodę findRandomQuote
.
QuoteController.java
Aby udostępnić punkt końcowy usługi, klasa QuoteController
udostępnia tę funkcję.
Utwórz plik o nazwie QuoteController.java
o src/main/java/com/example/springboot
i skopiuj tę treść
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);
}
}
}
Dodaj konfiguracje bazy danych
application.yaml
Dodaj konfigurację bazy danych backendu, do której usługa ma dostęp. Edytuj (lub utwórz, jeśli nie istnieje) plik o nazwie application.yaml
w katalogu src/main/resources
i dodaj z parametrami konfigurację Spring dla backendu.
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
Dodaj usługę migracji bazy danych
Tworzenie folderów db/migration
pod adresem src/main/resources
Utwórz plik SQL: V1__create_quotes_table.sql
Wklej do pliku tę zawartość
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');
Konfiguracja Kubernetes
Poniższe dodatki do pliku deployment.yaml
umożliwiają aplikacji łączenie się z instancjami Cloud SQL.
- TARGET – konfiguruje zmienną, aby wskazać środowisko, w którym aplikacja jest wykonywana
- SPRING_PROFILES_ACTIVE – pokazuje aktywny profil Spring, który zostanie skonfigurowany jako
cloud-dev
- DB_HOST – prywatny adres IP bazy danych, który został zanotowany podczas tworzenia instancji bazy danych lub po kliknięciu
SQL
w menu nawigacyjnym konsoli Google Cloud – zmień wartość. - DB_USER i DB_PASS – wartość ustawiona w konfiguracji instancji Cloud SQL, zapisana jako obiekt tajny w GCP
Zaktualizuj plik Deployment.yaml poniżej, podając poniższą zawartość.
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
Zastąp wartość DB_HOST adresem bazy danych, uruchamiając w terminalu te polecenia:
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
Otwórz plik Deployment.yaml i sprawdź, czy wartość DB_HOST została zaktualizowana przy użyciu adresu IP instancji.
Wdróż i zweryfikuj aplikację
- W panelu u dołu edytora Cloud Shell kliknij Cloud Code, a potem u góry ekranu wybierz Debug on Kubernetes (Debuguj w Kubernetes).
- Po zakończeniu kompilacji i testów na karcie Dane wyjściowe będzie widoczny komunikat
Resource deployment/demo-app status completed successfully
oraz adres URL: „Przekierowany adres URL z aplikacji demonstracyjnej usługi: http://localhost:8080”. Pamiętaj, że czasami numer portu może być inny, np. 8081. W takim przypadku ustaw odpowiednią wartość. Ustaw wartość adresu URL w terminalu
export URL=localhost:8080
- Wyświetl losowe cytaty
W terminalu uruchom poniższe polecenie kilka razy, kierując się punktem końcowym w postaci losowej cudzysłowu. Zwróć uwagę na powtarzające się wywołanie zwracające różne cudzysłowy
curl $URL/random-quote | jq
- Dodaj wycenę
Utwórz nową wycenę o wartości id=6 za pomocą polecenia wymienionego poniżej i sprawdź, czy żądanie jest powtarzane
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
- Usuwanie wyceny
Teraz usuń dodany przed chwilą cytat za pomocą metody usuwania i obserwuj kod odpowiedzi HTTP/1.1 204
.
curl -v -X DELETE $URL/quotes/6
- Błąd serwera
Wystąpił błąd podczas ponownego uruchamiania ostatniego żądania po usunięciu wpisu
curl -v -X DELETE $URL/quotes/6
Zauważ, że odpowiedź zwraca wartość HTTP:500 Internal Server Error
.
Debugowanie aplikacji
W poprzedniej sekcji podczas próby usunięcia wpisu, którego nie było w bazie danych, wystąpił w aplikacji stan błędu. W tej sekcji ustawisz punkt przerwania, który pozwoli zlokalizować problem. Błąd wystąpił w operacji DELETE, więc będziesz pracować z klasą citeController.
- Otwórz:
src/main/java/com/example/springboot/QuoteController.java
- Znajdź metodę
deleteQuote()
- Znajdź linię:
Optional<Quote> quote = quoteRepository.findById(id);
- Aby ustawić punkt przerwania w tym wierszu, kliknij puste miejsce po lewej stronie numeru wiersza.
- Pojawi się czerwony wskaźnik wskazujący, że punkt przerwania został ustawiony.
- Ponownie uruchom polecenie
delete
curl -v -X DELETE $URL/quotes/6
- Aby wrócić do widoku debugowania, kliknij ikonę w lewej kolumnie
- Zwróć uwagę na wiersz debugowania zatrzymany w klasie citeController.
- W debugerze kliknij ikonę
step over
.
- Zwróć uwagę, że kod zwraca do klienta wewnętrzny błąd serwera HTTP 500, co nie jest idealnym rozwiązaniem.
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
Zaktualizuj kod
Kod jest nieprawidłowy i należy zrefaktoryzować blok else
, aby odesłać kod stanu 404 (nie znaleziono).
Popraw błąd.
- Gdy sesja debugowania nadal działa, dokończ żądanie, naciskając „Dalej”. w panelu sterowania debugowania.
- Następnie zmień blok
else
na kod:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Metoda powinna wyglądać mniej więcej tak:
@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); } }
- Ponownie uruchom polecenie usuwania
curl -v -X DELETE $URL/quotes/6
- Przejdź przez debuger i sprawdź, czy zwracany do elementu wywołującego wywołanie błędu HTTP 404 „Not Found”.
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
- Zatrzymaj sesję debugowania, klikając czerwony kwadrat na pasku narzędzi debugowania.
6. Gratulacje
Gratulacje! W tym module udało Ci się utworzyć od zera nową aplikację w języku Java i skonfigurować ją tak, aby wydajnie współpracowała z kontenerami. Następnie wdrożono i debugowałeś(-aś) aplikację w zdalnym klastrze GKE, postępując zgodnie z procedurą programistyczną obowiązującą w tradycyjnych stosach aplikacji.
Zdobyte informacje
- Programowanie InnerLoop z użyciem Cloud Workstations
- Tworzenie nowej aplikacji startowej Java
- Omówienie procesu programowania
- Programowanie prostej usługi CRUD REST
- Debugowanie aplikacji w klastrze GKE
- Łączę aplikację z bazą danych Cloud SQL