Développer avec Cloud Workstations et Cloud Code

1. Présentation

Cet atelier présente des fonctionnalités conçues pour simplifier le workflow de développement pour les ingénieurs logiciel chargés de développer des applications Java dans un environnement conteneurisé. En règle générale, le développement de conteneurs nécessite que l'utilisateur comprenne les détails des conteneurs et le processus de création de conteneurs. De plus, les développeurs doivent généralement interrompre leur flux et sortir de 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

58a4cdd3ed7a123a.png

2. Préparation

Configuration de l'environnement d'auto-formation

  1. 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.)

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 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. généralement, vous ne vous souciez pas de ce que c’est. Dans la plupart des ateliers de programmation, vous devrez référencer l'ID du projet (il est généralement identifié comme PROJECT_ID). Si l'ID généré ne vous convient pas, vous pouvez en générer un autre au hasard. Vous pouvez également essayer la vôtre pour voir si elle est disponible. Il ne peut pas être modifié après cette étape et restera actif pendant toute la durée du projet.
  • Pour votre information, il existe une troisième valeur, le numéro de projet, utilisé par certaines API. Pour en savoir plus sur ces trois valeurs, consultez la documentation.
  1. 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 arrêter les ressources afin d'éviter que des frais ne vous soient facturés au-delà de ce tutoriel, vous pouvez supprimer les ressources que vous avez créées ou l'ensemble du projet. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai gratuit 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 Google Cloud Shell. Pour accéder à l'éditeur,

  1. Accédez à votre projet Google à l'adresse https://console.cloud.google.com.
  2. En haut à droite, cliquez sur l'icône de l'éditeur Cloud Shell.

8560cc8d45e8c112.png

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

9e504cb98a6a8005.png

  1. L'éditeur s'ouvre avec un explorateur à droite et un éditeur dans la zone centrale
  2. Un volet de terminal devrait également être disponible au bas de l'écran.
  3. 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 l'ID de votre 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 l'atelier "container-developer-workshop" de 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 que le script se termine avant de passer à la section suivante.

./setup_with_cw.sh &

Cluster Cloud Workstations

Ouvrez Cloud Workstations dans la console Cloud. Attendez que le cluster affiche l'état READY.

305e1a3d63ac7ff6.png

Créer une configuration de stations de travail

Si votre session Cloud Shell a été déconnectée, cliquez sur "Reconnecter". puis exécutez la commande gcloud cli pour définir l'ID du projet. Remplacez l'exemple d'ID de projet ci-dessous par l'ID de votre projet Qwiklabs avant d'exécuter la commande.

gcloud config set project qwiklabs-gcp-project-id

Exécutez le script ci-dessous dans le terminal pour créer une configuration Cloud Workstations.

cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh

Vérifiez les résultats dans la section "Configurations". La transition vers l'état PRÊT prend deux minutes.

7a6af5aa2807a5f2.png

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

a53adeeac81a78c8.png

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

f21c216997746097.png

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

66a9fc8b20543e32.png

Lancer la station de travail

Démarrez et lancez la station de travail.

c91bb69b61ec8635.png

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

fcf9405b6957b7d7.png

Cliquez sur "Site inactif ?".

36a84c0e2e3b85b.png

Cliquez sur "Autoriser les cookies".

2259694328628fba.png

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

94874fba9b74cc22.png

3. Créer une application de démarrage Java

Dans cette section, vous allez créer entièrement une application Java Spring Boot à l'aide d'un exemple d'application fourni par print.io. Ouvrez un nouveau terminal.

c31d48f2e4938c38.png

Cloner l'exemple d'application

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

Cliquez sur le bouton "Allow" (Autoriser) si ce message s'affiche pour que vous puissiez copier et coller le contenu sur le poste de travail.

58149777e5cc350a.png

  1. Décompresser l'application
unzip sample-app.zip -d sample-app
  1. Ouvrez l'exemple d'application. dossier
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"

Ajoutez des outils de développement Jab

Pour activer les outils de développement Spring Boot, recherchez et ouvrez le fichier pom.xml depuis l'explorateur de votre éditeur. Collez ensuite le code suivant après la ligne de description <description>Demo project for Spring Boot</description>.

  1. Ajoutez "Spring-boot-devtools" dans le fichier 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>
  1. 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 générateur d'images de conteneurs rapide et simple qui gère toutes les étapes du packaging de votre application dans une image de conteneur. Vous n'avez pas besoin d'écrire un fichier Dockerfile ni d'avoir installé Docker. De plus, ce fichier 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 l'opération terminée, la section de compilation 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. Au cours de cette étape, vous allez initialiser Skaffold, qui créera automatiquement des fichiers YAML Kubernetes de base. Le processus tente d'identifier les répertoires avec des définitions d'image de conteneur, comme un 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.

d869e0cd38e983d7.png

  1. Exécutez la commande suivante dans le terminal.
skaffold init --generate-manifests
  1. 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
  1. Saisissez 8080 comme port
  2. Saisissez y pour enregistrer la configuration

Deux fichiers sont ajoutés à l'espace de travail skaffold.yaml et deployment.yaml

Résultat Skaffold:

b33cc1e0c2077ab8.png

Mettre à jour le nom de l'application

Actuellement, les valeurs par défaut incluses dans la configuration ne correspondent pas au nom de votre application. Mettez à jour les fichiers pour référencer le nom de votre application plutôt que les valeurs par défaut.

  1. Modifier les 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 demo-app.
  1. Modifier les entrées dans 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 demo-app.

Activer le mode de synchronisation automatique

Pour optimiser l'expérience de hot reload, vous utilisez 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 l'option "sync" que vous configurez dans la configuration Skaffold utilise la synchronisation Spring Profil que vous avez configuré à l'étape précédente, où vous avez activé la prise en charge de Spring-dev-tools.

  1. Mettre à jour la configuration Skaffold

Dans le fichier skaffold.yaml, remplacez l'intégralité de la section de compilation du fichier 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/.

a624f5dd0c477c09.png

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 apprendre à utiliser le plug-in Cloud Code pour connaître 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. 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 afin d'éliminer les détails du flux de développement. Cloud Code améliore également votre processus de développement en fournissant des fonctionnalités traditionnelles de débogage et de synchronisation à chaud avec le développement basé sur des conteneurs.

Se connecter à Google Cloud

Cliquez sur l'icône Cloud Code et sélectionnez "Sign in to Google Cloud" (Se connecter à Google Cloud) :

1769afd39be372ff.png

Cliquez sur "Procéder à la connexion".

923bb1c8f63160f9.png

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

517fdd579c34aa21.png

Connectez-vous avec vos identifiants d'étudiant Qwiklabs.

db99b345f7a8e72c.png

Sélectionnez "Autoriser" :

a5376553c430ac84.png

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

6719421277b92eac.png

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

e9847cfe3fa8a2ce.png

Ajouter un cluster Kubernetes

  1. Ajouter un cluster

62a3b97bdbb427e5.png

  1. Sélectionnez Google Kubernetes Engine:

9577de423568bbaa.png

  1. Sélectionnez un projet.

c5202fcbeebcd41c.png

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

366cfd8bc27cd3ed.png

9d68532c9bc4a89b.png

Définir l'ID de projet actuel à l'aide de la CLI gcloud

Copiez l'ID de projet de cet atelier depuis la page Qwiklabs.

fcff2d10007ec5bc.png

Exécutez la commande gcloud cli pour définir l'ID du projet. Remplacez l'exemple d'ID de projet avant d'exécuter la commande.

gcloud config set project qwiklabs-gcp-project-id

Exemple de résultat :

f1c03d01b7ac112c.png

Déboguer sur Kubernetes

  1. Dans le volet de gauche situé en bas, sélectionnez Cloud Code.

60b8e4e95868b561.png

  1. Dans le panneau qui s'affiche sous SESSIONS DE DÉVELOPPEMENT, sélectionnez "Debug on Kubernetes" (Déboguer dans Kubernetes).

Faites défiler l'écran vers le bas si l'option n'est pas visible.

7d30833d96632ca0.png

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

a024a69b64de7e9e.png

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

faebabf372e3caf0.png

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

fabc6dce48bae1b4.png

  1. Sélectionnez l'onglet "Output" (Sortie) dans le volet inférieur pour afficher la progression et les notifications.
  2. Sélectionnez "Kubernetes: Run/Debug - Détaillé". dans le menu déroulant du canal à droite pour afficher des informations supplémentaires et les journaux diffusés en direct depuis les conteneurs.

86b44c59db58f8f3.png

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

9f37706a752829fe.png

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

6ad220e5d1980756.png

  1. Revenez à la vue simplifiée en sélectionnant "Kubernetes: Run/Debug" (Kubernetes : Exécuter/Déboguer) dans le menu déroulant de l'onglet RÉSULTAT.
  2. Une fois la compilation et les tests terminés, l'onglet "Output" (Sortie) indique Resource deployment/demo-app status completed successfully, et une URL s'affiche : "Forwarded URL from service demo-app: http://localhost:8080"
  3. Dans le terminal Cloud Code, pointez sur l'URL affichée dans le résultat (http://localhost:8080), puis sélectionnez "Suivre le lien" dans l'info-bulle qui s'affiche.

28c5539880194a8e.png

Le nouvel onglet s'ouvre et affiche le résultat ci-dessous:

d67253ca16238f49.png

Utiliser des points d'arrêt

  1. Ouvrez l'application HelloController.java située à l'adresse /src/main/java/com/example/springboot/HelloController.java.
  2. Recherchez l'instruction de retour pour le chemin d'accès racine qui indique return String.format("Hello from your %s environment!", target);
  3. 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.

5027dc6da2618a39.png

  1. Actualisez 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.

71acfb426623cec2.png

  1. Cliquez sur la section "Variables" vers le bas jusqu'à la colonne "Cible". .
  2. Observer la valeur actuelle en tant que "local"

a1160d2ed2bb5c82.png

  1. Double-cliquez sur le nom de variable "target". Dans la fenêtre pop-up,

remplacez la valeur par "Cloud Workstations"

e597a556a5c53f32.png

  1. Cliquez sur le bouton "Continuer" dans le panneau de configuration du débogage.

ec17086191770d0d.png

  1. Examinez la réponse dans votre navigateur qui affiche à présent la nouvelle valeur que vous venez de saisir.

6698a9db9e729925.png

  1. Supprimez le point d'arrêt en cliquant sur l'indicateur rouge à gauche du numéro de ligne. Cela empêchera l'exécution de votre code au niveau de cette ligne au fur et à mesure que vous avancerez dans cet atelier.

Actualisation à chaud

  1. Modifiez l'instruction pour renvoyer une valeur différente, telle que "Hello from %s Code" (Bonjour de %s Code).
  2. Le fichier est automatiquement enregistré et synchronisé dans les conteneurs distants dans GKE
  3. Actualisez votre navigateur pour voir les résultats mis à jour.
  4. Arrêtez la session de débogage en cliquant sur le carré rouge dans la barre d'outils de débogage.

a541f928ec8f430e.png c2752bb28d82af86.png

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

984eb2fa34867d70.png

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 suivi 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 se connectant à une base de données gérée dans Google Cloud.

Configurer des 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 ce qui suit dans le fichier pom.xl.

  1. Ouvrez le fichier pom.xml et ajoutez le code suivant dans 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>

Service REST Code

Quote.java

Créez un fichier nommé Quote.java dans /src/main/java/com/example/springboot/ et copiez-y le code ci-dessous. Ce champ définit le modèle d'entité de l'objet Citation 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 nommé 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 la persistance des 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 au service, une classe QuoteController fournira cette fonctionnalité.

Créez un fichier nommé QuoteController.java à l'emplacement src/main/java/com/example/springboot et copiez 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 une configuration pour la base de données backend à laquelle le service a accès. Modifiez (ou créez-le s'il n'existe pas) 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 une migration de base de données

Créer les dossiers db/migration sous src/main/resources

Créer 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 de 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 SQL dans le menu de navigation de la console Google Cloud. Veuillez modifier la valeur.
  • DB_USER et DB_PASS, tels que 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 le fichier deployment.yaml et vérifiez que la valeur DB_HOST a été mise à jour avec l'adresse IP de l'instance.

fd63c0aede14beba.png

Déployer et valider l'application

  1. Dans le volet situé en bas de l'éditeur Cloud Shell, sélectionnez Cloud Code, puis "Déboguer sur Kubernetes" en haut de l'écran.

33a5cf41aae91adb.png

  1. Une fois la compilation et les tests terminés, l'onglet "Output" (Sortie) indique Resource deployment/demo-app status completed successfully, et une URL est répertoriée : "Forwarded URL from service demo-app: http://localhost:8080". Notez que parfois, le port peut être différent, comme 8081. Si tel est le cas, définissez la valeur appropriée. Définir la valeur de l'URL dans le terminal
export URL=localhost:8080
  1. Afficher les citations aléatoires

Depuis le terminal, exécutez plusieurs fois la commande ci-dessous sur le point de terminaison "random-citation". Observer un appel répété renvoyant des guillemets différents

curl $URL/random-quote | jq
  1. Ajouter un devis

Créez un autre devis avec id=6 à l'aide de la commande ci-dessous et observez l'écho de la requête.

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
  1. Supprimer une citation

Maintenant, supprimez la citation que vous venez d'ajouter avec la méthode delete et observez un code de réponse HTTP/1.1 204.

curl -v -X DELETE $URL/quotes/6
  1. Erreur du serveur

Vous rencontrerez 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 trouvé 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 localiser le problème. L'erreur s'est produite lors de l'opération DELETE. Vous allez donc utiliser la classe CitationController.

  1. Ouvrir src/main/java/com/example/springboot/QuoteController.java
  2. Rechercher la méthode deleteQuote()
  3. Recherchez la ligne: Optional<Quote> quote = quoteRepository.findById(id);
  4. Définissez un point d'arrêt sur cette ligne en cliquant sur l'espace vide à gauche du numéro de ligne.
  5. Un indicateur rouge s'affiche pour indiquer que le point d'arrêt est défini.
  6. Exécutez à nouveau la commande delete.
curl -v -X DELETE $URL/quotes/6
  1. Pour revenir à la vue de débogage, cliquez sur l'icône dans la colonne de gauche.
  2. Comme vous pouvez le constater, la ligne de débogage s'est arrêtée dans la classe CitationController.
  3. Dans le débogueur, cliquez sur l'icône step over b814d39b2e5f3d9e.png.
  4. Notez qu'un code renvoie au client une erreur interne de serveur HTTP 500, 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 (introuvable).

Corrigez l'erreur.

  1. La session de débogage étant toujours en cours d'exécution, terminez la requête en cliquant sur le bouton "Continuer". dans le panneau de configuration du débogage.
  2. Remplacez ensuite le bloc else par le code suivant:
       else {
                return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
            }

La méthode doit 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);
        }
    }
  1. Réexécuter la commande de suppression
curl -v -X DELETE $URL/quotes/6
  1. Parcourez le débogueur et observez le message "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
  1. Arrêtez la session de débogage en cliquant sur le carré rouge dans la barre d'outils de débogage.

12bc3c82f63dcd8a.png

6f19c0f855832407.png

6. Félicitations

Félicitations ! Dans cet atelier, vous avez entièrement créé une application Java et l'avez configurée pour fonctionner efficacement avec des conteneurs. Vous avez ensuite déployé et débogué votre application sur un cluster GKE distant en suivant le même parcours de développement que 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