Mise à l'échelle de Scala : comment dockeriser à l'aide de Kubernetes

Publié: 2022-03-11

Kubernetes est le nouveau venu, promettant d'aider à déployer des applications dans le cloud et à les faire évoluer plus rapidement. Aujourd'hui, lors du développement d'une architecture de microservices, il est assez courant de choisir Scala pour créer des serveurs d'API.

Les microservices remplacent les serveurs back-end monolithiques classiques par de multiples services indépendants qui communiquent entre eux et disposent de leurs propres processus et ressources.

S'il y a une application Scala dans vos plans et que vous souhaitez la mettre à l' échelle dans un cloud, alors vous êtes au bon endroit. Dans cet article, je vais montrer étape par étape comment prendre une application Scala générique et implémenter Kubernetes avec Docker pour lancer plusieurs instances de l'application. Le résultat final sera une application unique déployée en plusieurs instances et dont la charge sera équilibrée par Kubernetes.

Tout cela sera implémenté en important simplement le kit source Kubernetes dans votre application Scala. Attention, le kit cache beaucoup de détails compliqués liés à l'installation et à la configuration, mais il est suffisamment petit pour être lisible et facile à comprendre si vous souhaitez analyser ce qu'il fait. Pour plus de simplicité, nous allons tout déployer sur votre machine locale. Cependant, la même configuration convient à un déploiement cloud réel de Kubernetes.

Faites évoluer votre application Scala avec Kubernetes

Soyez intelligent et dormez bien, faites évoluer votre Docker avec Kubernetes.
Tweeter

Qu'est-ce que Kubernetes ?

Avant d'entrer dans les détails sanglants de la mise en œuvre, discutons de ce qu'est Kubernetes et pourquoi c'est important.

Vous avez peut-être déjà entendu parler de Docker. Dans un sens, c'est une machine virtuelle légère.

Docker offre l'avantage de déployer chaque serveur dans un environnement isolé, très similaire à une machine virtuelle autonome, sans la complexité de la gestion d'une machine virtuelle à part entière.

Pour ces raisons, c'est déjà l'un des outils les plus largement utilisés pour déployer des applications dans les nuages. Une image Docker est assez facile et rapide à construire et à dupliquer, beaucoup plus facile qu'une machine virtuelle traditionnelle comme VMWare, VirtualBox ou XEN.

Kubernetes complète Docker, offrant un environnement complet pour la gestion des applications dockerisées. En utilisant Kubernetes, vous pouvez facilement déployer, configurer, orchestrer, gérer et surveiller des centaines voire des milliers d'applications Docker.

Kubernetes est un outil open source développé par Google et adopté par de nombreux autres fournisseurs. Kubernetes est disponible nativement sur la plate-forme cloud de Google, mais d'autres fournisseurs l'ont également adopté pour leurs services cloud OpenShift. Il peut être trouvé sur Amazon AWS, Microsoft Azure, RedHat OpenShift et bien d'autres technologies cloud. On peut dire qu'il est bien positionné pour devenir un standard de déploiement d'applications cloud.

Conditions préalables

Maintenant que nous avons couvert les bases, vérifions si vous avez installé tous les logiciels prérequis. Tout d'abord, vous avez besoin de Docker. Si vous utilisez Windows ou Mac, vous avez besoin de la boîte à outils Docker. Si vous utilisez Linux, vous devez installer le package particulier fourni par votre distribution ou simplement suivre les instructions officielles.

Nous allons coder en Scala, qui est un langage JVM. Vous avez bien sûr besoin du kit de développement Java et de l'outil scala SBT installés et disponibles dans le chemin global. Si vous êtes déjà un programmeur Scala, il est probable que ces outils soient déjà installés.

Si vous utilisez Windows ou Mac, Docker créera par défaut une machine virtuelle nommée default avec seulement 1 Go de mémoire, ce qui peut être trop petit pour exécuter Kubernetes. D'après mon expérience, j'ai eu des problèmes avec les paramètres par défaut. Je vous recommande d'ouvrir l'interface graphique de VirtualBox, de sélectionner la default de votre machine virtuelle et de modifier la mémoire à au moins 2048 Mo.

Paramètres de mémoire VirtualBox

L'application à clusteriser

Les instructions de ce didacticiel peuvent s'appliquer à n'importe quelle application ou projet Scala. Pour que cet article ait de la "viande" sur laquelle travailler, j'ai choisi un exemple très souvent utilisé pour démontrer un microservice REST simple dans Scala, appelé Akka HTTP. Je vous recommande d'essayer d'appliquer le kit source à l'exemple suggéré avant d'essayer de l'utiliser sur votre application. J'ai testé le kit par rapport à l'application de démonstration, mais je ne peux pas garantir qu'il n'y aura pas de conflits avec votre code.

Alors d'abord, nous commençons par cloner l'application de démonstration :

 git clone https://github.com/theiterators/akka-http-microservice

Ensuite, testez si tout fonctionne correctement :

 cd akka-http-microservice sbt run

Ensuite, accédez à http://localhost:9000/ip/8.8.8.8 et vous devriez voir quelque chose comme dans l'image suivante :

Le microservice HTTP Akka est en cours d'exécution

Ajout du kit source

Maintenant, nous pouvons ajouter le kit source avec un peu de magie Git :

 git remote add ScalaGoodies https://github.com/sciabarra/ScalaGoodies git fetch --all git merge ScalaGoodies/kubernetes

Avec cela, vous avez la démo, y compris le kit source, et vous êtes prêt à essayer. Ou vous pouvez même copier et coller le code à partir de là dans votre application.

Une fois que vous avez fusionné ou copié les fichiers dans vos projets, vous êtes prêt à commencer.

Démarrer Kubernetes

Une fois que vous avez téléchargé le kit, nous devons télécharger le binaire kubectl nécessaire, en exécutant :

 bin/install.sh

Ce programme d'installation est assez intelligent (espérons-le) pour télécharger le bon binaire kubectl pour OSX, Linux ou Windows, selon votre système. Remarque, l'installateur a travaillé sur les systèmes que je possède. Veuillez signaler tout problème afin que je puisse réparer le kit.

Une fois que vous avez installé le binaire kubectl , vous pouvez démarrer l'ensemble de Kubernetes dans votre Docker local. Exécutez simplement :

 bin/start-local-kube.sh

La première fois qu'elle est exécutée, cette commande téléchargera les images de l'ensemble de la pile Kubernetes, ainsi qu'un registre local nécessaire pour stocker vos images. Cela peut prendre un certain temps, alors soyez patient. Notez également qu'il a besoin d'un accès direct à Internet. Si vous êtes derrière un proxy, ce sera un problème car le kit ne prend pas en charge les proxys. Pour le résoudre, vous devez configurer les outils comme Docker, curl, etc. pour utiliser le proxy. C'est suffisamment compliqué pour que je recommande d'obtenir un accès illimité temporaire.

En supposant que vous avez réussi à tout télécharger, pour vérifier si Kubernetes fonctionne correctement, vous pouvez taper la commande suivante :

 bin/kubectl get nodes

La réponse attendue est :

 NAME STATUS AGE 127.0.0.1 Ready 2m

Notez que l'âge peut varier, bien sûr. De plus, comme le démarrage de Kubernetes peut prendre un certain temps, vous devrez peut-être appeler la commande plusieurs fois avant de voir la réponse. Si vous n'obtenez pas d'erreurs ici, félicitations, Kubernetes est opérationnel sur votre machine locale.

Dockeriser votre application Scala

Maintenant que Kubernetes est opérationnel, vous pouvez y déployer votre application. Autrefois, avant Docker, vous deviez déployer un serveur entier pour exécuter votre application. Avec Kubernetes, il vous suffit de déployer votre application :

  • Créez une image Docker.
  • Poussez-le dans un registre à partir duquel il peut être lancé.
  • Lancez l'instance avec Kubernetes, qui prendra l'image du registre.

Heureusement, c'est beaucoup moins compliqué qu'il n'y paraît, surtout si vous utilisez l'outil de construction SBT comme beaucoup le font.

Dans le kit, j'ai inclus deux fichiers contenant toutes les définitions nécessaires pour créer une image capable d'exécuter des applications Scala, ou du moins ce qui est nécessaire pour exécuter la démo HTTP Akka. Je ne peux pas garantir que cela fonctionnera avec d'autres applications Scala, mais c'est un bon point de départ et devrait fonctionner pour de nombreuses configurations différentes. Les fichiers à rechercher pour construire l'image Docker sont :

 docker.sbt project/docker.sbt

Voyons ce qu'il y a dedans. Le fichier project/docker.sbt contient la commande pour importer le plugin sbt-docker :

 addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0")

Ce plugin gère pour vous la construction de l'image Docker avec SBT. La définition Docker se trouve dans le fichier docker.sbt et ressemble à ceci :

 imageNames in docker := Seq(ImageName("localhost:5000/akkahttp:latest")) dockerfile in docker := { val jarFile: File = sbt.Keys.`package`.in(Compile, packageBin).value val classpath = (managedClasspath in Compile).value val mainclass = mainClass.in(Compile, packageBin).value.getOrElse(sys.error("Expected exactly one main class")) val jarTarget = s"/app/${jarFile.getName}" val classpathString = classpath.files.map("/app/" + _.getName) .mkString(":") + ":" + jarTarget new Dockerfile { from("anapsix/alpine-java:8") add(classpath.files, "/app/") add(jarFile, jarTarget) entryPoint("java", "-cp", classpathString, mainclass) } }

Pour bien comprendre la signification de ce fichier, vous devez connaître suffisamment Docker pour comprendre ce fichier de définition. Cependant, nous n'entrons pas dans les détails du fichier de définition Docker, car vous n'avez pas besoin de le comprendre à fond pour construire l'image.

La beauté de l'utilisation de SBT pour créer l'image Docker est que
le SBT se chargera de collecter tous les fichiers pour vous.

Notez que le classpath est automatiquement généré par la commande suivante :

 val classpath = (managedClasspath in Compile).value

En général, il est assez compliqué de rassembler tous les fichiers JAR pour exécuter une application. En utilisant SBT, le fichier Docker sera généré avec add(classpath.files, "/app/") . De cette façon, SBT collecte tous les fichiers JAR pour vous et construit un Dockerfile pour exécuter votre application.

Les autres commandes rassemblent les pièces manquantes pour créer une image Docker. L'image sera construite à l'aide d'une image APT existante pour exécuter des programmes Java ( anapsix/alpine-java:8 , disponible sur Internet dans le Docker Hub). D'autres instructions ajoutent les autres fichiers pour exécuter votre application. Enfin, en spécifiant un point d'entrée, nous pouvons l'exécuter. Notez également que le nom commence exprès par localhost: localhost:5000 , car localhost:5000 est l'endroit où j'ai installé le registre dans le script start-kube-local.sh .

Construire l'image Docker avec SBT

Pour construire l'image Docker, vous pouvez ignorer tous les détails du Dockerfile. Il vous suffit de taper :

 sbt dockerBuildAndPush

Le plugin sbt-docker créera alors une image Docker pour vous, en téléchargeant depuis Internet toutes les pièces nécessaires, puis il poussera vers un registre Docker qui a été démarré auparavant, avec l'application Kubernetes dans localhost . Donc, tout ce dont vous avez besoin est d'attendre un peu plus pour que votre image soit cuite et prête.

Attention, si vous rencontrez des problèmes, la meilleure chose à faire est de tout remettre dans un état connu en exécutant les commandes suivantes :

 bin/stop-kube-local.sh bin/start-kube-local.sh

Ces commandes doivent arrêter tous les conteneurs et les redémarrer correctement pour que votre registre soit prêt à recevoir l'image construite et poussée par sbt .

Démarrage du service dans Kubernetes

Maintenant que l'application est empaquetée dans un conteneur et poussée dans un registre, nous sommes prêts à l'utiliser. Kubernetes utilise la ligne de commande et les fichiers de configuration pour gérer le cluster. Étant donné que les lignes de commande peuvent devenir très longues et également pouvoir reproduire les étapes, j'utilise ici les fichiers de configuration. Tous les échantillons du kit source se trouvent dans le dossier kube .

Notre prochaine étape consiste à lancer une seule instance de l'image. Une image en cours d'exécution est appelée, dans le langage Kubernetes, un pod . Créons donc un pod en invoquant la commande suivante :

 bin/kubectl create -f kube/akkahttp-pod.yml

Vous pouvez maintenant inspecter la situation avec la commande :

 bin/kubectl get pods

Tu devrais voir:

 NAME READY STATUS RESTARTS AGE akkahttp 1/1 Running 0 33s k8s-etcd-127.0.0.1 1/1 Running 0 7d k8s-master-127.0.0.1 4/4 Running 0 7d k8s-proxy-127.0.0.1 1/1 Running 0 7d

Le statut peut en fait être différent, par exemple, "ContainerCreating", cela peut prendre quelques secondes avant qu'il ne devienne "Running". De plus, vous pouvez obtenir un autre statut comme "Erreur" si, par exemple, vous oubliez de créer l'image avant.

Vous pouvez également vérifier si votre pod est en cours d'exécution avec la commande :

 bin/kubectl logs akkahttp

Vous devriez voir une sortie se terminant par quelque chose comme ceci :

 [DEBUG] [05/30/2016 12:19:53.133] [default-akka.actor.default-dispatcher-5] [akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /0:0:0:0:0:0:0:0:9000

Le service est maintenant opérationnel à l'intérieur du conteneur. Cependant, le service n'est pas encore joignable. Ce comportement fait partie de la conception de Kubernetes. Votre pod est en cours d'exécution, mais vous devez l'exposer explicitement. Sinon, le service est censé être interne.

Création d'un service

La création d'un service et la vérification du résultat consistent à exécuter :

 bin/kubectl create -f kube/akkahttp-service.yaml bin/kubectl get svc

Vous devriez voir quelque chose comme ceci :

 NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE akkahttp-service 10.0.0.54 9000/TCP 44s kubernetes 10.0.0.1 <none> 443/TCP 3m

Notez que le port peut être différent. Kubernetes a alloué un port pour le service et l'a démarré. Si vous utilisez Linux, vous pouvez directement ouvrir le navigateur et taper http://10.0.0.54:9000/ip/8.8.8.8 pour voir le résultat. Si vous utilisez Windows ou Mac avec Docker Toolbox, l'adresse IP est locale à la machine virtuelle qui exécute Docker et, malheureusement, elle est toujours inaccessible.

Je tiens à souligner ici qu'il ne s'agit pas d'un problème de Kubernetes, mais plutôt d'une limitation de la Docker Toolbox, qui à son tour dépend des contraintes imposées par les machines virtuelles comme VirtualBox, qui agissent comme un ordinateur dans un autre ordinateur. Pour surmonter cette limitation, nous devons créer un tunnel. Pour faciliter les choses, j'ai inclus un autre script qui ouvre un tunnel sur un port arbitraire pour atteindre n'importe quel service que nous avons déployé. Vous pouvez taper la commande suivante :

 bin/forward-kube-local.sh akkahttp-service 9000

Notez que le tunnel ne fonctionnera pas en arrière-plan, vous devez garder la fenêtre du terminal ouverte tant que vous en avez besoin et la fermer lorsque vous n'avez plus besoin du tunnel. Pendant que le tunnel est en cours d'exécution, vous pouvez ouvrir : http://localhost:9000/ip/8.8.8.8 et enfin voir l'application s'exécuter dans Kubernetes.

Touche finale : Échelle

Jusqu'à présent, nous avons "simplement" mis notre application dans Kubernetes. Bien qu'il s'agisse d'une réalisation passionnante, elle n'ajoute pas trop de valeur à notre déploiement. Nous sommes épargnés de l'effort de téléchargement et d'installation sur un serveur et de la configuration d'un serveur proxy pour celui-ci.

Là où Kubernetes brille, c'est dans la mise à l'échelle. Vous pouvez déployer deux, dix ou cent instances de notre application en modifiant uniquement le nombre de répliques dans le fichier de configuration. Alors faisons-le.

Nous allons arrêter le pod unique et démarrer un déploiement à la place. Exécutons donc les commandes suivantes :

 bin/kubectl delete -f kube/akkahttp-pod.yml bin/kubectl create -f kube/akkahttp-deploy.yaml

Ensuite, vérifiez l'état. Encore une fois, vous pouvez essayer plusieurs fois car le déploiement peut prendre un certain temps :

 NAME READY STATUS RESTARTS AGE akkahttp-deployment-4229989632-mjp6u 1/1 Running 0 16s akkahttp-deployment-4229989632-s822x 1/1 Running 0 16s k8s-etcd-127.0.0.1 1/1 Running 0 6d k8s-master-127.0.0.1 4/4 Running 0 6d k8s-proxy-127.0.0.1 1/1 Running 0 6d

Maintenant, nous avons deux pods, pas un. En effet, dans le fichier de configuration que j'ai fourni, il y a la valeur replica: 2 , avec deux noms différents générés par le système. Je n'entrerai pas dans les détails des fichiers de configuration, car la portée de l'article est simplement une introduction permettant aux programmeurs Scala de se lancer dans Kubernetes.

Quoi qu'il en soit, il y a maintenant deux pods actifs. Ce qui est intéressant, c'est que le service est le même qu'avant. Nous avons configuré le service pour équilibrer la charge entre tous les pods étiquetés akkahttp . Cela signifie que nous n'avons pas à redéployer le service, mais nous pouvons remplacer l'instance unique par une répliquée.

On peut le vérifier en lançant à nouveau le proxy (si vous êtes sous Windows et que vous l'avez fermé) :

 bin/forward-kube-local.sh akkahttp-service 9000

Ensuite, nous pouvons essayer d'ouvrir deux fenêtres de terminal et voir les journaux de chaque pod. Par exemple, dans le premier type :

 bin/kubectl logs -f akkahttp-deployment-4229989632-mjp6u

Et dans le second type :

 bin/kubectl logs -f akkahttp-deployment-4229989632-s822x

Bien sûr, modifiez la ligne de commande en conséquence avec les valeurs que vous avez dans votre système.

Maintenant, essayez d'accéder au service avec deux navigateurs différents. Vous devez vous attendre à voir les requêtes être réparties entre les multiples serveurs disponibles, comme dans l'image suivante :

Kubernet en action

Conclusion

Aujourd'hui, nous avons à peine effleuré la surface. Kubernetes offre beaucoup plus de possibilités, notamment la mise à l'échelle et le redémarrage automatisés, les déploiements incrémentiels et les volumes. De plus, l'application que nous avons utilisée comme exemple est très simple, sans état, les différentes instances n'ayant pas besoin de se connaître. Dans le monde réel, les applications distribuées doivent se connaître et doivent changer de configuration en fonction de la disponibilité des autres serveurs. En effet, Kubernetes propose un keystore distribué ( etcd ) pour permettre aux différentes applications de communiquer entre elles lors du déploiement de nouvelles instances. Cependant, cet exemple est délibérément suffisamment petit et simplifié pour vous aider à démarrer, en vous concentrant sur les fonctionnalités de base. Si vous suivez le tutoriel, vous devriez pouvoir obtenir un environnement de travail pour votre application Scala sur votre machine sans être dérouté par un grand nombre de détails et sans vous perdre dans la complexité.

En rapport:
  • Développer pour le cloud dans le cloud : développement BigData avec Docker dans AWS
  • K8s/Kubernetes : AWS contre GCP contre Azure
  • Comparaison du maillage de services Kubernetes