1. Présentation
Cet atelier présente des fonctionnalités conçues pour simplifier le workflow de développement des ingénieurs logiciels chargés de développer des applications Java dans un environnement conteneurisé. Le développement de conteneurs typique exige de l'utilisateur qu'il comprenne les détails des conteneurs et du processus de création de conteneurs. De plus, les développeurs doivent généralement interrompre leur flux de travail et quitter leur IDE pour tester et déboguer leurs applications dans des environnements distants. Grâce aux outils et technologies mentionnés dans ce tutoriel, les développeurs peuvent travailler efficacement avec des applications conteneurisées sans quitter leur IDE.
Objectifs de l'atelier
Dans cet atelier, vous allez découvrir des méthodes de développement avec des conteneurs dans GCP, y compris :
- Développement InnerLoop avec Cloud Workstations
- Créer une application de démarrage Java
- Parcourir le processus de développement
- Développer un service REST CRUD simple
- Déboguer une application sur un cluster GKE
- Connecter l'application à la base de données Cloud SQL

2. Préparation
Configuration de l'environnement d'auto-formation
- Connectez-vous à la console Google Cloud, puis créez un projet ou réutilisez un projet existant. (Si vous ne possédez pas encore de compte Gmail ou Google Workspace, vous devez en créer un.)



- Le nom du projet est le nom à afficher pour les participants au projet. Il s'agit d'une chaîne de caractères non utilisée par les API Google. Vous pouvez le modifier à tout moment.
- L'ID du projet est unique parmi tous les projets Google Cloud et non modifiable une fois défini. La console Cloud génère automatiquement une chaîne unique (en général, vous n'y accordez d'importance particulière). Dans la plupart des ateliers de programmation, vous devrez indiquer l'ID du projet (généralement identifié par
PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre de manière aléatoire. Vous pouvez également en spécifier un et voir s'il est disponible. Après cette étape, l'ID n'est plus modifiable et restera donc le même pour toute la durée du projet. - Pour information, il existe une troisième valeur (le numéro de projet) que certaines API utilisent. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
- Vous devez ensuite activer la facturation dans la console Cloud pour utiliser les ressources/API Cloud. L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Pour désactiver les ressources et éviter ainsi que des frais ne vous soient facturés après ce tutoriel, vous pouvez supprimer le projet ou les ressources que vous avez créées. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300$.
Démarrer l'éditeur Cloudshell
Cet atelier a été conçu et testé pour être utilisé avec l'éditeur Cloud Shell. Pour accéder à l'éditeur :
- Accédez à votre projet Google à l'adresse https://console.cloud.google.com.
- En haut à droite, cliquez sur l'icône de l'éditeur Cloud Shell.

- Un nouveau volet s'ouvre en bas de la fenêtre.
- Cliquez sur le bouton "Ouvrir l'éditeur".

- L'éditeur s'ouvre avec un explorateur à droite et un éditeur dans la zone centrale.
- Un volet de terminal doit également être disponible en bas de l'écran.
- Si le terminal n'est PAS ouvert, utilisez la combinaison de touches "ctrl+`" pour ouvrir une nouvelle fenêtre de terminal.
Configurer gcloud
Dans Cloud Shell, définissez votre ID de projet et la région dans laquelle vous souhaitez déployer votre application. Enregistrez-les en tant que variables PROJECT_ID et REGION.
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
Cloner le code source
Le code source de cet atelier se trouve dans container-developer-workshop dans GoogleCloudPlatform sur GitHub. Clonez-le à l'aide de la commande ci-dessous, puis accédez au répertoire.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
Provisionner l'infrastructure utilisée dans cet atelier
Dans cet atelier, vous allez déployer du code sur GKE et accéder aux données stockées dans une base de données Cloud SQL. Le script de configuration ci-dessous prépare cette infrastructure pour vous. Le processus de provisionnement prendra plus de 25 minutes. Attendez la fin de l'exécution du script avant de passer à la section suivante.
./setup_with_cw.sh &
Cluster Cloud Workstations
Ouvrez Cloud Workstations dans la console Cloud. Attendez que le cluster soit à l'état READY.
Créer une configuration de stations de travail
Si votre session Cloud Shell a été déconnectée, cliquez sur "Se reconnecter", puis exécutez la commande gcloud CLI pour définir l'ID du projet. Avant d'exécuter la commande, remplacez l'ID de projet exemple ci-dessous par l'ID de votre projet Qwiklabs.
gcloud config set project qwiklabs-gcp-project-id
Exécutez le script ci-dessous dans le terminal pour créer la configuration Cloud Workstations.
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
Vérifiez les résultats dans la section "Configurations". Le passage à l'état PRÊT prendra deux minutes.

Ouvrez Cloud Workstations dans la console et créez une instance.

Remplacez le nom par my-workstation et sélectionnez la configuration existante : codeoss-java.

Vérifiez les résultats dans la section "Postes de travail".

Lancer la station de travail
Démarrez et lancez la station de travail.

Autorisez les cookies tiers en cliquant sur l'icône dans la barre d'adresse. 

Cliquez sur "Impossible d'accéder au site ?".

Cliquez sur "Autoriser les cookies".

Une fois la station de travail lancée, l'IDE Code OSS s'affiche. Cliquez sur "Marquer comme terminé" sur la page "Premiers pas" de l'IDE de la station de travail.

3. Créer une application de démarrage Java
Dans cette section, vous allez créer une application Java Spring Boot à partir de zéro en utilisant un exemple d'application fourni par spring.io. Ouvrez un nouveau terminal.

Cloner l'exemple d'application
- Créer une application de démarrage
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
Si ce message s'affiche, cliquez sur le bouton "Autoriser" pour pouvoir copier et coller le texte dans la station de travail.

- Décompresser l'application
unzip sample-app.zip -d sample-app
- Ouvrez le dossier "sample-app".
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
Ajouter spring-boot-devtools et Jib
Pour activer Spring Boot DevTools, recherchez et ouvrez le fichier pom.xml à partir de l'explorateur de votre éditeur. Collez ensuite le code suivant après la ligne de description <description>Demo project for Spring Boot</description>.
- Ajoutez spring-boot-devtools dans pom.xml
Ouvrez le fichier pom.xml à la racine du projet. Ajoutez la configuration suivante après l'entrée 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>
- Activer jib-maven-plugin dans pom.xml
Jib est un outil de conteneurisation Java Open Source de Google qui permet aux développeurs Java de créer des conteneurs à l'aide des outils Java qu'ils connaissent. Jib est un outil de création d'images de conteneurs rapide et simple qui gère toutes les étapes de l'empaquetage de votre application dans une image de conteneur. Il ne nécessite pas d'écrire un fichier Dockerfile ni d'installer Docker, et il est directement intégré à Maven et Gradle.
Faites défiler le fichier pom.xml vers le bas et mettez à jour la section Build pour inclure le plug-in Jib. Une fois la section "Build" terminée, elle doit correspondre à ce qui suit.
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>
Générer des fichiers manifestes
Skaffold fournit des outils intégrés pour simplifier le développement de conteneurs. Dans cette étape, vous allez initialiser Skaffold, ce qui créera automatiquement des fichiers YAML Kubernetes de base. Le processus tente d'identifier les répertoires contenant des définitions d'images de conteneurs, comme un fichier Dockerfile, puis crée un fichier manifeste de déploiement et de service pour chacun d'eux.
Exécutez la commande ci-dessous dans le terminal pour commencer le processus.

- Exécutez la commande suivante dans le terminal.
skaffold init --generate-manifests
- Lorsque vous y êtes invité :
- Utilisez les flèches pour déplacer le curseur sur
Jib Maven Plugin. - Appuyez sur la barre d'espace pour sélectionner l'option.
- Appuyez sur Entrée pour continuer
- Saisissez 8080 pour le port.
- Saisissez y pour enregistrer la configuration.
Deux fichiers sont ajoutés à l'espace de travail : skaffold.yaml et deployment.yaml.
Résultat Skaffold :

Modifier le nom de l'application
Les valeurs par défaut incluses dans la configuration ne correspondent pas au nom de votre application. Mettez à jour les fichiers pour qu'ils fassent référence au nom de votre application plutôt qu'aux valeurs par défaut.
- Modifier des entrées dans la configuration Skaffold
- Ouvrir
skaffold.yaml - Sélectionnez le nom de l'image actuellement défini sur
pom-xml-image. - Effectuez un clic droit et sélectionnez "Modifier toutes les occurrences".
- Saisissez le nouveau nom, par exemple
demo-app.
- Modifier les entrées de la configuration Kubernetes
- Ouvrir le fichier
deployment.yaml - Sélectionnez le nom de l'image actuellement défini sur
pom-xml-image. - Effectuez un clic droit et sélectionnez "Modifier toutes les occurrences".
- Saisissez le nouveau nom, par exemple
demo-app.
Activer le mode Synchronisation automatique
Pour faciliter une expérience de rechargement à chaud optimisée, vous utiliserez la fonctionnalité de synchronisation fournie par Jib. Au cours de cette étape, vous allez configurer Skaffold pour utiliser cette fonctionnalité dans le processus de compilation.
Notez que le profil "sync" que vous configurez dans la configuration Skaffold utilise le profil Spring "sync" que vous avez configuré à l'étape précédente, où vous avez activé la prise en charge de spring-dev-tools.
- Mettre à jour la configuration Skaffold
Dans le fichier skaffold.yaml, remplacez l'intégralité de la section de compilation par la spécification suivante. Ne modifiez pas les autres sections du fichier.
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
Ajouter une route par défaut
Créez un fichier nommé HelloController.java dans le dossier /src/main/java/com/example/springboot/.

Collez le contenu suivant dans le fichier pour créer une route HTTP par défaut.
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. Parcourir le processus de développement
Dans cette section, vous allez suivre quelques étapes à l'aide du plug-in Cloud Code pour découvrir les processus de base et valider la configuration de votre application de démarrage.
Cloud Code s'intègre à Skaffold pour simplifier votre processus de développement. Lorsque vous déployez sur GKE lors des étapes suivantes, Cloud Code et Skaffold créent automatiquement votre image de conteneur, la transfèrent vers Container Registry, puis déploient votre application sur GKE. Cela se produit en arrière-plan, en masquant les détails du flux de développement. Cloud Code améliore également votre processus de développement en fournissant des fonctionnalités de débogage et de synchronisation à chaud traditionnelles pour le développement basé sur des conteneurs.
Se connecter à Google Cloud
Cliquez sur l'icône Cloud Code, puis sélectionnez "Se connecter à Google Cloud" :

Cliquez sur "Continuer".

Vérifiez le résultat dans le terminal et ouvrez le lien :

Connectez-vous avec vos identifiants Qwiklabs pour les étudiants.

Sélectionnez "Autoriser" :

Copiez le code de validation et revenez à l'onglet "Poste de travail".

Collez le code de validation, puis appuyez sur Entrée.

Ajouter un cluster Kubernetes
- Ajouter un cluster

- Sélectionnez Google Kubernetes Engine :

- Sélectionnez un projet.

- Sélectionnez "quote-cluster" créé lors de la configuration initiale.


Définir l'ID du projet actuel à l'aide de gcloud CLI
Copiez l'ID du projet pour cet atelier à partir de la page Qwiklabs.

Exécutez la commande gcloud CLI pour définir l'ID du projet. Remplacez l'ID du projet exemple avant d'exécuter la commande.
gcloud config set project qwiklabs-gcp-project-id
Exemple de résultat :

Déboguer sur Kubernetes
- Dans le volet de gauche, en bas, sélectionnez Cloud Code.

- Dans le panneau qui s'affiche sous "SESSIONS DE DÉVELOPPEMENT", sélectionnez "Déboguer sur Kubernetes".
Faites défiler l'écran vers le bas si l'option n'est pas visible.

- Sélectionnez "Oui" pour utiliser le contexte actuel.

- Sélectionnez "quote-cluster" créé lors de la configuration initiale.

- Sélectionnez "Dépôt de conteneurs".

- Sélectionnez l'onglet "Sortie" dans le volet inférieur pour afficher la progression et les notifications.
- Sélectionnez "Kubernetes: Run/Debug - Detailed" (Kubernetes : Exécuter/Déboguer – Détails) dans le menu déroulant à droite pour afficher des informations supplémentaires et les journaux diffusés en direct depuis les conteneurs.

Attendez que l'application soit déployée.

- Examinez l'application déployée sur GKE dans la console Cloud.

- Pour revenir à la vue simplifiée, sélectionnez "Kubernetes : Exécuter/Déboguer" dans le menu déroulant de l'onglet OUTPUT (SORTIE).
- Une fois la compilation et les tests terminés, l'onglet "Résultat" indique
Resource deployment/demo-app status completed successfullyet une URL est listée : "URL transférée depuis l'application de démonstration du service : http://localhost:8080". - Dans le terminal Cloud Code, pointez sur l'URL dans la sortie (http://localhost:8080), puis sélectionnez "Suivre le lien" dans l'info-bulle qui s'affiche.

Un nouvel onglet s'ouvre et le résultat suivant s'affiche :

Utiliser les points d'arrêt
- Ouvrez l'application
HelloController.javasituée sous/src/main/java/com/example/springboot/HelloController.java. - Recherchez l'instruction de retour pour le chemin racine, qui se lit
return String.format("Hello from your %s environment!", target);. - Ajoutez un point d'arrêt à cette ligne en cliquant sur l'espace vide à gauche du numéro de ligne. Un indicateur rouge s'affiche pour indiquer que le point d'arrêt est défini.

- Rechargez votre navigateur et notez que le débogueur arrête le processus au point d'arrêt et vous permet d'examiner les variables et l'état de l'application qui s'exécute à distance dans GKE.

- Cliquez sur la section des variables jusqu'à ce que vous trouviez la variable "Cible".
- Notez que la valeur actuelle est "local".

- Double-cliquez sur le nom de la variable "target" (cible), puis dans le pop-up,
remplacer la valeur par "Cloud Workstations"

- Cliquez sur le bouton "Continuer" dans le panneau de configuration du débogueur.

- Examinez la réponse dans votre navigateur. Elle affiche désormais la valeur modifiée que vous venez de saisir.

- Supprimez le point d'arrêt en cliquant sur l'indicateur rouge à gauche du numéro de ligne. Cela empêchera votre code d'arrêter l'exécution à cette ligne à mesure que vous progresserez dans cet atelier.
Hot reload
- Modifiez l'instruction pour renvoyer une autre valeur, par exemple "Bonjour depuis %s Code".
- Le fichier est automatiquement enregistré et synchronisé dans les conteneurs distants de GKE.
- Actualisez votre navigateur pour afficher les résultats mis à jour.
- Arrêtez la session de débogage en cliquant sur le carré rouge dans la barre d'outils de débogage.

Sélectionnez "Oui, nettoyer après chaque exécution".

5. Développer un service REST CRUD simple
À ce stade, votre application est entièrement configurée pour le développement conteneurisé et vous avez parcouru le workflow de développement de base avec Cloud Code. Dans les sections suivantes, vous allez mettre en pratique ce que vous avez appris en ajoutant des points de terminaison de service REST qui se connectent à une base de données gérée dans Google Cloud.
Configurer les dépendances
Le code de l'application utilise une base de données pour conserver les données du service REST. Assurez-vous que les dépendances sont disponibles en ajoutant les éléments suivants dans le fichier pom.xl.
- Ouvrez le fichier
pom.xmlet ajoutez les éléments suivants à la section des dépendances de la configuration.
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>
Coder un service REST
Quote.java
Créez un fichier appelé Quote.java dans /src/main/java/com/example/springboot/ et copiez-y le code ci-dessous. Cela définit le modèle d'entité pour l'objet "Quote" (Devis) utilisé dans l'application.
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
Créez un fichier appelé QuoteRepository.java dans src/main/java/com/example/springboot et copiez-y le code suivant.
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();
}
Ce code utilise JPA pour conserver les données. La classe étend l'interface Spring JPARepository et permet de créer du code personnalisé. Dans le code, vous avez ajouté une méthode personnalisée findRandomQuote.
QuoteController.java
Pour exposer le point de terminaison du service, une classe QuoteController fournira cette fonctionnalité.
Créez un fichier nommé QuoteController.java à l'emplacement src/main/java/com/example/springboot et copiez-y le contenu suivant :
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);
}
}
}
Ajouter des configurations de base de données
application.yaml
Ajoutez la configuration de la base de données de backend à laquelle le service accède. Modifiez (ou créez, le cas échéant) le fichier application.yaml sous src/main/resources et ajoutez une configuration Spring paramétrée pour le 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
Ajouter la migration de bases de données
Créez des dossiers db/migration sous src/main/resources.
Créez un fichier SQL : V1__create_quotes_table.sql
Collez le contenu suivant dans le fichier.
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');
Configuration Kubernetes
Les ajouts suivants au fichier deployment.yaml permettent à l'application de se connecter aux instances Cloud SQL.
- TARGET : configure la variable pour indiquer l'environnement dans lequel l'application est exécutée.
- SPRING_PROFILES_ACTIVE : affiche le profil Spring actif, qui sera configuré sur
cloud-dev - DB_HOST : adresse IP privée de la base de données, qui a été notée lors de la création de l'instance de base de données ou en cliquant sur
SQLdans le menu de navigation de la console Google Cloud. Veuillez modifier la valeur. - DB_USER et DB_PASS : tels qu'ils sont définis dans la configuration de l'instance Cloud SQL, stockés en tant que secret dans GCP
Mettez à jour votre fichier deployment.yaml avec le contenu ci-dessous.
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
Remplacez la valeur DB_HOST par l'adresse de votre base de données en exécutant les commandes ci-dessous dans le terminal :
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
Ouvrez deployment.yaml et vérifiez que la valeur DB_HOST a été mise à jour avec l'adresse IP de l'instance.

Déployer et valider l'application
- Dans le volet situé au bas de l'éditeur Cloud Shell, sélectionnez Cloud Code, puis Déboguer sur Kubernetes en haut de l'écran.

- Une fois la compilation et les tests terminés, l'onglet "Résultat" indique
Resource deployment/demo-app status completed successfullyet affiche l'URL "URL transférée depuis la démo de service : http://localhost:8080". Notez que le port peut parfois être différent, par exemple 8081. Si c'est le cas, définissez la valeur appropriée. Définir la valeur de l'URL dans le terminal
export URL=localhost:8080
- Afficher des citations aléatoires
Dans le terminal, exécutez plusieurs fois la commande ci-dessous sur le point de terminaison random-quote. Observer des appels répétés renvoyant des citations différentes
curl $URL/random-quote | jq
- Ajouter un devis
Créez un devis avec l'ID 6 à l'aide de la commande ci-dessous et observez la demande renvoyée.
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
- Supprimer un devis
Supprimez maintenant la citation que vous venez d'ajouter avec la méthode de suppression et observez un code de réponse HTTP/1.1 204.
curl -v -X DELETE $URL/quotes/6
- Erreur du serveur
Découvrez un état d'erreur en exécutant à nouveau la dernière requête après la suppression de l'entrée.
curl -v -X DELETE $URL/quotes/6
Notez que la réponse renvoie un HTTP:500 Internal Server Error.
Déboguer l'application
Dans la section précédente, vous avez rencontré un état d'erreur dans l'application lorsque vous avez essayé de supprimer une entrée qui ne figurait pas dans la base de données. Dans cette section, vous allez définir un point d'arrêt pour identifier le problème. L'erreur s'est produite lors de l'opération DELETE. Vous allez donc travailler avec la classe QuoteController.
- Ouvrir
src/main/java/com/example/springboot/QuoteController.java - Recherchez la méthode
deleteQuote(). - Recherchez la ligne suivante :
Optional<Quote> quote = quoteRepository.findById(id); - Définissez un point d'arrêt sur cette ligne en cliquant sur l'espace vide à gauche du numéro de ligne.
- Un indicateur rouge s'affiche pour indiquer que le point d'arrêt est défini.
- Exécutez à nouveau la commande
delete.
curl -v -X DELETE $URL/quotes/6
- Revenez à la vue Débogage en cliquant sur l'icône dans la colonne de gauche.
- Observez la ligne de débogage arrêtée dans la classe QuoteController.
- Dans le débogueur, cliquez sur l'icône
step over
. - Notez qu'un code renvoie une erreur HTTP 500 (Internal Server Error) au client, ce qui n'est pas idéal.
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
Mettre à jour le code
Le code est incorrect et le bloc else doit être refactorisé pour renvoyer un code d'état HTTP 404 (page introuvable).
Corrigez l'erreur.
- La session de débogage étant toujours en cours d'exécution, terminez la requête en appuyant sur le bouton "Continuer" dans le panneau de configuration du débogage.
- Ensuite, remplacez le bloc
elsepar le code suivant :
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
La méthode devrait se présenter comme suit :
@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);
}
}
- Réexécuter la commande de suppression
curl -v -X DELETE $URL/quotes/6
- Parcourez le débogueur et observez le code HTTP 404 Not Found renvoyé à l'appelant.
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
- Arrêtez la session de débogage en cliquant sur le carré rouge dans la barre d'outils de débogage.


6. Félicitations
Félicitations ! Dans cet atelier, vous avez créé une application Java à partir de zéro et l'avez configurée pour qu'elle fonctionne efficacement avec les conteneurs. Vous avez ensuite déployé et débogué votre application sur un cluster GKE distant en suivant le même flux de développement que celui utilisé dans les piles d'applications traditionnelles.
Ce que vous avez appris
- Développement InnerLoop avec Cloud Workstations
- Créer une application de démarrage Java
- Parcourir le processus de développement
- Développer un service REST CRUD simple
- Déboguer une application sur un cluster GKE
- Connecter l'application à la base de données Cloud SQL
