1. Przegląd
W tym laboratorium dowiesz się o funkcjach i możliwościach, które usprawniają przepływ pracy programistów zajmujących się tworzeniem aplikacji w Javie w środowisku skonteneryzowanym. Typowe tworzenie kontenerów wymaga od użytkownika znajomości szczegółów dotyczących kontenerów i procesu kompilacji kontenerów. Deweloperzy zwykle muszą też przerywać pracę i opuszczać IDE, aby testować i debugować aplikacje w środowiskach zdalnych. Dzięki narzędziom i technologiom wymienionym w tym samouczku deweloperzy mogą efektywnie pracować z aplikacjami w kontenerach bez opuszczania środowiska IDE.
Czego się nauczysz
W tym module poznasz metody tworzenia aplikacji w kontenerach w GCP, w tym:
- Programowanie w trybie inner loop z użyciem Cloud Workstations
- Tworzenie nowej aplikacji startowej w Javie
- Omówienie procesu tworzenia
- Tworzenie prostej usługi REST CRUD
- Debugowanie aplikacji w klastrze GKE
- Łączenie aplikacji z bazą danych Cloud SQL

2. Konfiguracja i wymagania
Samodzielne konfigurowanie środowiska
- Zaloguj się w konsoli Google Cloud i utwórz nowy projekt lub użyj istniejącego. Jeśli nie masz jeszcze konta Gmail ani Google Workspace, musisz je utworzyć.



- Nazwa projektu to wyświetlana nazwa uczestników tego projektu. Jest to ciąg znaków, który nie jest używany przez interfejsy API Google. Możesz ją zaktualizować w dowolnym momencie.
- Identyfikator projektu jest unikalny we wszystkich projektach Google Cloud i nie można go zmienić po ustawieniu. Konsola Cloud automatycznie generuje unikalny ciąg znaków. Zwykle nie musisz się nim przejmować. W większości ćwiczeń z programowania musisz odwoływać się do identyfikatora projektu (zwykle jest on oznaczony jako
PROJECT_ID). Jeśli wygenerowany identyfikator Ci się nie podoba, możesz wygenerować inny losowy identyfikator. Możesz też spróbować własnej nazwy i sprawdzić, czy jest dostępna. Po tym kroku nie można go zmienić i będzie obowiązywać przez cały czas trwania projektu. - Warto wiedzieć, że istnieje też trzecia wartość, czyli numer projektu, z której korzystają niektóre interfejsy API. Więcej informacji o tych 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w konsoli Cloud, aby korzystać z zasobów i interfejsów API Google Cloud. Ukończenie tego laboratorium nie powinno wiązać się z dużymi kosztami, a nawet z żadnymi. Aby wyłączyć zasoby i uniknąć naliczania opłat po zakończeniu tego samouczka, możesz usunąć utworzone zasoby lub cały projekt. Nowi użytkownicy Google Cloud mogą skorzystać z bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Uruchamianie edytora Cloud Shell
To laboratorium zostało zaprojektowane i przetestowane pod kątem używania w edytorze Google Cloud Shell. Aby uzyskać dostęp do edytora:
- uzyskać dostęp do projektu Google na stronie https://console.cloud.google.com;
- W prawym górnym rogu kliknij ikonę edytora Cloud Shell.

- U dołu okna otworzy się nowy panel.
- Kliknij przycisk Otwórz edytor.

- Edytor otworzy się z eksploratorem po prawej stronie i edytorem w środkowej części.
- U dołu ekranu powinien być też dostępny panel terminala.
- 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 laboratorium znajduje się w repozytorium container-developer-workshop w GoogleCloudPlatform na GitHubie. Sklonuj 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
wdrożyć 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 przygotuje tę infrastrukturę. Proces obsługi administracyjnej potrwa ponad 25 minut. Zanim przejdziesz do następnej sekcji, poczekaj na zakończenie działania skryptu.
./setup_with_cw.sh &
Klaster Cloud Workstations
Otwórz Cloud Workstations w Cloud Console. Poczekaj, aż klaster osiągnie stan READY.
Tworzenie konfiguracji stacji roboczych
Jeśli sesja Cloud Shell została odłączona, kliknij „Połącz ponownie”, a następnie uruchom polecenie gcloud cli, aby ustawić identyfikator projektu. Przed uruchomieniem polecenia zastąp poniższy przykładowy identyfikator projektu identyfikatorem projektu Qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
Aby utworzyć konfigurację Cloud Workstations, uruchom w terminalu poniższy skrypt.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Sprawdź wyniki w sekcji Konfiguracje. Przejście do stanu GOTOWY zajmie 2 minuty.

Otwórz Cloud Workstations w konsoli 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 stację roboczą.

Zezwól na pliki cookie innych firm, klikając ikonę na pasku adresu. 

Kliknij „Strona nie działa?”.

Kliknij „Zezwalaj na pliki cookie”.

Po uruchomieniu stacji roboczej pojawi się środowisko IDE Code OSS. Na stronie Wprowadzenie w środowisku IDE stacji roboczej kliknij „Oznacz jako gotowe”.

3. Tworzenie nowej aplikacji startowej w Javie
W tej sekcji utworzysz od zera 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 początkowej
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 zobaczysz ten komunikat, kliknij przycisk Zezwól, aby móc kopiować i wklejać na stacji roboczej.

- Rozpakuj aplikację
unzip sample-app.zip -d sample-app
- Otwórz folder „sample-app”.
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Dodawanie spring-boot-devtools i Jib
Aby włączyć narzędzia Spring Boot DevTools, znajdź i otwórz plik pom.xml w eksploratorze w edytorze. Następnie wklej ten kod po wierszu opisu <description>Demo project for Spring Boot</description>
- Dodawanie spring-boot-devtools w pliku pom.xml
Otwórz pom.xml w katalogu głównym projektu. Dodaj tę 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 wtyczkę jib-maven-plugin w pliku pom.xml
Jib to narzędzie open source do konteneryzacji w Javie od Google, które umożliwia programistom Java tworzenie kontenerów za pomocą znanych im narzędzi Java. Jib to szybki i prosty kreator obrazów kontenerów, który wykonuje wszystkie czynności związane z pakowaniem aplikacji w obraz kontenera. Nie wymaga pisania pliku Dockerfile ani instalowania Dockera i jest bezpośrednio zintegrowany z Mavenem i Gradle.
Przewiń w dół plik pom.xml i zaktualizuj sekcję Build, aby uwzględnić wtyczkę Jib. Po zakończeniu sekcja kompilacji powinna wyglądać tak:
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>
Generowanie manifestów
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 próbuje zidentyfikować katalogi z definicjami obrazów kontenerów, takie jak Dockerfile, a następnie tworzy dla każdego z nich manifest wdrożenia i usługi.
Aby rozpocząć proces, wykonaj w terminalu to polecenie:

- W terminalu wykonaj to polecenie:
skaffold init --generate-manifests
- Gdy pojawi się odpowiedni komunikat:
- Użyj strzałek, aby przenieść kursor do ikony
Jib Maven Plugin. - Aby wybrać opcję, naciśnij spację.
- Aby kontynuować, naciśnij Enter
- Wpisz 8080 jako port.
- Wpisz y, aby zapisać konfigurację.
Do obszaru roboczego dodano 2 pliki: skaffold.yaml i deployment.yaml.
Dane wyjściowe Skaffold:

Aktualizowanie nazwy aplikacji
Wartości domyślne uwzględnione w konfiguracji nie pasują obecnie do nazwy aplikacji. Zaktualizuj pliki, aby odwoływały się do nazwy aplikacji, a nie do wartości domyślnych.
- Zmiana wpisów w konfiguracji Skaffold
- Otwórz:
skaffold.yaml - Wybierz nazwę obrazu, która jest obecnie ustawiona jako
pom-xml-image. - Kliknij prawym przyciskiem myszy i wybierz Zmień wszystkie wystąpienia.
- Wpisz nową nazwę w formacie
demo-app.
- Zmiana wpisów w konfiguracji Kubernetes
- Otwórz plik
deployment.yaml - Wybierz nazwę obrazu, która jest obecnie ustawiona jako
pom-xml-image. - Kliknij prawym przyciskiem myszy i wybierz Zmień wszystkie wystąpienia.
- Wpisz nową nazwę w formacie
demo-app.
Włącz tryb automatycznej synchronizacji
Aby ułatwić zoptymalizowane gorące przeładowanie, użyj funkcji synchronizacji udostępnianej przez Jib. W tym kroku skonfigurujesz Skaffold tak, aby korzystał z tej funkcji w procesie kompilacji.
Pamiętaj, że profil „sync” konfigurowany w konfiguracji Skaffold wykorzystuje profil „sync” Springa skonfigurowany w poprzednim kroku, w którym włączono obsługę spring-dev-tools.
- Aktualizowanie konfiguracji Skaffold
W pliku skaffold.yaml zastąp całą sekcję kompilacji tymi specyfikacjami. 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
Dodawanie trasy domyślnej
Utwórz plik o nazwie HelloController.java w folderze /src/main/java/com/example/springboot/.

Wklej do pliku tę zawartość, 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 tworzenia
W tej sekcji wykonasz kilka czynności za pomocą wtyczki Cloud Code, aby poznać podstawowe procesy i sprawdzić konfigurację aplikacji startowej.
Cloud Code jest zintegrowany ze Skaffold, aby usprawnić proces programowania. Gdy w kolejnych krokach wdrożysz aplikację w GKE, Cloud Code i Skaffold automatycznie skompilują obraz kontenera, wypchną go do Container Registry, a następnie wdrożą aplikację w GKE. Dzieje się to w sposób niewidoczny, co pozwala deweloperowi skupić się na innych aspektach. Cloud Code usprawnia też proces programowania, zapewniając tradycyjne funkcje debugowania i szybkiej synchronizacji w przypadku programowania opartego na kontenerach.
Logowanie się do Google Cloud
Kliknij ikonę Cloud Code i wybierz „Zaloguj się w Google Cloud”:

Kliknij „Przejdź do logowania”.

Sprawdź dane wyjściowe w terminalu i otwórz link:

Zaloguj się, używając danych logowania ucznia Qwiklabs.

Wybierz „Zezwól”:

Skopiuj kod weryfikacyjny i wróć na kartę Stacja robocza.

Wklej kod weryfikacyjny i naciśnij Enter.

Dodawanie klastra Kubernetes
- Dodawanie klastra

- Wybierz Google Kubernetes Engine:

- Wybierz projekt.

- Wybierz „quote-cluster” utworzony podczas konfiguracji początkowej.


Ustawianie bieżącego identyfikatora projektu za pomocą gcloud CLI
Skopiuj identyfikator projektu na potrzeby tego modułu ze strony Qwiklabs.

Uruchom polecenie gcloud CLI, aby ustawić identyfikator projektu. Przed uruchomieniem polecenia zastąp przykładowy identyfikator projektu.
gcloud config set project qwiklabs-gcp-project-id
Przykładowe dane wyjściowe:

Debugowanie w Kubernetes
- W lewym panelu u dołu wybierz Cloud Code.

- W panelu, który pojawi się w sekcji SESJE DEWELOPERSKIE, kliknij Debugowanie w Kubernetes.
Jeśli nie widzisz tej opcji, przewiń w dół.

- Aby użyć bieżącego kontekstu, wybierz „Tak”.

- Wybierz „quote-cluster” utworzony podczas konfiguracji początkowej.

- Wybierz repozytorium kontenerów.

- Wybierz kartę Wyjście w dolnym panelu, aby wyświetlić postęp i powiadomienia.
- Aby wyświetlić dodatkowe szczegóły i logi przesyłane strumieniowo na żywo z kontenerów, w menu po prawej stronie wybierz „Kubernetes: Run/Debug - Detailed” (Kubernetes: uruchamianie/debugowanie – szczegółowe).

Poczekaj na wdrożenie aplikacji.

- Sprawdź wdrożoną aplikację w GKE w konsoli Google Cloud.

- Aby wrócić do widoku uproszczonego, w menu na karcie WYJŚCIE wybierz „Kubernetes: Run/Debug” (Kubernetes: uruchamianie/debugowanie).
- Po zakończeniu kompilacji i testów na karcie Wyniki pojawi się komunikat
Resource deployment/demo-app status completed successfullyoraz adres URL: „Forwarded URL from service demo-app: http://localhost:8080”. - W terminalu Cloud Code najedź kursorem na adres URL w danych wyjściowych (http://localhost:8080), a następnie w wyświetlonej etykiecie narzędzia kliknij Follow link (Otwórz link).

Otworzy się nowa karta, na której zobaczysz ten wynik:

Wykorzystywanie punktów przerwania
- Otwórz aplikację
HelloController.javaznajdującą się w lokalizacji/src/main/java/com/example/springboot/HelloController.java. - Znajdź instrukcję powrotu dla ścieżki głównej, która brzmi
return String.format("Hello from your %s environment!", target); - Dodaj punkt przerwania do tego wiersza, klikając puste miejsce po lewej stronie numeru wiersza. Pojawi się czerwony wskaźnik, który oznacza, że punkt przerwania został ustawiony.

- Odśwież przeglądarkę i zwróć uwagę, że debuger zatrzymuje proces w punkcie przerwania i umożliwia zbadanie zmiennych i stanu aplikacji, która jest uruchomiona zdalnie w GKE.

- Klikaj w sekcji zmiennych, aż znajdziesz zmienną „Cel”.
- Sprawdź, czy bieżąca wartość to „local”.

- Kliknij dwukrotnie nazwę zmiennej „target” i w wyskakującym okienku
zmień wartość na „Cloud Workstations”;

- Kliknij przycisk Dalej w panelu sterowania debugowaniem.

- Sprawdź odpowiedź w przeglądarce, w której powinna się teraz wyświetlać wpisana przez Ciebie zaktualizowana wartość.

- Usuń punkt przerwania, klikając czerwony wskaźnik po lewej stronie numeru wiersza. Dzięki temu kod nie zatrzyma się w tym wierszu podczas wykonywania kolejnych kroków tego laboratorium.
Gorące przeładowanie
- Zmień instrukcję, aby zwracała inną wartość, np. „Hello from %s Code”.
- Plik jest automatycznie zapisywany i synchronizowany w kontenerach zdalnych w GKE.
- Aby zobaczyć zaktualizowane wyniki, odśwież przeglądarkę.
- Zakończ sesję debugowania, klikając czerwony kwadrat na pasku narzędzi debugowania.

Wybierz „Tak, zwalniaj miejsce po każdym uruchomieniu”.

5. Tworzenie prostej usługi REST CRUD
Na tym etapie aplikacja jest w pełni skonfigurowana pod kątem tworzenia skonteneryzowanych aplikacji, a Ty znasz już podstawowy przepływ pracy programisty w Cloud Code. W kolejnych sekcjach przećwiczysz zdobytą wiedzę, dodając punkty końcowe usługi REST łączące się z zarządzaną bazą danych w Google Cloud.
Konfigurowanie zależności
Kod aplikacji używa bazy danych do przechowywania danych usługi REST. Sprawdź, czy zależności są dostępne, dodając poniższy kod do pliku pom.xml.
- Otwórz plik
pom.xmli dodaj ten kod do sekcji zależności w 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 katalogu /src/main/java/com/example/springboot/ plik o nazwie Quote.java i skopiuj do niego poniższy kod. Określa model jednostki dla obiektu Quote 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 w katalogu src/main/java/com/example/springboot i skopiuj do niego 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 używa interfejsu JPA do utrwalania danych. Klasa rozszerza interfejs Spring JPARepository i umożliwia tworzenie niestandardowego kodu. W dodawanym kodzie znajduje się metoda niestandardowa findRandomQuote.
QuoteController.java
Aby udostępnić punkt końcowy usługi, klasa QuoteController będzie udostępniać tę funkcję.
Utwórz plik o nazwie QuoteController.java w katalogu src/main/java/com/example/springboot i skopiuj do niego 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);
}
}
}
Dodawanie konfiguracji 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 folderze src/main/resources i dodaj sparametryzowaną konfigurację Springa 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 w 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 wskazywała środowisko, w którym jest wykonywana aplikacja.
- SPRING_PROFILES_ACTIVE – pokazuje aktywny profil Spring, który zostanie skonfigurowany na
cloud-dev - DB_HOST – prywatny adres IP bazy danych, który został zapisany podczas tworzenia instancji bazy danych lub po kliknięciu
SQLw menu nawigacyjnym konsoli Google Cloud. Zmień tę wartość. - DB_USER i DB_PASS – zgodnie z ustawieniami konfiguracji instancji Cloud SQL, przechowywane jako Secret w GCP.
Zaktualizuj plik deployment.yaml, wstawiając do niego 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 o adres IP instancji.

Wdrażanie i weryfikowanie aplikacji
- W panelu u dołu edytora Cloud Shell wybierz Cloud Code, a następnie u góry ekranu kliknij Debug on Kubernetes (Debugowanie w Kubernetes).

- Po zakończeniu kompilacji i testów na karcie Wyniki pojawi się komunikat
Resource deployment/demo-app status completed successfullyoraz adres URL: „Forwarded URL from service demo-app: http://localhost:8080”. Pamiętaj, że czasami port może być inny, np. 8081. Jeśli tak, ustaw odpowiednią wartość. Ustawianie wartości adresu URL na terminalu
export URL=localhost:8080
- Wyświetlanie losowych cytatów
W terminalu uruchom kilka razy to polecenie w odniesieniu do punktu końcowego random-quote. Obserwowanie powtarzających się wywołań zwracających różne wyceny
curl $URL/random-quote | jq
- Dodawanie wyceny
Utwórz nową ofertę z identyfikatorem 6 za pomocą polecenia podanego poniżej i sprawdź, czy żądanie zostało zwrócone.
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 cytat za pomocą metody usuwania i sprawdź kod odpowiedzi HTTP/1.1 204.
curl -v -X DELETE $URL/quotes/6
- Błąd serwera
Wywołaj stan błędu, ponownie uruchamiając ostatnie żądanie po usunięciu wpisu.
curl -v -X DELETE $URL/quotes/6
Zwróć uwagę, że odpowiedź zwraca wartość HTTP:500 Internal Server Error.
Debugowanie aplikacji
W poprzedniej sekcji wystąpił błąd w aplikacji, gdy próbowano usunąć wpis, którego nie było w bazie danych. W tej sekcji ustawisz punkt przerwania, aby zlokalizować problem. Błąd wystąpił w operacji DELETE, więc będziesz pracować z klasą QuoteController.
- Otwórz:
src/main/java/com/example/springboot/QuoteController.java - Znajdź metodę
deleteQuote(). - Znajdź wiersz:
Optional<Quote> quote = quoteRepository.findById(id); - Ustaw punkt przerwania w tym wierszu, klikając puste miejsce po lewej stronie numeru wiersza.
- Pojawi się czerwony wskaźnik, który oznacza, ż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 QuoteController.
- W debugerze kliknij ikonę
step over
. - Zwróć uwagę, że kod zwraca do klienta błąd wewnętrzny serwera HTTP 500, co nie jest idealne.
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
Aktualizowanie kodu
Kod jest nieprawidłowy i blok else należy zmodyfikować, aby zwracał kod stanu HTTP 404 Not Found.
Popraw błąd.
- Gdy sesja debugowania jest nadal aktywna, dokończ żądanie, klikając przycisk „Dalej” w panelu sterowania debugowaniem.
- Następnie zmień blok
elsena ten kod:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Metoda powinna wyglądać 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);
}
}
- Ponowne uruchomienie polecenia usuwania
curl -v -X DELETE $URL/quotes/6
- Przejdź przez debuger i obserwuj, jak do elementu wywołującego jest zwracany błąd HTTP 404 (nie znaleziono).
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
- Zakończ sesję debugowania, klikając czerwony kwadrat na pasku narzędzi debugowania.


6. Gratulacje
Gratulacje! W tym module udało Ci się utworzyć od podstaw nową aplikację w Javie i skonfigurować ją tak, aby skutecznie działała z kontenerami. Następnie wdrożyliśmy i debugowaliśmy aplikację w zdalnym klastrze GKE, korzystając z tego samego przepływu pracy dewelopera, który jest stosowany w tradycyjnych stosach aplikacji.
Czego się dowiedziałeś
- Programowanie w trybie inner loop z użyciem Cloud Workstations
- Tworzenie nowej aplikacji startowej w Javie
- Omówienie procesu tworzenia
- Tworzenie prostej usługi REST CRUD
- Debugowanie aplikacji w klastrze GKE
- Łączenie aplikacji z bazą danych Cloud SQL
