1. Przegląd
W tym module poznasz funkcje i możliwości, które usprawniają proces tworzenia aplikacji w języku Java 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 muszą też zwykle 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 lub Google Workspace, musisz je utworzyć.



- Nazwa projektu to wyświetlana nazwa dla 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 przewodników 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ć utworzyć własny adres i sprawdzić, czy jest dostępny. Po tym kroku nie można go zmienić i będzie obowiązywać przez cały czas trwania projektu. - Warto wiedzieć, że istnieje trzecia wartość, numer projektu, której używają 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 programu bezpłatnego okresu próbnego, w którym mają do dyspozycji środki w wysokości 300 USD.
Uruchamianie edytora Cloud Shell
Ten moduł został zaprojektowany i przetestowany 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 modułu znajduje się w repozytorium container-developer-workshop w GoogleCloudPlatform na GitHubie. Sklonuj go za pomocą polecenia poniżej, a potem 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 przykładowy identyfikator projektu poniżej 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 plik 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 języku Java 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 plików 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 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:

- Uruchom w terminalu 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 numer portu.
- Wpisz y, aby zapisać konfigurację.
Do obszaru roboczego dodawane są 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 Twojej 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
- Zmienianie 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 korzysta z profilu „sync” Springa skonfigurowanego 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 za kulisami, 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ę stacji roboczej.

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 Debuguj 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.

- Aby wyświetlić postęp i powiadomienia, w dolnym okienku wybierz kartę Wyjście.
- Aby wyświetlić dodatkowe szczegóły i dzienniki 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 successfullyi adres URL: „Forwarded URL from service demo-app: http://localhost:8080” (Przekierowany adres URL z usługi demo-app: http://localhost:8080). - W terminalu Cloud Code najedź kursorem na adres URL w wyniku (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 te dane wyjściowe:

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 oraz stanu aplikacji, która działa 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”;

- Na panelu sterowania debugowaniem kliknij przycisk Dalej.

- 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 modułu.
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. Upewnij się, że 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 Code
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
Dodawanie usługi migracji bazy danych
Utwórz foldery db/migration w folderze 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 będzie 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ń wartość. - DB_USER i DB_PASS – zgodnie z ustawieniami w 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 successfullyi adres URL: „Forwarded URL from service demo-app: http://localhost:8080” (Przekierowany adres URL z usługi 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 poniższe polecenie kilka razy 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 delete 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 debuggerze 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 (nie znaleziono).
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 błąd HTTP 404 Not Found zwracany do wywołującego.
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 języku Java i skonfigurować ją tak, aby skutecznie działała z kontenerami. Następnie wdrożono i debugowano 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ś(-aś)
- 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
