Déployer une application Micronaut conteneurisée avec Jib sur Google Kubernetes Engine

1. Présentation

À propos de Micronaut

Micronaut est un framework complet, moderne et basé sur la JVM, qui permet de créer des microservices modulaires et des applications sans serveur faciles à tester. Micronaut vise à offrir un temps de démarrage rapide, un débit élevé et une empreinte mémoire minimale. Les développeurs peuvent développer avec Micronaut en Java, Groovy ou Kotlin.

Micronaut fournit les éléments suivants :

  • Temps de démarrage rapide et faible consommation de mémoire : les frameworks IoC basés sur la réflexion chargent et mettent en cache les données de réflexion pour chaque champ, méthode et constructeur de votre code. Avec Micronaut, le temps de démarrage de votre application et la consommation de mémoire ne sont pas liés à la taille de votre codebase.
  • Client HTTP déclaratif, réactif et au moment de la compilation : créez de manière déclarative des clients HTTP réactifs, implémentés au moment de la compilation, ce qui réduit la consommation de mémoire.
  • Serveur HTTP non bloquant basé sur Netty : avec une courbe d'apprentissage fluide, le serveur HTTP de Micronaut facilite l'exposition d'API pouvant être utilisées par les clients HTTP.
  • Tests rapides et faciles : configurez facilement des serveurs et des clients dans vos tests unitaires, et exécutez-les instantanément.
  • Injection de dépendances et AOP efficaces au moment de la compilation : Micronaut fournit une API de programmation orientée aspect simple au moment de la compilation, qui n'utilise pas la réflexion.
  • Créez des applications entièrement réactives et non bloquantes : Micronaut est compatible avec tous les frameworks qui implémentent Reactive Streams, y compris RxJava et Reactor.

Pour en savoir plus, consultez le site Web de Micronaut.

À propos de Kubernetes

Kubernetes est un projet Open Source qui peut s'exécuter dans de nombreux environnements différents : des ordinateurs portables aux clusters multinœuds à haute disponibilité, des clouds publics aux déploiements sur site, des machines virtuelles aux machines dédiées.

Dans cet atelier, vous allez déployer sur Kubernetes un microservice Micronaut simple basé sur Groovy qui s'exécute sur Kubernetes Engine.

L'objectif de cet atelier de programmation est de vous permettre d'exécuter votre microservice en tant que service répliqué sur Kubernetes. Vous allez transformer le code que vous avez développé sur votre machine en une image de conteneur Docker, que vous exécuterez ensuite sur Kubernetes Engine.

Voici un schéma des interactions entre les différents éléments de cet atelier de programmation. Utilisez ce schéma comme référence au fur et à mesure de votre progression dans l'atelier de programmation. Il prendra tout son sens à la fin de l'atelier. Pour l'instant, vous pouvez le laisser de côté.

Kubernetes Codelab Diagram 1 (2).png

Pour cet atelier de programmation, l'utilisation d'un environnement géré comme Kubernetes Engine (une version de Kubernetes hébergée par Google et exécutée sur Compute Engine) vous permet de vous consacrer à la découverte de Kubernetes plutôt que d'avoir à configurer l'infrastructure sous-jacente.

Si vous souhaitez exécuter Kubernetes sur votre appareil local, par exemple votre ordinateur portable de développement, n'hésitez pas à utiliser Minikube. Il permet de configurer facilement un cluster Kubernetes à nœud unique à des fins de développement et de test. Si vous le souhaitez, vous pouvez utiliser Minikube dans cet atelier de programmation.

À propos de Jib

Jib est un outil Open Source qui vous permet de créer des images Docker et OCI pour vos applications Java. Il est disponible sous forme de plug-ins pour Maven et Gradle, ainsi que sous forme de bibliothèque Java.

Jib vise à être :

  • Rapide : déployez vos modifications rapidement. Jib sépare votre application en plusieurs couches, en distinguant les dépendances des classes. Vous n'avez plus besoin d'attendre que Docker recompile l'intégralité de votre application Java. Il vous suffit de déployer les couches qui ont été modifiées.
  • Reproductible : la recompilation de votre image de conteneur avec le même contenu génère toujours la même image. Ne déclenchez plus jamais de mise à jour inutile.
  • Sans démon : réduisez vos dépendances CLI. Créez votre image Docker dans Maven ou Gradle, puis transférez-la vers le registre de votre choix. Vous n'avez plus besoin d'écrire des fichiers Dockerfile ni d'appeler docker build/push.

Pour en savoir plus sur Jib, consultez la page du projet sur GitHub.

À propos de ce tutoriel

Ce tutoriel utilise l'exemple de code de l'outil Jib pour créer des conteneurs pour les applications Java.

L'exemple est un simple service Hello World, utilisant le framework Micronaut et le langage de programmation Apache Groovy.

Points abordés

  • Empaqueter une application Java simple en tant que conteneur Docker à l'aide de Jib
  • Créer un cluster Kubernetes sur Kubernetes Engine
  • Déployer votre service Micronaut dans Kubernetes sur Kubernetes Engine
  • Effectuer un scaling à la hausse et déployer une mise à niveau de votre service
  • Accéder au tableau de bord graphique de Kubernetes

Prérequis

  • Un projet Google Cloud Platform
  • Un navigateur tel que Chrome ou Firefox
  • Bonne connaissance des éditeurs de texte Linux standards tels que Vim, EMACs ou Nano

Comment allez-vous utiliser ce tutoriel ?

Je vais le lire uniquement Je vais le lire et effectuer les exercices

Comment évalueriez-vous votre expérience de création d'applications Web HTML/CSS ?

Débutant Intermédiaire Expert

Quel est votre niveau d'expérience avec les services Google Cloud Platform ?

Débutant Intermédiaire Expert

2. Préparation

Configuration de l'environnement d'auto-formation

  1. Connectez-vous à Cloud Console, puis créez un projet ou réutilisez un projet existant. (Si vous n'avez pas encore de compte Gmail ou G Suite, vous devez en créer un.)

dMbN6g9RawQj_VXCSYpdYncY-DbaRzr2GbnwoV7jFf1u3avxJtmGPmKpMYgiaMH-qu80a_NJ9p2IIXFppYk8x3wyymZXavjglNLJJhuXieCem56H30hwXtd8PvXGpXJO9gEUDu3cZw

ci9Oe6PgnbNuSYlMyvbXF1JdQyiHoEgnhl4PlV_MFagm2ppzhueRkqX4eLjJllZco_2zCp0V0bpTupUSKji9KkQyWqj11pqit1K1faS1V6aFxLGQdkuzGp4rsQTan7F01iePL5DtqQ

8-tA_Lheyo8SscAVKrGii2coplQp2_D1Iosb2ViABY0UUO1A8cimXUu6Wf1R9zJIRExL5OB2j946aIiFtyKTzxDcNnuznmR45vZ2HMoK3o67jxuoUJCAnqvEX6NgPGFjCVNgASc-lg

Mémorisez l'ID du projet. Il s'agit d'un nom unique permettant de différencier chaque projet Google Cloud (le nom ci-dessus est déjà pris ; vous devez en trouver un autre). Il sera désigné par le nom PROJECT_ID tout au long de cet atelier de programmation.

  1. Vous devez ensuite activer la facturation dans Cloud Console pour pouvoir utiliser les ressources Google Cloud.

L'exécution de cet atelier de programmation est très peu coûteuse, voire sans frais. Veillez à suivre les instructions de la section "Nettoyer" qui indique comment désactiver les ressources afin d'éviter les frais une fois ce tutoriel terminé. Les nouveaux utilisateurs de Google Cloud peuvent participer au programme d'essai sans frais pour bénéficier d'un crédit de 300 $.

3. Obtenir l'exemple de code source Micronaut

Une fois l'environnement Cloud Shell lancé, vous pouvez utiliser la ligne de commande pour cloner l'exemple de code source dans le répertoire d'accueil et accéder au répertoire contenant notre exemple de service :

$ git clone https://github.com/GoogleContainerTools/jib.git
$ cd jib/examples/micronaut/

4. Aperçu rapide du code

Notre service simple Micronaut est composé d'un contrôleur qui génère le fameux message Hello World :

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello World"
    }
}

Le contrôleur HelloController répond aux requêtes sous le chemin /hello, et la méthode index() accepte les requêtes HTTP GET.

Une classe de test Spock est également disponible pour vérifier que le bon message est affiché dans le résultat.

class HelloControllerSpec extends Specification {
    @Shared
    @AutoCleanup
    EmbeddedServer embeddedServer = ApplicationContext.run(EmbeddedServer)

    @Shared
    @AutoCleanup
    RxHttpClient client = embeddedServer.applicationContext.createBean(RxHttpClient, embeddedServer.getURL()) 

    void "test hello world response"() {
        when:
        HttpRequest request = HttpRequest.GET('/hello')
        String rsp  = client.toBlocking().retrieve(request)

        then:
        rsp == "Hello World"
    }
}

Plus qu'un simple test unitaire, ce test exécute en fait la même pile de serveurs Micronaut (basée sur le framework Netty) que celle exécutée en production. Le comportement de votre code sera donc exactement le même dans le produit que dans vos tests.

Pour exécuter les tests, vous pouvez exécuter la commande suivante pour vérifier que tout fonctionne correctement :

./gradlew test

5. Exécuter l'application en local

Vous pouvez démarrer le service Micronaut normalement avec la commande Gradle suivante :

$ ./gradlew run

Une fois l'application démarrée, vous pouvez ouvrir une instance Cloud Shell supplémentaire grâce à la petite icône +, puis vérifier avec curl que vous obtenez le résultat attendu :

$ curl localhost:8080/hello

Un simple message "Hello World" devrait s'afficher.

6. Empaqueter l'application en tant que conteneur Docker avec Jib

Préparez ensuite votre application à s'exécuter sur Kubernetes. Pour ce faire, nous allons profiter de Jib pour faire le gros du travail à notre place, car nous n'aurons pas à toucher un Dockerfile nous-mêmes !

Exécutons la commande pour créer notre conteneur :

$ ./gradlew jibDockerBuild

Voici le résultat que vous devriez obtenir :

Tagging image with generated image reference micronaut-jib:0.1. If you'd like to specify a different tag, you can set the jib.to.image parameter in your build.gradle, or use the --im
age=<MY IMAGE> commandline flag.

Containerizing application to Docker daemon as micronaut-jib:0.1...
warning: Base image 'gcr.io/distroless/java' does not use a specific image digest - build may not be reproducible
Getting base image gcr.io/distroless/java...
Building dependencies layer...
Building resources layer...
Building classes layer...
Finalizing...

Container entrypoint set to [java, -cp, /app/resources:/app/classes:/app/libs/*, example.micronaut.Application]
Loading to Docker daemon...

Built image to Docker daemon as micronaut-jib:0.1

Maintenant que notre image est créée, vérifions si nous pouvons voir notre message de salutation en exécutant notre image Docker dans le premier onglet de Cloud Shell :

$ docker run -it -p 8080:8080 micronaut-jib:0.1
16:57:20.255 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [cloud, gcp]
16:57:23.203 [main] INFO  io.micronaut.runtime.Micronaut - Startup completed in 2926ms. Server Running: http://97b7d76ccf3f:8080

Notre service est en cours d'exécution. Nous pouvons donc maintenant lancer notre commande curl dans notre deuxième onglet Cloud Shell pour vérifier qu'il fonctionne comme prévu :

$ curl localhost:8080/hello
Hello World

Vous pouvez arrêter le conteneur en appuyant sur Ctrl+C dans Cloud Shell.

7. Transférer notre service conteneurisé vers le registre

Maintenant que l'image fonctionne comme prévu, vous pouvez la transférer vers Google Container Registry, un dépôt privé pour vos images Docker accessible depuis tous les projets Google Cloud (mais également en dehors de Google Cloud Platform).

Avant de pouvoir transférer des images vers le registre, assurez-vous que Container Registry est activé pour votre projet en accédant à Outils > Container Registry. Si elle n'est pas activée, la boîte de dialogue suivante s'affiche. Cliquez sur Activer l'API Container Registry pour l'activer :

ac812e6260ac7dfb.png

Une fois le registre prêt, exécutez les commandes suivantes pour transférer l'image vers le registre :

$ gcloud auth configure-docker
$ docker tag micronaut-jib:0.1 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

Les commandes ci-dessus permettent au SDK gcloud de configurer et d'autoriser Docker à transférer des images vers votre instance de Container Registry, à taguer l'image pour indiquer son emplacement dans le registre, puis à la transférer vers le registre.

Si tout se passe bien, vous devriez voir l'image de conteneur listée dans la console après un certain temps : Outils > Container Registry. Vous disposez maintenant d'une image Docker pour l'ensemble du projet. Comme vous le verrez dans quelques minutes, vous pouvez accéder à cette image et l'orchestrer grâce à Kubernetes.

12224c4e42183b4e.png

8. Créer votre cluster

Vous êtes maintenant prêt à créer votre cluster Kubernetes Engine. Mais avant cela, accédez à la section Google Kubernetes Engine de la console Web et attendez que le système s'initialise (cela ne devrait prendre que quelques secondes).

20c0587c0108b8ba.png

Un cluster se compose d'un serveur d'API maître Kubernetes géré par Google et d'un ensemble de nœuds de calcul. Les nœuds de calcul sont des machines virtuelles Compute Engine. Utilisons l'interface de ligne de commande gcloud de votre session Cloud Shell pour créer un cluster avec deux nœuds n1-standard-1 (cette opération prend quelques minutes) :

$ gcloud container clusters create hello-cluster \
  --num-nodes 2 \
  --machine-type n1-standard-1 \
  --zone us-central1-c

Une fois l'opération terminée, vous devez voir le cluster créé.

Creating cluster hello-cluster in us-central1-c...done.
Created [https://container.googleapis.com/v1/projects/mn-gke-test/zones/us-central1-c/clusters/hello-cluster].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-c/hello-cluster?project=mn-gke-test
kubeconfig entry generated for hello-cluster.
NAME           LOCATION       MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
hello-cluster  us-central1-c  1.9.7-gke.7     35.239.224.115  n1-standard-1  1.9.7-gke.7   2          RUNNING

Vous devez maintenant disposer d'un cluster Kubernetes entièrement fonctionnel, fourni par Google Kubernetes Engine :

d9e1e314769753e7.png

Vous pouvez à présent déployer votre application conteneurisée sur le cluster Kubernetes. À partir de maintenant, vous utiliserez la ligne de commande kubectl (déjà configurée dans votre environnement Cloud Shell). Dans la suite de cet atelier de programmation, vous devrez utiliser les versions client et serveur 1.2 ou ultérieures de Kubernetes. kubectl version affiche la version actuelle de la commande.

9. Déployer votre application sur Kubernetes

Un déploiement Kubernetes peut créer, gérer et faire évoluer plusieurs instances de votre application à l'aide de l'image de conteneur que vous venez de créer. Créons un déploiement de votre application dans Kubernetes à l'aide de la commande kubectl create deployment :

$ kubectl create deployment hello-micronaut \
  --image=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.1

Pour afficher le déploiement que vous venez de créer, exécutez simplement la commande suivante :

$ kubectl get deployments
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   1         1         1            1           5m

Pour afficher les instances d'application créées par le déploiement, exécutez la commande suivante :

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
hello-micronaut-5647fb98c5-lh5h7   1/1       Running   0          5m

À ce stade, votre conteneur devrait être sous le contrôle de Kubernetes, mais vous devez quand même le rendre accessible depuis l'extérieur.

10. Autoriser le trafic externe

Par défaut, le pod n'est accessible que par le biais de son adresse IP interne au sein du cluster. Pour rendre le conteneur hello-micronaut accessible en dehors du réseau virtuel Kubernetes, vous devez exposer le pod en tant que service Kubernetes.

Depuis Cloud Shell, vous pouvez présenter le pod au réseau Internet public avec la commande kubectl expose associée à l'option --type=LoadBalancer. Cette option est obligatoire pour créer une adresse IP accessible depuis l'extérieur :

$ kubectl expose deployment hello-micronaut --type=LoadBalancer --port=8080

L'option utilisée dans cette commande spécifie que vous allez utiliser l'équilibreur de charge fourni par l'infrastructure sous-jacente (dans ce cas, l'équilibreur de charge Compute Engine). Remarquez que vous présentez le déploiement, et non le pod directement. Par conséquent, le service obtenu va répartir le trafic sur tous les pods gérés par le déploiement (dans le cas présent, un seul pod, mais vous ajouterez des instances répliquées par la suite).

Le Kubernetes maître crée l'équilibreur de charge et les règles de transfert Compute Engine associées, les pools cibles et les règles de pare-feu afin de rendre le service entièrement accessible en dehors de Google Cloud Platform.

Pour trouver l'adresse IP publiquement accessible du service, demandez à kubectl de lister tous les services du cluster :

$ kubectl get services
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)          AGE
hello-micronaut   LoadBalancer   10.39.243.251   aaa.bbb.ccc.ddd 8080:30354/TCP   1m
kubernetes        ClusterIP      10.39.240.1     <none>          443/TCP          31m

Remarquez que deux adresses IP accessibles par le port 8080 sont répertoriées pour votre service. L'une est l'adresse IP interne visible uniquement au sein de votre réseau virtuel cloud. L'autre correspond à l'adresse IP externe avec équilibrage de charge. Dans cet exemple, l'adresse IP externe est aaa.bbb.ccc.ddd.

Vous devriez maintenant pouvoir accéder au service en utilisant l'adresse suivante dans votre navigateur : http://<EXTERNAL_IP>:8080/hello

11. Effectuer un scaling à la hausse de votre service

Le scaling des applications, qui est une puissante fonctionnalité de Kubernetes, est simple à mettre en œuvre. Imaginons que vous ayez soudainement besoin d'augmenter la capacité de votre application. Vous pouvez simplement demander au contrôleur de réplication de gérer un certain nombre de nouvelles instances répliquées pour vos instances d'application :

$ kubectl scale deployment hello-micronaut --replicas=3
deployment.extensions "hello-micronaut" scaled

$ kubectl get deployment
NAME              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hello-micronaut   3         3         3            3           16m

Notez que la programmation déclarative est utilisée dans cet exemple : plutôt que de démarrer ou d'arrêter de nouvelles instances, vous déclarez le nombre d'instances à exécuter en permanence. Les boucles de réconciliation de Kubernetes veillent à ce que la réalité corresponde à votre demande et prennent des mesures si nécessaire.

12. Déployer une mise à niveau sur votre service

À un moment donné, des corrections de bugs ou de nouvelles fonctionnalités seront nécessaires sur l'application que vous avez déployée en production. Kubernetes va vous aider à déployer une nouvelle version en production, sans impact sur vos utilisateurs.

Commençons par modifier l'application. Ouvrez l'éditeur de code depuis Cloud Shell.

5aee8f3d1e003571.png

Accédez à /jib/examples/micronaut/src/main/groovy/example/micronaut/HelloController.groovy et modifiez la valeur de la réponse :

@Controller("/hello")
class HelloController {
    @Get("/")
    String index() {
        "Hello Kubernetes World"
    }
}

Dans /jib/examples/micronaut/build.gradle, nous allons mettre à niveau la version de notre image de 0.1 à 0.2 en modifiant cette ligne :

version '0.2'

Recompilez et packagez ensuite l'application avec les dernières modifications :

$ ./gradlew jibDockerBuild

Ajoutez un tag à l'image et transférez-la vers le registre d'images de conteneurs :

$ docker tag micronaut-jib:0.2 \
         gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2
$ docker push gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

Vous êtes maintenant prêt à ce que Kubernetes mette à jour votre contrôleur de réplication de manière fluide et installe la nouvelle version de l'application. Afin de modifier le libellé de l'image pour votre conteneur en cours d'exécution, vous devez modifier le hello-micronaut deployment existant et remplacer l'image gcr.io/PROJECT_ID/micronaut-jib:0.1 par gcr.io/PROJECT_ID/micronaut-jib:0.2.

Vous pouvez utiliser la commande kubectl set image pour demander à Kubernetes de déployer la nouvelle version de votre application sur l'ensemble du cluster, une instance à la fois, avec une mise à jour continue :

$ kubectl set image deployment/hello-micronaut \
          micronaut-jib=gcr.io/$GOOGLE_CLOUD_PROJECT/micronaut-jib:0.2

deployment.apps "hello-micronaut" image updated

Vérifiez à nouveau http://EXTERNAL_IP:8080 pour voir si la nouvelle réponse s'affiche.

13. Rollback

Oups ! Vous avez fait une erreur avec une nouvelle version de l'application ? Peut-être que la nouvelle version contenait une erreur et que vous devez revenir rapidement à la version précédente. Avec Kubernetes, vous pouvez facilement revenir à l'état précédent. Rétablissons l'application en exécutant la commande suivante :

$ kubectl rollout undo deployment/hello-micronaut

Si vous examinez la sortie du service, vous retrouverez notre message initial "Hello World".

14. Résumé

Dans cette étape, vous avez configuré un service Micronaut Hello World simple basé sur Apache Groovy, l'avez exécuté directement dans Cloud Shell, l'avez empaqueté en tant que conteneur avec Jib et l'avez déployé sur Google Kubernetes Engine.

15. Félicitations !

Vous avez appris à créer et à déployer un nouveau microservice Web Apache Groovy / Micronaut sur Kubernetes dans Google Kubernetes Engine.

En savoir plus

Licence

Ce document est publié sous une licence Creative Commons Attribution 2.0 Generic.