Microk8s et Quarkus Part 2 : Comment déployer simplement une application Quarkus et sa base de données dans k8s

Le but de cette série de billets est de montrer comment on peut simplement, facilement et gratuitement (à partir du moment ou vous avez un ordinateur suffisamment performant pour développer) faire du Kubernetes et déployer des applications conteneurisées. Et concernant ces dernières, je montrerai en quoi Quarkus est le candidat idéal (A Kubernetes Native Java stack).

Lors du précédent article, nous avons vu comment installer et configurer un Microk8s single-node et multi-node, le but à présent est de déployer dans Microk8s.

Cette seconde partie sera plus focus sur Quarkus et nous allons voir comment déployer une application Quarkus avec une base de donnée PosgreSQL dans notre Microk8s précédemment installé et configuré lors de la part1.

Note : vous pouvez travailler avec n’importe quel cluster Kubernetes, il faut juste pour cela que votre cli kubectl soit bien configuré en local. Dans cet article les exemples se base sur l’utilisation d’un Microk8s single node.

PostgreSQL

Commençons par installer la BDD PostgreSQL, au préalable il nous faut récupérer le code sur github, dans le répertoire /microk8s vous trouverez tous les fichiers de déploiement k8s nécessaires.

Pour rappel il vous faut d’abord avoir bien créer le namespace quarkus-apero-code, si ce n’est pas fait vous pouvez jouer la commande suivante à la racine du projet github (en ayant pensé aussi à monter un volume à l’aide de Multipass, voir article précédent)

A présent jouons le fichier de déploiement de notre BDD à l’aide de la commande kubectl apply

Vérifions que notre BDD est bien déployée et fonctionnelle en accédant au dashboard k8s en naviguant à https://192.168.64.2:10443

Tout est au vert, nous avons déployé une base de données Postgresql que nous allons pouvoir utiliser avec Quarkus dans notre kube, attaquons-nous à présent à notre application Quarkus que nous déploierons dans Microk8s

Quarkus

Génération de l’application

L’initialisation du projet a été fait en ligne de commande (mais vous pouvez aussi vous appuyer sur le generator Quarkus) :

J’ai par la suite enrichi ce projet avec les extensions kotlin pour le language de programmation, quarkus-jdbc-h2 pour le connecteur jdbc de la base h2 (pour le dev et les tests unitaires), quarkus-jdbc-postgresql pour le connnecteur jdbc de postgresql (pour Microk8s) et enfin quarkus-hibernate-orm-panache-kotlin pour utiliser notre ORM favori avec la ligne de commande suivante :

J’ai ensuite développé les services REST (Resource) et la couche de persistance (Repository) pour exposer les données de la base via des apis.

Lançons l’application

Enfin, notre application développée, je vais d’abord m’assurer que tout est OK en la testant en local de la manière suivante

L’application est bien démarrée en moins de trois secondes et écoute sur le port 2000, vérifions qu’elle répond correctement allant sur http://localhost:2000

et vérifions que l’accès à la base H2 se fait bien en allant sur http://localhost:2000/beers

Déploiement de l’application

A présent je sais que mon application fonctionne bien en local, je vais compiler, tester, packager, déployer nos images docker dans un registry et déployer dans k8s via les lignes de commande ci-dessous

Je lance donc un maven package de l’application sur le profil microk8s (voir dans le application.properties ci-dessous) avec un paramètre -Pnative qui indique que je veux construire un exécutable natif (et non un jar pour une cible JVM) et enfin je précise que je veux que ce soit déployé directement dans mon Microk8s avec l’option -Dquarkus.kubernetes.deploy=true.

Profil Quarkus

Pour rappel un profil Quarkus est un prefix (« %myProfile.property« ) sur les propriétés du fichier de configuration et peut être activé lorsque je lance un commande maven avec l’option -Dquarkus.profile=myProfile. Dans cet exemple j’ai défini trois profil dev, test et microk8s.

Note : Le profil dev est activé par défaut lorsque je lance un mvn compile quarkus:dev et le profil test est activé par défaut lorsque les tests unitaires sont lancés (mvn test)

Le profil microk8s est spécifique pour le déploiement d’image native dans Microk8s. La base de données en dev et en test sera une base in memory H2 alors que en profil microk8s on travaillera avec une base postgreSQL préalablement déployée dans k8s.

Focus sur la configuration Quarkus

Revenons un peu plus en détails sur la configuration du fichier application.properties qui se trouve :

Voici ce qu’il contient

Analysons un peu plus en détail de cette configuration qui est le nerf de la guerre pour déployer dans k8s (mais pas que)

Ces deux lignes me permettent de configurer le port http et d’indiquer que je veux que mon exécutable soit construite dans une image Docker spécifique et non en utilisant un GraalVM local à mon poste de travail. Pourquoi? Et bien tout simplement je ne veux pas générer un exécutable pour mon macOS. Quarkus offre un moyen très pratique de créer un exécutable Linux natif en tirant parti d’un moteur de conteneur tel que Docker ou podman. Un avantage avec cette option est que je n’ai pas à installer GraalVM sur ma machine, Quarkus se charge de tout.

Les lignes ci-dessus sont pour la génération de la future image docker de notre application. Les 5 premières lignes me servent à fournir les informations de mon image (group, tag, nom) et à indiquer que je veux que mon image soit construite et aussi déployée dans un registry docker. Dans mon example de code sur github je déploie mes images dans le hub docker. Ce registry sera utilisé par microk8s pour installer les bonnes images.

Cette partie de la configuration va nous servir pour la configuration de notre fichier de déploiement k8s (généré automatiquement par quarkus) et pour configurer le client k8s qui sera en charge de communiquer avec les APIs k8s pour appliquer un déploiement en remote.

Enfin ci-dessus concerne la base de donnée que nous allons utiliser selon les différents profils de quarkus. %dev et %test pour la configuration d’une base H2 in memory pour le développement local et pour les tests unitaires, et %microk8s pour la cible de déploiement final c’est à dire une base PostgreSQL dans un k8s.

ConfigMap & Secret k8s

Concernant le login mot de passe de la base de données cible dans k8s, vous pouvez voir que je vais m’appuyer sur deux variables d’environnements que sont POSTGRES_DB, POSTGRES_USER, POSTGRES_HOST et POSTGRES_PASSWORD. Ces quatre variables sont fournies par une ConfigMap et un Secret déployer avec le fichier de la base (rappel des logs du déploiement de postgreSQL: configmap/postgres-config created et secret/postgres-secret created)

Donc pour s’appuyer sur ces informations, il me suffit de le préciser dans le fichier de configuration et d’indiquer quel Secret et quelle ConfigMap je vais avoir besoin d’utiliser en ayant bien sur activer kuberntes-config comme suit

Injection de Data dans la BDD

Dans ces dernières options ci-dessous, nous définissons donc la stratégie d’intégration du modèle de données et des données dans les bases de nos différents environnements par l’intermédiaire de Hibernate (dev, test, microk8s). Je définis que à chaque déploiement de l’application, le schéma doit être supprimé et créé (drop-and-create) et que les données de fichier sql doivent être chargées dans le schéma créé (scripts import-X.sql). Vous pouvez constater dans le projet que j’ai un fichier SQL par environnement donc par profil.

Note : Pour avoir un listing complet de toutes les propriétés de configuration Quarkus voir ici

Quarkus build steps

Où en sommes nous donc de notre déploiement? Nous avons donc lancé la commande maven pour déployer notre application dans kube. Que disent les logs

La commande maven lance donc le clean et la compilation (ligne 2-44), les tests unitaires (ligne 45-62), build le jar (ligne 63-65). Jusqu’ici rien de nouveau sous le soleil.

L’étape intéressante se lance, la génération de l’image native (NativeImageBuildStep, ligne 66-107). C’est là que une particularité Quarkus entre en jeu. La première étape, avant de générer l’exécutable natif, consiste en de la compilation « Ahead of Time ». Lorsqu’une application Quarkus est créée, certains traitement qui se produisent généralement au moment de l’exécution d’une application Java classique (run time) sont effectués au moment de la construction (phase dite d’augmentation). Ainsi, lorsque l’application s’exécute, tout a été pré-calculé et l’analyse des annotations, l’analyse XML, etc. ne seront plus exécutées. Il a deux avantages : sur le temps de démarrage (beaucoup plus rapide) et sur la consommation de mémoire (beaucoup plus faible). La finalité de cette première étape est de produire un runner.jar. Vous pouvez constater qu’un répertoire de travail est créé dans /target, /kubernetes-quickstart-1.0.0-SNAPSHOT-native-image-source-jar, il y est copié les librairies utiles à l’application et le fameux runner.jar est crée en ayant fait les optimisations. Par la suite les élément de ce répertoire sont utilisés dans l’image Docker de build natif (quay.io/quarkus/ubi-quarkus-native-image:21.0.0-java11, grâce à cela pas besoin de GraalVM en local), afin de générer l’exécutable natif Linux qui permet de réduire d’environ 99% le temps de boot et d’environ 86% l’empreinte mémoire par rapports à des applications Java classiques.

La finalité de cette étape est le fichier /target/ kubernetes-quickstart-1.0.0-SNAPSHOT-runner, c’est l’exécutable proprement dit qui est destiné à fonctionner sur un OS Linux.

Une fois notre exécutable généré, l’étape de build de l’image Docker cible se lance (DockerProcessor, ligne 108-168).

Une fois l’image créée, celle-ci va être déployée dans notre registry Docker (ligne 169-183).

Note : Quarkus vient avec son propre DockerFile basé sur ubi, universal base image (c’est une RHEL minimale, Red Hat enterprise Linux), il y a un DockerFile par cible de build, une pour la JVM (fast-Jar par défaut à partir de la 1.12) et une pour une application native. Rien ne vous empêche d’utiliser vos DockerFile customisées.

Et enfin dernière étape (KubernetesDeployer, ligne 184-190), le fichier de déploiement, kubernetes.yml, généré par Quarkus va être appliqué dans k8s via le client k8S qui se connecte aux APIs du serveur.

Pour info voici le fichier de déploiement qui est généré par Quarkus

La fin des logs nous a bien indiqué un « BUILD SUCCESS », tout semble s’être bien déroulé, donc à présent vérifions dans le Dashboard que notre application Quarkus est bien déployée dans le namespace quarkus-apero-code

Une fois de plus tout est au vert, notre PostgreSQL et notre Quarkus semble fonctionner, donc nous devrions pouvoir accéder sans difficultés à notre application fraichement déployée.

Note : le build d’image native est très couteux en temps, si vous voulez aller plus vite pour déployer dans votre Microk8s vous avez simplement à ne pas positionner l’option -Pnative lors du lancement de la commande de package. /!\ Attention si vous faites une mise à jour de votre application Quarkus d’une version inférieur à la 1.12 sur une supérieur ou égal à la 1.12, par défaut en mode jvm c’est le concept de fast-jar qui est adopté (démarre plus rapidement que l’ancien uber-jar), il faut donc mettre à jour vos DockerFile.jvm avec celui-ci, et voir la note de migration ici pour plus de détails.

Ingress

Mais comment accéder à notre application, sur quelle URL, sur quel port? Pour rappel, ce qui est déployé dans k8s n’est pas exposé par défaut à l’extérieur du réseau interne kube. Pour cela nous avons besoin de déployer une « Ingress » qui sera la passerelle d’accès à notre service kubernetes-quickstart. Pour rappel

Ingress

Un Ingress est un objet Kubernetes qui gère l’accès externe aux services dans un cluster, généralement du trafic HTTP.
Un Ingress peut fournir un équilibrage de charge, une terminaison TLS et un hébergement virtuel basé sur un nom.

Un fichier de déploiement d’Ingress k8s est dans le projet Github, vous pouvez le voir ci-dessous

Et donc, vous pouvez constater que pour le service kubernetes-quickstart (le point d’accès interne kube à l’application Quarkus), j’indique que via le path /k8s-quickstart nous allons pouvoir y accéder depuis cette Ingress

A présent déployons notre Ingress à l’aide de la commande suivante

Vérifions si notre application est disponible à l’adresse du point d’entrée du cluster à savoir 192.168.64.2 sur le path définie dans mon fichier de déploiement qui est /k8s-quickstart, naviguons donc sur http://192.168.64.2/k8s-quickstart

Comme vous pouvez le constater notre application Quarkus est bien accessible depuis l’extérieur du cluster single-node Microk8s.

Il nous reste un point à vérifier toute fois, la page d’acceuil est bien accessible, mais qu’en est-il des données de la base qui ont été chargées au déploiement de notre application. Vérifions que Quarkus communique bien avec sa base de données en accédant à l’API Rest /beers qui expose les données de la base PostgreSQL, naviguons sur https://192.168.64.2/k8s-quickstart/beers

Well done! Cela fonctionne également.

Pour rappel, nous avons crée un BDD avec une table BEER_TABLE, que nous avons populé grâce à Quarkus qui intègre automatiquement les données au démarrage de l’application en s’appuyant sur un script SQL (cf application.properties), et on voit bien ci-dessus que les données sont accessibles.

En résumé, nous avons donc déployé une base de données PostgreSQL dans k8s, créé et déployé une application Quarkus, intégré des données à la base de données lors du déploiement de notre application, déployé une Ingress pour accéder à notre nouvelle application depuis l’extérieur du cluster k8s et enfin accédé à celle-ci via le navigateur.

Ce sera tout pour cette partie, vous pouvez retrouver le code de cette application sur mon github.

Lors de la prochaine partie nous verrons comment déployer un client et un serveur gRPC dans notre Microk8S.

Références

You may also like...