Le projet open source Apache Kafka est une plateforme, distribuée, d’event streaming, employée par de nombreuses entreprises pour implémenter leurs solutions logicielles. Mais, de manière concrète de quoi s’agit-il ? Qu’est-ce que Kafka entend par la notion d’event streaming ? Quelques éléments de réponse dans cet article d’introduction à son déploiement et utilisation.
Comme de nombreuses solutions logicielles élaborées, Kafka vient avec son jargon et ses concepts clés. Afin de bien appréhender son fonctionnement interne, nous allons donc commencer cet article en explicitant les éléments de son architecture et la terminologie utilisée par le projet pour désigner ceux-ci ainsi que la manière dont il conceptualise les données qu’il manipule.
1. Concepts clés de Kafka
Le projet Kafka se définit, avant tout, comme une plateforme d’event streaming, donc nous allons commencer par expliciter la signification de ce terme. En essence, il s’agit de capturer des données, en temps réel, depuis des sources diverses et variées, telles que des bases de données, des capteurs, des appareils mobiles, des microservices, et toutes autres sources logiciels. Après cette capture, ces informations peuvent être stockées pour être utilisées plus tard, ou être directement manipulées ou éditées, ou peuvent encore simplement déclencher d’autres processus associés, le tout, en temps réel. Dans ce contexte, il importe également de proprement diriger ou rediriger les informations vers les systèmes appropriés. En essence, cette technologie garantit un flux continu de données, et de leurs interprétations, afin que l’information adéquate soit disponible dès que possible.
Sans surprise, cette approche a de nombreux cas d’utilisation. On peut traiter ainsi des paiements en ligne ou des transactions financières, ou encore l’échange de stock. On peut également l’employer afin de suivre et observer une flotte de véhicules, le déplacement de colis en temps réel, ou bien collecter les données des clients et utilisateurs d’une application, pour réagir immédiatement à tout problème. Il y a même des applications médicales, on peut utiliser cette approche dans l’intention de surveiller les patients d’un hôpital et s’assurer que l’ensemble des traitements nécessaires soit dispensé, à la cadence requise. Dans notre industrie, on trouve aussi ce type de technologie au cœur d’architectures dites event driven et/ou de l’implémentation de microservices.
Revenons maintenant à Kafka en lui-même. Ce projet combine trois capacités clés, qui vont permettre à son utilisateur de construire sa solution logicielle, avec l’approche event streaming, en respectant le processus suivant :
- Publier (opération d’écriture) et souscrire (opération de lecture) à un flux d’évènements, incluant l’import ou l’export, en continu, des données depuis ou vers d’autres systèmes.
- Persister ces flux d’événements de manière durable, et fiable.
- Et effectuer le traitement nécessaire de ceux-ci, directement lorsqu’ils sont émis, ou plus tard, à la demande.
Kafka fournit l’ensemble de ses fonctionnalités de manière distribuée, et est ainsi en mesure de tenir la montée en charge, et d’être également résistant aux pannes. Le logiciel peut être déployé librement sur un système physique, une machine virtuelle, et bien sûr sous forme de conteneur, qu’il s’agisse d’un déploiement en interne ou sur un cloud.
Évoquons maintenant, dans ses grandes lignes, le fonctionnement interne de Kafka. Il est constitué d’une collection de clients et de serveurs distribués, qui communiquent à l’aide d’un protocole TCP. Celui-ci a été développé afin d’être le plus performant possible, quel que soit le contexte d’usage du logiciel. Ainsi, il n’y a pas de prérequis réseau à son fonctionnement, qui interdirait son utilisation au sein de cloud ou d’infrastructure physique ou virtuelle.
Au sein de son architecture, les serveurs de Kafka ont différentes fonctions, et peuvent être déployés au travers de plusieurs datacenters ou de zones d’un cloud. Certains d’entre eux forment une couche de stockage, que l’on désigne par le terme brokers. D’autres importent de manière continue des données, et s’intègrent ainsi avec des systèmes existants tels que des bases de données, ou d’autres instances de Kafka.
Tout ceci a été conçu avec la robustesse et la tolérance aux pannes à l’esprit. Ce qui veut dire que, si l’un de ces serveurs devient indisponible, les autres sont en mesure de reprendre sa charge et ses opérations, garantissant également la continuité des traitements en cours, sans aucune perte d’information.
De l’autre côté de son architecture, on trouve les clients. Ceux-ci permettent aux utilisateurs de Kafka de construire leur application distribuée et leurs microservices, qui vont lire ou écrire (ou traiter) les événements reçus, en parallèle, et de manière, encore une fois, tolérante aux pannes, qu’il s’agisse d’un incident réseau ou de l’indisponibilité d’une ou plusieurs machines.
Il faut noter que Kafka vient également avec plusieurs logiciels clients, prêts à l’emploi. Ceux-ci sont implémentés dans différentes technologies typiques de l’écosystème Java, telles que Scala, mais aussi d’autres, comme Go, Python ou encore C/C++. En outre, le logiciel disposant de nombreuses API REST, il est possible de l’intégrer à pratiquement toute technologie.
1.1 Terminologie
L’outil et sa problématique, décrits dans les grandes lignes, développons maintenant ses concepts clés et sa terminologie. Le premier élément de son lexique à appréhender correctement est, évidemment, la notion d’event. En essence, elle désigne l’enregistrement d’une information émise au sein d’un système. Celle-ci peut être une donnée purement logicielle ou le reflet d’une action ou d’un changement d’état dans le monde « physique ». Il peut s’agir, par exemple, de l’arrivée d’un colis à destination, de la souscription par un client d’une police d’assurance, de la réception de fonds sur un compte en banque, ou encore simplement de l’enregistrement d’une ligne dans une base de données. Notons sur ce point que la documentation réfère à ces événements également sous le terme d’enregistrements (record) ou de messages.
D’une manière générale, si une application lit ou écrit des données avec Kafka, ceux-ci prennent la forme d’événements. En termes de modèle de données, ces informations sont associées à une clé, une valeur, et un timestamp. On peut aussi ajouter un ensemble de métadonnées sous forme d’en tête (headers).
Voici, un exemple simple, d’un événement :
- Clé : “DoorOpens”
- Valeur : “la clé de Sophie a ouvert la porte de la maison, de l’intérieur.”
- Timestamp : "Mar. 25, 2022 at 7:06 a.m."
Toujours dans le jargon du logiciel, le terme suivant à retenir est celui de producteurs (Producers). Dans le contexte de Kafka, il s’agit d’applications clientes qui publient des données (opération en écriture) et qui en consomment d’autres, en souscrivant auprès d’autres systèmes. Il s’agit alors, dans ce dernier cas, d’opérations de lecture et de traitement de données. Il est important de retenir que, dans le cadre d’un déploiement du logiciel, les consommateurs comme les producteurs sont entièrement séparés. L’un ignore tout de l’implémentation de l’autre, ce qui est un élément essentiel au fonctionnement distribué et tolérant en panne de Kafka.
Un autre terme à bien comprendre dans le contexte de ce logiciel est la notion de sujet de discussion ou topic en anglais. Ceux-ci permettent d’organiser et de stocker de manière durable les événements enregistrés par le système. De manière simpliste, un topic est comparable à un répertoire d’un système de fichiers pour l’ensemble des événements. Ces derniers ne sont, dans cette analogie, que des fichiers au sein de celui-ci. Ainsi, on peut imaginer un topic « employé » contenant les informations de chaque membre du personnel d’une société.
Un point important à retenir ici est que les topics sont toujours des producteurs multiples et des souscripteurs multiples. Ainsi, l’un d’eux peut souscrire à aucun producteur, un seul ou plusieurs, et réciproquement.
Les lecteurs familiers avec le monde des Middleware Oriented Messaging (MOM), tels que les implémentations de la spécification JMS comme Apache MQ, verront une analogie directe avec la notion de topics au sein de ce type de système. Cependant, il y a une différence cruciale avec ceux-ci : les événements peuvent être lus et relus autant de fois que nécessaire. À l’intérieur d’un MOM, un message ne peut être consommé qu’une seule fois.
En outre, les événements ne sont pas automatiquement effacés après leur utilisation. Leur durée de vie au sein du système est définie selon le paramétrage du topic. Celui-ci indique combien de temps un événement doit être conservé avant d’être retiré du système. Il faut souligner ici que, grâce à son architecture, les performances de Kafka sont constantes, quelle que soit la taille du jeu de données, ainsi préserver celles-ci, sur une longue période, n’a pas d’impact sur l’efficacité du système.
Ces topics peuvent être partitionnés (partitioned), ce qui signifie que l’un d’entre eux peut être réparti sur un certain nombre de composants, désignés par le terme de buckets. Ces derniers peuvent être situés au sein de différents brokers. Cette organisation, fondamentalement distribuée des données, est clé, car elle permet au logiciel de tenir la montée en charge. Cette approche autorise, en effet, aux applications de lire et d’écrire des données de ou vers différents brokers au même instant.
Lorsqu’un nouvel événement est publié au sein d’un topic, il est, en fait, ajouté à l’une de ces partitions. Un événement doté de la même clé qu’un autre, par exemple l’identifiant d’une voiture, est écrit dans la même partition. En outre, le logiciel assure que tout consommateur d’un topic et de sa partition récupère les événements dans l’ordre exact dans lequel ils ont été ajoutés.
1.2 Distribution et réplication des données
Le dernier élément de langage à retenir dans le cadre de l’étude de Kafka est la notion de réplication. En effet, pour garantir la tolérance aux pannes et une haute disponibilité du système, chaque topic peut être répliqué, et ceci même sur différents centres de données ou zones d’un cloud. Ainsi, il y a toujours de multiples brokers détenant une copie des données, au cas où l’un d’eux devient indisponible. La plupart des systèmes en production ont jusqu’à trois instances du même jeu de données. Notez que cette réplication est réalisée au niveau de la partition d’un topic.
Les concepts clés et les cas d’utilisations de Kafka désormais explicités, la suite de cet article va se concentrer sur la mise en place et le déploiement de la solution. Afin de garantir une installation documentée et reproductible, nous emploierons Ansible pour effectuer les différentes étapes nécessaires à celle-ci. La syntaxe de l’outil d’automatisation ne sera pas expliquée dans cet article, pour rester concentrés sur notre cas d’étude, Kafka. Cependant, celle-ci est simple à appréhender. Le lecteur intéressé par le sujet pourra se référer aux précédents articles publiés sur Ansible.
2. Déploiement à l’aide d’Ansible
2.1 Prérequis
Nous allons commencer le déploiement de Kafka par la mise en place des prérequis suivants :
- récupération de l’archive Kafka depuis le site du projet ;
- décompression de celle-ci dans le répertoire d’installation ;
- installation dans la machine virtuelle Java requise pour son fonctionnement.
Ces opérations réalisées lors de l’exécution du playbook Ansible, nous avons tous les éléments nécessaires pour déployer, sur le système cible (en l’occurrence, le poste de travail utilisé) les différents composants de Kafka.
2.2 Démarrage des services de Kafka
Par sa nature profondément distribuée, Kafka requiert l’usage de plusieurs services, qui vont interagir les uns avec les autres. Tous ceux-ci peuvent être démarrés, à l’aide de scripts fournis par le projet.
Nous allons donc commencer par concevoir un playbook Ansible dédié à leur démarrage, que nous utiliserons ensuite pour lancer chaque composant :
Par souci de simplicité, et pour rester didactiques, nous avons choisi de ne pas gérer, au sein du playbook, l’état du serveur. Ainsi, si nous avons exécuté le script ci-dessus auparavant et que le serveur associé est déjà démarré, il est nécessaire d’ajouter la variable dédiée à celui-ci qui indique qu’il a été préalablement lancé, afin que l’outil d’automatisation n’exécute pas à nouveau cette opération. Cette approche est loin d’être élégante et de respecter les bonnes pratiques de l’utilisation d’Ansible, mais elle nous permet ici, dans le cadre de cet article, de ne pas nous égarer en devant intégrer complètement chacun des services dans systemd, ou un autre gestionnaire de service.
Voyons maintenant la mise en place du premier service requis par Kafka, Zookeeper :
Une fois Zookeeper démarré sans incident, nous pouvons maintenant mettre en place le second service requis par l’architecture de Kafka, le broker :
En guise d’alternative à Zookeeper, Kafka autorise également l’utilisation de Kraft, que nous ne présenterons pas ici.
2.3 Déploiement d’un topic
Le broker désormais fonctionnel, nous pouvons maintenant mettre en place un topic dans lequel nous ajouterons des événements par la suite. Là encore, Kafka vient avec un script, simple d’utilisation, qui permet de créer un topic :
On notera que, ici, nous avons pris soin de placer l’exécution de ce script au sein d’un bloc, afin de détecter l’erreur associée à l’existence du topic. Ainsi, lors de la seconde exécution de notre playbook, celle-ci ne sera pas interrompue par l’erreur retournée par le script.
2.4 Vérification du topic
Afin de confirmer que le topic a bien été créé sans erreur et que celui-ci est bien disponible, nous allons ajouter quelques tâches de vérification à notre playbook Ansible :
Avec ces deux éléments (Zookeeper et le broker) proprement configurés, installés et démarrés, nous pouvons maintenant placer des données au sein de notre topic. Pour ce faire, là encore, le projet Kafka fournit un script, simple d’utilisation, qui permet d’ajouter directement des événements :
Notez qu’il s’agit d’un script interactif, il faut donc, après l’avoir lancé, saisir les données à ajouter, un événement par ligne, puis interrompre l’exécution de celui-ci.
Observons également que nous avons opté pour associer un numéro à chaque évènement, afin de démontrer, lors de la prochaine étape, ci-dessous, que l’ordre d’arrivée des événements est bien respecté.
Conclusion
Cet article, nous l’espérons, aura présenté, dans ses grandes lignes, tout l’intérêt de déployer et utiliser Kafka, mais également de la facilité de son automatisation, à l’aide d’Ansible. Bien évidemment, il ne s’agit ici que d’une simple introduction, décrivant comment mettre en place les composants élémentaires de la solution. Il reste encore à découvrir comment intégrer ses brokers avec des sources d’événements et d’étudier ses capacités de haute disponibilité, en employant plusieurs brokers et en simulant des incidents sur le système. Cependant, ces premiers pas effectués dans cet article nous donnent les bases requises pour une telle exploration, plus en profondeur, de Kafka.