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:
- Konfiguracja i wymagania
- Tworzenie nowej aplikacji startowej Java
- Omówienie procesu programowania
- Opracowanie prostej usługi CRUD Rest Service
- Czyszczenie
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 i w każdej chwili możesz go zaktualizować.
- Identyfikator projektu musi być unikalny we wszystkich projektach Google Cloud i nie można go zmienić (nie można go zmienić po ustawieniu). Cloud Console automatycznie wygeneruje unikalny ciąg znaków. zwykle nieważne, co ona jest. W większości ćwiczeń w Codelabs musisz odwoływać się do identyfikatora projektu (który zwykle nazywa się
PROJECT_ID
), więc jeśli Ci się nie podoba, wygeneruj kolejny losowy projekt lub wypróbuj swój własny identyfikator i sprawdź, czy jest dostępny. Potem urządzenie jest „zawieszone”. po utworzeniu projektu. - Występuje trzecia wartość – numer projektu – używany przez niektóre interfejsy API. Więcej informacji o wszystkich 3 wartościach znajdziesz w dokumentacji.
- Następnie musisz włączyć płatności w konsoli Cloud, aby móc korzystać z zasobów i interfejsów API Cloud. 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, wykonaj czynności „wyczyść” znajdziesz na końcu tego ćwiczenia. 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 PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Pobieranie 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 CloudSql. Poniższy skrypt konfiguracji przygotowuje dla Ciebie tę infrastrukturę. Proces obsługi administracyjnej potrwa ponad 10 minut. W trakcie przetwarzania możesz kontynuować wykonywanie kilku kolejnych czynności.
./setup.sh
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
Klonowanie przykładowej aplikacji
- Tworzenie aplikacji startowej
curl https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=11 -d packageName=com.example.springboot -o sample-app.zip
- Rozpakuj aplikację
unzip sample-app.zip -d sample-app
- Przejdź do katalogu sample-app i otwórz folder w obszarze roboczym IDE Cloud Shell
cd sample-app && cloudshell workspace .
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 ten kod po wierszu opisu, który brzmi: <description>Projekt demonstracyjny dla Spring Boot</description>
- Dodaj narzędzie spring-boot-devtools w pliku pom.xml
Otwórz plik 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ń w dół pliku pom.xml 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>
Jeśli pojawi się pytanie o zmianę pliku kompilacji, wybierz Always
.
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.
- 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 obszaru roboczego zostaną dodane 2 pliki: skaffold.yaml
i deployment.yaml
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 synchronizację z pamięcią
Aby zoptymalizować proces ponownego wczytywania z pamięci, użyjesz funkcji synchronizacji dostępnej 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.
- Zaktualizuj konfigurację skaffold
W pliku skaffold.yaml zastąp całą sekcję kompilacji w pliku podaną niżej specyfikacją. 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/java:debug
sync:
auto: true
Dodaj trasę domyślną
Utwórz plik o nazwie HelloController.java pod adresem /src/main/java/com/example/springboot/.
Wklej tę 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.
Wdróż w Kubernetes
- W panelu u dołu edytora Cloud Shell wybierz Cloud Code .
- W panelu, który pojawi się u góry, kliknij Debuguj w Kubernetes. W razie potrzeby wybierz Tak, aby użyć bieżącego kontekstu Kubernetes.
- Przy pierwszym uruchomieniu polecenia u góry ekranu pojawi się prompt z pytaniem, czy chcesz poznać bieżący kontekst Kubernetes. Wybierz „Tak”. aby zaakceptować i wykorzystać bieżący kontekst.
- Pojawi się pytanie, którego rejestru kontenerów użyć. Naciśnij Enter, aby zaakceptować podaną wartość domyślną
- 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.
- Wróć do widoku uproszczonego, wybierając „Kubernetes: Run/Debug”. z menu
- 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 w wyświetlonej wskazówce narzędzia wybierz Otwórz podgląd w przeglądarce.
Odpowiedź będzie:
Hello from your local environment!
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 zmiennego stanu piaskowego aplikacji, która działa zdalnie w GKE.
- Przejdź do sekcji zmiennych, aż znajdziesz sekcję „Wartość docelowa” .
- Obserwuj obecną wartość jako „local”
- Kliknij dwukrotnie zmienną o nazwie „target”. i w wyskakującym okienku zmień wartość na inną, np. „Cloud”.
- Kliknij przycisk Dalej w panelu sterowania debugowania.
- Sprawdź odpowiedź w przeglądarce, w której wyświetla się wprowadzona przed chwilą zaktualizowana wartość.
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
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 spoczynkowej łą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>
Zakoduj resztę usługi
Quote.java
Utwórz plik o nazwie cite.java w katalogu /src/main/java/com/example/springboot/ i skopiuj poniższy kod. Definiuje model encji dla obiektu Offer używanego w aplikacji.
package com.example.springboot;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
Utwórz plik o nazwie citeRepository.java na stronie src/main/java/com/example/springboot, a następnie 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 citeController.java na src/main/java/com/example/springboot, a następnie skopiuj go i wklej tę zawartość:
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
try {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (RuntimeException e) {
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
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
Utwórz folder pod adresem src/main/resources/db/migration/
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 swojej bazy danych
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
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”. - Wyświetl losowe cytaty
W terminalu Cloud Shell uruchom poniższe polecenie kilka razy i skieruj je do punktu końcowego losowego cudzysłowu. Zwróć uwagę na powtarzające się wywołanie zwracające różne cudzysłowy
curl -v 127.0.0.1:8080/random-quote
- Dodaj wycenę
Utwórz nową wycenę o wartości id=6 za pomocą polecenia wymienionego poniżej i sprawdź, czy żądanie jest powtarzane
curl -v -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST 127.0.0.1:8080/quotes
- Usuwanie wyceny
Teraz usuń dodany przed chwilą cytat za pomocą metody usuwania i obserwuj kod odpowiedzi HTTP/1.1 204
.
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Błąd serwera
Wystąpił błąd podczas ponownego uruchamiania ostatniego żądania po usunięciu wpisu
curl -v -X DELETE 127.0.0.1:8080/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 plik src.main.java.com.example.springboot.OfferController.java
- Znajdź metodę
deleteQuote()
- Znajdź wiersz, w którym usuń element z bazy danych:
quoteRepository.deleteById(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 127.0.0.1:8080/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
i zauważ, że został zgłoszony wyjątek. - Zwróć uwagę na bardzo ogólny atrybut
RuntimeException was caught.
. Powoduje to zwracanie klientowi wewnętrznego błędu 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 wyjątku, aby wychwytywać wyjątek EmptyResultDataAccessException
i odesłać kod stanu HTTP 404 (nie znaleziono).
Popraw błąd.
- Gdy sesja debugowania nadal działa, dokończ żądanie, naciskając „Dalej”. w panelu sterowania debugowania.
- Następnie dodaj do kodu ten blok:
} catch (EmptyResultDataAccessException e){
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Metoda powinna wyglądać mniej więcej tak:
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) { try { quoteRepository.deleteById(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch(EmptyResultDataAccessException e){ return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND); } catch (RuntimeException e) { System.out.println(e.getMessage()); return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); } }
- Ponownie uruchom polecenie usuwania
curl -v -X DELETE 127.0.0.1:8080/quotes/6
- Przejdź przez debuger i sprawdź, czy udało się wychwytywać kod
EmptyResultDataAccessException
, a uruchomienie zwraca błąd 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. Czyszczenie
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.
Aby posprzątać po ukończeniu modułu:
- Usuń pliki używane w module
cd ~ && rm -rf container-developer-workshop
- Usuń projekt, aby usunąć całą powiązaną infrastrukturę i zasoby