1. Panoramica
Questo lab mostra funzionalità e capacità progettate per semplificare il flusso di lavoro di sviluppo per gli ingegneri software incaricati di sviluppare applicazioni Java in un ambiente containerizzato. Il tipico sviluppo di container richiede all'utente di comprendere i dettagli dei container e il processo di compilazione dei container. Inoltre, gli sviluppatori in genere devono interrompere il flusso di lavoro, uscire dall'IDE per testare ed eseguire il debug delle applicazioni in ambienti remoti. Con gli strumenti e le tecnologie menzionati in questo tutorial, gli sviluppatori possono lavorare in modo efficace con le applicazioni in contenitori senza uscire dall'IDE.
Cosa imparerai a fare
In questo lab imparerai i metodi per sviluppare con i container in Google Cloud, tra cui:
- Sviluppo InnerLoop con Cloud Workstations
- Creazione di una nuova applicazione Java iniziale
- Esaminare il processo di sviluppo
- Sviluppo di un semplice servizio REST CRUD
- Eseguire il debug dell'applicazione sul cluster GKE
- Connessione dell'applicazione al database Cloud SQL

2. Configurazione e requisiti
Configurazione dell'ambiente autonomo
- Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.



- Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. È una stringa di caratteri non utilizzata dalle API di Google. Puoi aggiornarlo in qualsiasi momento.
- L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Cloud genera automaticamente una stringa univoca, di solito non ti interessa di cosa si tratta. Nella maggior parte dei codelab, devi fare riferimento all'ID progetto (in genere è identificato come
PROJECT_ID). Se non ti piace l'ID generato, puoi generarne un altro casuale. In alternativa, puoi provare a crearne uno e vedere se è disponibile. Non può essere modificato dopo questo passaggio e rimarrà per tutta la durata del progetto. - Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
- Successivamente, devi abilitare la fatturazione in Cloud Console per utilizzare le risorse/API Cloud. L'esecuzione di questo codelab non dovrebbe costare molto, se non nulla. Per arrestare le risorse in modo da non incorrere in costi di fatturazione al termine di questo tutorial, puoi eliminare le risorse che hai creato o l'intero progetto. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.
Avvia l'editor di Cloud Shell
Questo lab è stato progettato e testato per l'utilizzo con l'editor di Google Cloud Shell. Per accedere all'editor:
- accedi al tuo progetto Google all'indirizzo https://console.cloud.google.com.
- Nell'angolo in alto a destra, fai clic sull'icona dell'editor Cloud Shell.

- Si aprirà un nuovo riquadro nella parte inferiore della finestra.
- Fai clic sul pulsante Apri editor.

- L'editor si aprirà con un explorer a destra e l'editor nell'area centrale
- Nella parte inferiore dello schermo dovrebbe essere disponibile anche un riquadro del terminale
- Se il terminale NON è aperto, utilizza la combinazione di tasti "Ctrl + `" per aprire una nuova finestra del terminale.
Configura gcloud
In Cloud Shell, imposta l'ID progetto e la regione in cui vuoi eseguire il deployment dell'applicazione. Salvali come variabili PROJECT_ID e REGION.
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Clona il codice sorgente
Il codice sorgente di questo lab si trova in container-developer-workshop in GoogleCloudPlatform su GitHub. Clonalo con il comando riportato di seguito, quindi passa alla directory.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Esegui il provisioning dell'infrastruttura utilizzata in questo lab
In questo lab eseguirai il deployment del codice in GKE e accederai ai dati archiviati in un database CloudSQL. Lo script di configurazione riportato di seguito prepara questa infrastruttura per te. Il processo di provisioning richiederà più di 25 minuti. Attendi il completamento dello script prima di passare alla sezione successiva.
./setup_with_cw.sh &
Cluster Cloud Workstations
Apri Cloud Workstations in Cloud Console. Attendi che il cluster sia nello stato READY.
Crea configurazione workstation
Se la sessione di Cloud Shell è stata disconnessa, fai clic su "Riconnetti" e poi esegui il comando gcloud CLI per impostare l'ID progetto. Prima di eseguire il comando, sostituisci l'ID progetto di esempio riportato di seguito con il tuo ID progetto Qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
Esegui lo script riportato di seguito nel terminale per creare la configurazione di Cloud Workstations.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Verifica i risultati nella sezione Configurazioni. Il passaggio allo stato PRONTO richiederà 2 minuti.

Apri Cloud Workstations nella console e crea una nuova istanza.

Modifica il nome in my-workstation e seleziona la configurazione esistente: codeoss-java.

Verifica i risultati nella sezione Workstation.

Avvia workstation
Avvia la workstation.

Consenti i cookie di terze parti facendo clic sull'icona nella barra degli indirizzi. 

Fai clic su "Il sito non funziona?".

Fai clic su "Consenti cookie".

Una volta avviata la workstation, viene visualizzato l'IDE Code OSS. Fai clic su "Mark Done" (Segna come completato) nella pagina Getting Started (Guida introduttiva) dell'IDE della workstation.

3. Creazione di una nuova applicazione Java iniziale
In questa sezione creerai una nuova applicazione Java Spring Boot da zero utilizzando un'applicazione di esempio fornita da spring.io. Apri un nuovo terminale.

Clona l'applicazione di esempio
- Creare un'applicazione iniziale
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
Se visualizzi questo messaggio, fai clic sul pulsante Consenti per poter copiare e incollare nella workstation.

- Decomprimi l'applicazione
unzip sample-app.zip -d sample-app
- Apri la cartella "sample-app".
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Aggiungi spring-boot-devtools e Jib
Per attivare Spring Boot DevTools, trova e apri il file pom.xml dall'explorer nell'editor. Poi incolla il seguente codice dopo la riga descrittiva che riporta <description>Demo project for Spring Boot</description>
- Aggiungi spring-boot-devtools in pom.xml
Apri il file pom.xml nella directory root del progetto. Aggiungi la seguente configurazione dopo la voce 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>
- Abilita jib-maven-plugin in pom.xml
Jib è uno strumento open source di containerizzazione Java di Google che consente agli sviluppatori Java di creare container utilizzando gli strumenti Java che conoscono. Jib è un builder di immagini container veloce e semplice che gestisce tutti i passaggi di pacchettizzazione dell'applicazione in un'immagine container. Non richiede di scrivere un Dockerfile o di installare Docker ed è integrato direttamente in Maven e Gradle.
Scorri verso il basso nel file pom.xml e aggiorna la sezione Build per includere il plug-in Jib. Una volta completata, la sezione di creazione dovrebbe corrispondere a quella riportata di seguito.
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>
Genera manifest
Skaffold fornisce strumenti integrati per semplificare lo sviluppo dei container. In questo passaggio inizializzerai Skaffold, che creerà automaticamente i file YAML di base di Kubernetes. Il processo tenta di identificare le directory con definizioni di immagini container, come un Dockerfile, e poi crea un manifest di deployment e servizio per ciascuna.
Esegui il comando riportato di seguito nel terminale per iniziare la procedura.

- Esegui questo comando nel terminale
skaffold init --generate-manifests
- Quando richiesto:
- Usa le frecce per spostare il cursore su
Jib Maven Plugin. - Premi la barra spaziatrice per selezionare l'opzione.
- Premi Invio per continuare
- Inserisci 8080 per la porta.
- Inserisci y per salvare la configurazione.
Due file vengono aggiunti allo spazio di lavoro skaffold.yaml e deployment.yaml
Output di Skaffold:

Aggiorna il nome dell'app
I valori predefiniti inclusi nella configurazione non corrispondono attualmente al nome dell'applicazione. Aggiorna i file in modo che facciano riferimento al nome dell'applicazione anziché ai valori predefiniti.
- Modificare le voci nella configurazione di Skaffold
- Apri
skaffold.yaml - Seleziona il nome dell'immagine attualmente impostata come
pom-xml-image - Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze.
- Digita il nuovo nome come
demo-app
- Modificare le voci nella configurazione di Kubernetes
- Apri il file
deployment.yaml - Seleziona il nome dell'immagine attualmente impostata come
pom-xml-image - Fai clic con il tasto destro del mouse e scegli Modifica tutte le occorrenze.
- Digita il nuovo nome come
demo-app
Attivare la modalità Sincronizzazione automatica
Per facilitare un'esperienza di ricarica rapida ottimizzata, utilizzerai la funzionalità di sincronizzazione fornita da Jib. In questo passaggio configurerai Skaffold per utilizzare questa funzionalità nel processo di compilazione.
Tieni presente che il profilo "sync" che stai configurando nella configurazione di Skaffold utilizza il profilo "sync" di Spring che hai configurato nel passaggio precedente, in cui hai attivato il supporto per spring-dev-tools.
- Aggiorna la configurazione di Skaffold
Nel file skaffold.yaml, sostituisci l'intera sezione di compilazione del file con la seguente specifica. Non modificare altre sezioni del file.
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
Aggiungere una route predefinita
Crea un file denominato HelloController.java nella cartella /src/main/java/com/example/springboot/.

Incolla i seguenti contenuti nel file per creare una route HTTP predefinita.
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. Esaminare il processo di sviluppo
In questa sezione, seguirai alcuni passaggi utilizzando il plug-in Cloud Code per apprendere le procedure di base e convalidare la configurazione e la configurazione dell'applicazione iniziale.
Cloud Code si integra con Skaffold per semplificare il processo di sviluppo. Quando esegui il deployment in GKE nei passaggi successivi, Cloud Code e Skaffold creeranno automaticamente l'immagine container, eseguiranno il push in Container Registry e poi eseguiranno il deployment dell'applicazione in GKE. Questa operazione avviene dietro le quinte, astraendo i dettagli dal flusso dello sviluppatore. Cloud Code migliora anche il processo di sviluppo fornendo funzionalità di debug e sincronizzazione rapida tradizionali allo sviluppo basato su container.
Accedi a Google Cloud
Fai clic sull'icona di Cloud Code e seleziona "Accedi a Google Cloud":

Fai clic su "Procedi all'accesso".

Controlla l'output nel terminale e apri il link:

Accedi con le credenziali degli studenti Qwiklabs.

Seleziona "Consenti":

Copia il codice di verifica e torna alla scheda Workstation.

Incolla il codice di verifica e premi Invio.

Aggiungi cluster Kubernetes
- Aggiungere un cluster

- Seleziona Google Kubernetes Engine:

- Seleziona il progetto.

- Seleziona "quote-cluster" creato nella configurazione iniziale.


Imposta l'ID progetto corrente utilizzando gcloud CLI
Copia l'ID progetto per questo lab dalla pagina di Qwiklabs.

Esegui il comando gcloud CLI per impostare l'ID progetto. Sostituisci l'ID progetto di esempio prima di eseguire il comando.
gcloud config set project qwiklabs-gcp-project-id
Esempio di output:

Esegui il debug su Kubernetes
- Nel riquadro a sinistra in basso, seleziona Cloud Code.

- Nel riquadro visualizzato in SESSIONI DI SVILUPPO, seleziona Debug su Kubernetes.
Scorri verso il basso se l'opzione non è visibile.

- Seleziona "Sì" per utilizzare il contesto attuale.

- Seleziona "quote-cluster" creato durante la configurazione iniziale.

- Seleziona Container Repository.

- Seleziona la scheda Output nel riquadro inferiore per visualizzare l'avanzamento e le notifiche.
- Seleziona "Kubernetes: Run/Debug - Detailed" (Kubernetes: esecuzione/debug - dettagliato) nel menu a discesa del canale a destra per visualizzare ulteriori dettagli e i log in streaming live dai container.

Attendi il deployment dell'applicazione.

- Esamina l'applicazione di cui è stato eseguito il deployment su GKE in Cloud Console.

- Torna alla visualizzazione semplificata selezionando "Kubernetes: Run/Debug" (Kubernetes: esegui/debug) dal menu a discesa nella scheda OUTPUT.
- Al termine della build e dei test, nella scheda Output viene visualizzato il messaggio
Resource deployment/demo-app status completed successfullye viene elencato un URL: "Forwarded URL from service demo-app: http://localhost:8080" - Nel terminale di Cloud Code, passa il mouse sopra l'URL nell'output (http://localhost:8080), poi seleziona Segui link nel suggerimento visualizzato.

Si aprirà una nuova scheda e vedrai l'output riportato di seguito:

Utilizzare i punti di interruzione
- Apri l'applicazione
HelloController.javache si trova in/src/main/java/com/example/springboot/HelloController.java - Individua la dichiarazione return per il percorso principale che indica
return String.format("Hello from your %s environment!", target); - Aggiungi un punto di interruzione a quella riga facendo clic sullo spazio vuoto a sinistra del numero di riga. Viene visualizzato un indicatore rosso per indicare che il punto di interruzione è impostato

- Ricarica il browser e nota che il debugger interrompe il processo nel punto di interruzione e ti consente di esaminare le variabili e lo stato dell'applicazione in esecuzione in remoto in GKE

- Fai clic nella sezione delle variabili fino a trovare la variabile "Target".
- Osserva il valore corrente "local"

- Fai doppio clic sul nome della variabile "target" e nel popup,
modifica il valore in "Cloud Workstations"

- Fai clic sul pulsante Continua nel pannello di controllo del debug

- Controlla la risposta nel browser, che ora mostra il valore aggiornato appena inserito.

- Rimuovi il punto di interruzione facendo clic sull'indicatore rosso a sinistra del numero di riga. In questo modo, il codice non interromperà l'esecuzione a questa riga man mano che avanzi in questo lab.
Ricaricamento rapido
- Modifica l'istruzione in modo che restituisca un valore diverso, ad esempio "Hello from %s Code"
- Il file viene salvato e sincronizzato automaticamente nei container remoti in GKE
- Aggiorna il browser per visualizzare i risultati aggiornati.
- Interrompi la sessione di debug facendo clic sul quadrato rosso nella barra degli strumenti di debug.

Seleziona "Sì, libera spazio dopo ogni esecuzione".

5. Sviluppo di un semplice servizio REST CRUD
A questo punto, l'applicazione è completamente configurata per lo sviluppo containerizzato e hai esaminato il flusso di lavoro di sviluppo di base con Cloud Code. Nelle sezioni seguenti metterai in pratica ciò che hai imparato aggiungendo endpoint di servizio REST che si connettono a un database gestito in Google Cloud.
Configura le dipendenze
Il codice dell'applicazione utilizza un database per archiviare i dati del servizio REST. Assicurati che le dipendenze siano disponibili aggiungendo quanto segue in pom.xl
- Apri il file
pom.xmle aggiungi quanto segue alla sezione delle dipendenze della configurazione
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>
Servizio REST di codice
Quote.java
Crea un file denominato Quote.java in /src/main/java/com/example/springboot/ e copia il codice riportato di seguito. Definisce il modello di entità per l'oggetto Quote utilizzato nell'applicazione.
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
Crea un file chiamato QuoteRepository.java in src/main/java/com/example/springboot e copia il seguente codice
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();
}
Questo codice utilizza JPA per rendere persistenti i dati. La classe estende l'interfaccia Spring JPARepository e consente la creazione di codice personalizzato. Nel codice che hai aggiunto, è presente un metodo personalizzato findRandomQuote.
QuoteController.java
Per esporre l'endpoint per il servizio, una classe QuoteController fornirà questa funzionalità.
Crea un file denominato QuoteController.java in src/main/java/com/example/springboot e copia i seguenti contenuti
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);
}
}
}
Aggiungere configurazioni del database
application.yaml
Aggiungi la configurazione per il database di backend a cui accede il servizio. Modifica (o crea se non presente) il file application.yaml in src/main/resources e aggiungi una configurazione Spring parametrizzata per il backend.
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
Aggiungi migrazione del database
Crea cartelle db/migration in src/main/resources
Crea un file SQL: V1__create_quotes_table.sql
Incolla i seguenti contenuti nel file
V1__create_quotes_table.sql
CREATE TABLE quotes(
id INTEGER PRIMARY KEY,
quote VARCHAR(1024),
author VARCHAR(256)
);
INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');
Kubernetes Config
Le seguenti aggiunte al file deployment.yaml consentono all'applicazione di connettersi alle istanze Cloud SQL.
- TARGET: configura la variabile per indicare l'ambiente in cui viene eseguita l'app
- SPRING_PROFILES_ACTIVE: mostra il profilo Spring attivo, che verrà configurato su
cloud-dev - DB_HOST: l'IP privato per il database, annotato durante la creazione dell'istanza del database o facendo clic su
SQLnel menu di navigazione di console Google Cloud. Modifica il valore. - DB_USER e DB_PASS, come impostati nella configurazione dell'istanza Cloud SQL, memorizzati come secret in GCP
Aggiorna il file deployment.yaml con i contenuti riportati di seguito.
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
Sostituisci il valore DB_HOST con l'indirizzo del tuo database eseguendo i seguenti comandi nel terminale:
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
Apri deployment.yaml e verifica che il valore DB_HOST sia stato aggiornato con l'IP dell'istanza.

Esegui il deployment e convalida l'applicazione
- Nel riquadro in basso dell'editor di Cloud Shell, seleziona Cloud Code, quindi seleziona Debug su Kubernetes nella parte superiore dello schermo.

- Al termine della build e dei test, nella scheda Output viene visualizzato il messaggio
Resource deployment/demo-app status completed successfullye un URL: "Forwarded URL from service demo-app: http://localhost:8080". Tieni presente che a volte la porta potrebbe essere diversa, ad esempio 8081. In caso affermativo, imposta il valore appropriato. Imposta il valore dell'URL nel terminale
export URL=localhost:8080
- Visualizzare citazioni casuali
Dal terminale, esegui più volte il comando riportato di seguito sull'endpoint random-quote. Osserva le chiamate ripetute che restituiscono preventivi diversi
curl $URL/random-quote | jq
- Aggiungere una citazione
Crea una nuova citazione con id=6 utilizzando il comando elencato di seguito e osserva la richiesta che viene restituita
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
- Eliminare un preventivo
Ora elimina la citazione che hai appena aggiunto con il metodo di eliminazione e osserva un codice di risposta HTTP/1.1 204.
curl -v -X DELETE $URL/quotes/6
- Errore del server
Visualizzare uno stato di errore eseguendo di nuovo l'ultima richiesta dopo che la voce è già stata eliminata
curl -v -X DELETE $URL/quotes/6
Nota che la risposta restituisce un HTTP:500 Internal Server Error.
Esegui il debug dell'applicazione
Nella sezione precedente hai riscontrato uno stato di errore nell'applicazione quando hai tentato di eliminare una voce che non era presente nel database. In questa sezione imposterai un punto di interruzione per individuare il problema. L'errore si è verificato nell'operazione DELETE, quindi lavorerai con la classe QuoteController.
- Apri
src/main/java/com/example/springboot/QuoteController.java - Trova il metodo
deleteQuote() - Trova la riga:
Optional<Quote> quote = quoteRepository.findById(id); - Imposta un punto di interruzione su quella riga facendo clic sullo spazio vuoto a sinistra del numero di riga.
- Verrà visualizzato un indicatore rosso che indica che il punto di interruzione è impostato.
- Esegui di nuovo il comando
delete.
curl -v -X DELETE $URL/quotes/6
- Torna alla visualizzazione di debug facendo clic sull'icona nella colonna a sinistra.
- Osserva la riga di debug interrotta nella classe QuoteController.
- Nel debugger, fai clic sull'icona
step over
. - Tieni presente che un codice restituisce un errore interno del server HTTP 500 al client, il che non è l'ideale.
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
Aggiorna il codice
Il codice non è corretto e il blocco else deve essere sottoposto a refactoring per restituire un codice di stato HTTP 404 pagina non trovata.
Correggi l'errore.
- Con la sessione di debug ancora in esecuzione, completa la richiesta premendo il pulsante "Continua" nel pannello di controllo del debug.
- Poi modifica il blocco
elsecon il codice:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
Il metodo dovrebbe avere il seguente aspetto
@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);
}
}
- Esegui di nuovo il comando di eliminazione
curl -v -X DELETE $URL/quotes/6
- Esegui il debug e osserva l'errore HTTP 404 Not Found restituito al chiamante.
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
- Interrompi la sessione di debug facendo clic sul quadrato rosso nella barra degli strumenti di debug.


6. Complimenti
Complimenti! In questo lab hai creato una nuova applicazione Java da zero e l'hai configurata per funzionare in modo efficace con i container. Successivamente, hai eseguito il deployment e il debug dell'applicazione in un cluster GKE remoto seguendo lo stesso flusso di sviluppo presente negli stack di applicazioni tradizionali.
Cosa hai imparato
- Sviluppo InnerLoop con Cloud Workstations
- Creazione di una nuova applicazione Java iniziale
- Esaminare il processo di sviluppo
- Sviluppare un semplice servizio REST CRUD
- Eseguire il debug dell'applicazione sul cluster GKE
- Connessione dell'applicazione al database Cloud SQL
