Chez Geneanet, comme beaucoup d’autres acteurs du monde numérique, nous sommes amenés à traiter d’énorme quantités de données. Sur ces données, que les utilisateurs peuvent souvent mettre à jour en temps réel (profils, données généalogiques, forums, commentaires ou encore documents numérisés), nous devons régulièrement effectuer des traitements plus longs : des sauvegardes, des statistiques, des indexations… L’approche traditionnelle à ces traitements est de les planifier de manière quotidienne, hebdomadaire ou mensuelle, selon le besoin, généralement en heure creuse afin de ne pas impacter les visiteurs du site. Et sous Linux, l’outil dédié à cette approche est cron.

Cron est l’ami de l’administrateur système Unix : il existe depuis la nuit des temps, est disponible de base dans toutes les distributions, il est facile à configurer et à priori exempt de bugs. Une tâche à effectuer régulièrement ? Pas de problèmes, une ligne de plus dans la configuration de cron et le travail sera effectué à l’heure dite.

[TL;DR] Pour zapper les explications ennuyeuses et essayer Moneta, c’est par ici !

Le coeur du problème

Mais là où cette technique fonctionne lorsqu’on héberge un petit site sur un seul serveur, elle atteint ses limites lorsqu’on fait fonctionner un gros site sur cinquante machines. On fait alors face à plusieurs problèmes :

  • Comment gérer la configuration de manière centralisée et simple ?
  • Comment répartir efficacement les tâches planifiées sur différents serveurs ?
  • Comment faire face à la panne d’un serveur sur lequel l’exécution d’une tâche importante était planifiée ?

L’approche moderne à ces questions de répartition de charge et de redondance consiste à utiliser l’architecture message queue + workers, en se basant par exemple sur RabbitMQ, découper les tâches à effectuer en petits éléments, mettre ceux-ci dans une file d’attente, puis permettre à plusieurs serveurs d’aller piocher dans cette file d’attente. Ainsi, les tâches seront équitablement réparties entre les serveurs et si l’un d’entre eux venait à tomber en panne, les autres s’occuperaient de sa part de travail.

Approche Message Queue + Workers

Nous utilisons ce modèle pour certains de nos traitements de données, mais à côté de cela, nous avions près de 200 tâches planifiées utilisant cron sur différentes machines. Ayant subi par le passé des pannes empêchant l’exécution de tâches importantes, nous avons décidé de sécuriser le système. La charge de travail pour convertir toutes ces tâches vers un modèle message queue + workers étant colossale -et le modèle étant parfois non applicable- il nous fallait trouver une solution.

La solution

Nos besoins étaient les suivants :

  • Redondance (une panne de serveur ne doit pas affecter l’exécution des tâches)
  • Répartition de charge
  • Possibilité de spécialiser certains serveurs (les serveurs n’étant pas identiques, certaines tâches doivent ne s’exécuter que sur certains groupes de serveurs)
  • Pas de modification du code des tâches existantes
  • Facilité de configuration identique à cron

Devant l’absence de solution open source existante, nous avons créé Moneta.

Moneta

Moneta remplace cron et propose bien plus de fonctionnalités et d’extensibilité. Il s’agit d’un démon, écrit en Python, fonctionnant sur chaque machine devant exécuter des tâches. Les différents noeuds Moneta fonctionnent en cluster, se découvrent et partagent leur configuration à travers un cluster ZooKeeper. Les communications entre l’utilisateur et le cluster Moneta, ou entre les différents noeuds du cluster, s’effectuent via une API REST.

Architecture de Moneta

Lorsque le cluster Moneta se met en place, une élection se tient via ZooKeeper afin de choisir l’un des noeuds comme leader. C’est lui qui va servir de chef d’orchestre et distribuer les tâches aux autres noeuds le moment venu. Si le leader tombe en panne, un autre noeud est immédiatement élu pour reprendre le rôle.

La configuration s’effectue via l’API REST accessible par n’importe quel noeud du cluster (la configuration étant partagée dans ZooKeeper).

Afin de ne pas surcharger le programme, les fonctionnalités autres que la gestion du cluster et l’ordonnancement des tâches sont gérées par un système de plug-ins. Ils servent notamment pour le reporting.

Voici les plug-ins actuellement disponibles :

  • mailer - envoie un compte rendu par mail après l’exécution d’une tâche
  • audit - enregistre dans ElasticSearch les comptes rendus d’exécution ainsi que l’historique des modifications apportées à la configuration
  • configbackup - sauvegarde la configuration du cluster Moneta dans un fichier json ou yml, plus facile à sauvegarder et historiser que le cluster ZooKeeper
  • executionsummary - conserve (dans ZooKeeper) pour chaque tâche la date, la durée et le résultat de la dernière exécution, afin d’avoir ces informations en un coup d’oeil sans avoir à parcourir de logs.

Enfin, une interface web basée sur AngularJS/Foundation est disponible pour utiliser toutes ces fonctionnalités de manière plus agréable.

Screencast Moneta-Web

Moneta répond ainsi complètement à nos besoins, et apporte comme avantage supplémentaire de permettre à des non techniciens (ou à des non sysadmin) de gérer les tâches planifiées sans problématiques de déploiement de configuration.

Qu’a-t-on appris en développant Moneta ?

Rentrer dans les détails du fonctionnement serait intéressant mais un peu rébarbatif - cela fera peut-être l’objet d’un article ultérieur, en revanche, en développant ce logiciel, nous avons appris un certain nombre de choses qui valent le coup d’être partagées.

Par exemple :

  • Que la gestion d’un cluster est une problématique complexe à laquelle on ne peut prétendre se frotter sans passer beaucoup de temps en conception à évaluer tous les cas de figure. Au départ nous ne souhaitions pas que Moneta soit dépendant d’un autre système, mais au vu de la complexité nécessaire pour gérer tous les cas de panne, nous avons choisi de reposer sur ZooKeeper dont la fiabilité est reconnue.
  • Que ZooKeeper est l’outil idéal pour gérer un cluster : connaître une liste de membres connectés, organiser une élection, partager une configuration ou encore mettre en place des verrous partagés sont quelques une des applications qu’on peut tirer des fonctions de base de ZooKeeper.

I tried to run a cluster

  • Que ce dernier ne peut toutefois pas être utilisé pour tout car il comporte certaines limites. D’une part, la taille des données stockées dans une clé ZooKeeper est limitée à 1Mo. D’autre part, son fonctionnement repose sur un journal de transactions conservé sur le disque, qui peut très vite grossir lorsque les opérations sur le cluster sont très fréquentes ou très volumineuses. Il est donc préférable d’effectuer ce type de communications par un autre canal.
  • Qu’il n’est pas simple de faire un système complètement transactionnel (qui s’assure que toute tâche devant être lancée l’est bien et n’est pas lancée deux fois, même si une panne ou un changement de leader survient au milieu du processus). A l’heure actuelle, si le leader tombe en panne alors qu’il est en train de distribuer des tâches, le nouveau leader redistribuera celles-ci plutôt que de les ignorer. Pour certaines applications il est donc important de mettre en place un verrou qui empêchera plusieurs exécutions simultanées, Moneta ne gérant pas cet aspect.
  • Qu’il n’est pas facile non plus de gérer la mise à jour des démons Moneta alors que ceux-ci sont en train d’exécuter des tâches parfois longues. A ce jour, il existe trois possibilités, aucune n’étant idéale :
    • Pour terminer Moneta et toutes les tâches en cours sur la machine, en envoyant comme il se doit les éventuels rapports d’exécution, il faut envoyer le signal TERM
    • Pour terminer Moneta en laissant fonctionner les tâches en cours il faut envoyer le signal QUIT. Dans ce cas, Moneta ayant perdu le contrôle des processus lancés, il n’y aura pas de rapports d’exécution.
    • Pour terminer Moneta après avoir attendu la fin de toutes les tâches en cours, il faut envoyer le signal WINCH. Dans ce cas, plus aucune nouvelle tâche planifiée n’est exécutée sur cette machine entre l’envoi du signal et la fin des tâches en cours, ce qui peut parfois poser problème.
  • Que la bibliothèque kazoo, que nous utilisons pour la connexion à ZooKeeper, étant basé sur gevent, il fallait un serveur HTTP basé lui aussi sur gevent pour faire fonctionner notre API REST. Ou bien, à l’inverse, il fallait adapter kazoo pour le faire fonctionner avec twisted par exemple. Nous avons fait le choix de la première solution, et développé un serveur HTTP minimal basé sur gevent. Ça fait au passage une dépendance de moins, ce qui est toujours agréable !

Utilisez, participez !

La totalité de notre infrastructure repose sur du logiciel libre ou open source, de l’OS, Linux, au système de gestion des arbres généalogiques, Geneweb, en passant par le moteur de recherche basé sur ElasticSearch. Sachant que nous ne sommes pas les seuls à avoir de nombreuses tâches historiquement gérées par Cron, c’était donc une évidence pour nous de proposer ce développement à la communauté.

Moneta et son interface sont donc disponibles sur GitHub, sous licence BSD :

Le système est d’ores et déjà fonctionnel (nous l’utilisons en production pour 180 tâches sur 20 machines, avec un cluster de 3 instances ZooKeeper) mais il n’est pas forcément parfait. Et les idées ne manquent pas pour l’améliorer :

  • Meilleur validation des données en entrée (configuration des tâches)
  • Possibilité de relancer une tâche dont l’exécution se terminerait en erreur
  • Meilleur autonomie des tâches exécutées, permettant de redémarrer un démon Moneta sans perdre le contrôle des tâches en cours d’exécution
  • Conserver un état général transactionnel des tâches en cours d’exécution sur le cluster (actuellement il faut interroger chaque démon Moneta via son API)…

Nous vous invitons donc à essayer Moneta et ne pas hésiter à nous rapporter des bugs ou proposer des améliorations !

I don't always need to schedule tasks

Résumé des technos utilisées

  • Python
  • ZooKeeper - Pour la centralisation de la configuration et la synchronisation entre les noeuds
  • kazoo - Pour la communication avec Zookeeper
  • gevent - Pour la communication réseau (REST/HTTP) et les “green threads”
  • ElasticSearch - Pour le stockage des rapports d’exécution
  • AngularJS - Pour le GUI web
  • Foundation - Pour le GUI web

Pourquoi “Moneta” ?

Moneta fait référence à un personnage de la série de romans de science-fiction Hypérion, de Dan Simmons. C’est une jeune femme qui parcours le temps vers le passé afin de s’assurer que ce qui doit/devait se produire se/s’est produit.

Pour un système de planification devant s’assurer que tout marche bien même en cas de pannes, le nom paraissait pertinent ;-)

Commentaires