Mettre en place un monitoring de Zookeeper

Logo Zookeeper

Dans un article précédent « Zookeeper : Installation et bonnes pratiques« , j’ai indiqué comment installer et configurer Zookeeper. J’ai également indiqué quelques bonnes pratiques afin d’obtenir un ensemble Zookeeper stable et performant. Un rappel du fonctionnement et du rôle de Zookeeper dans un environnement SolrCloud aurait été judicieux. Je vais donc commencer par rattraper cette lacune avant d’aborder le sujet du monitoring de Zookeeper.

Rôle de Zookeeper dans un environnement SolrCloud

Zookeeper est un projet open-source de la fondation Apache qui a pour rôle de permettre aux composants d’un système distribué (les nœuds Solr pour SolrCloud) de se synchroniser. Il s’agit d’une base de données clé / valeur où la clé est plutôt un chemin et la valeur est un « znode« . Un znode est constitué de meta-données et éventuellement d’un fichier. Les composants du système distribué se connectent à Zookeeper afin d’y lire ou d’y écrire des informations. Il peut s’agir de fichiers de configurations (ceux d’une collection Solr) de fichiers de statut (d’un nœud Solr ou d’une collection). Outre Solr, les principaux projets open-source qui utilisent Zookeeper sont :

Zookeeper est donc embarqué dans toutes les stacks big data dont Cloudera / HortonWorks (qui ont fusionnées début 2019) et MAPR.

Un ensemble Zookeeper est constitué de plusieurs membres répliqués (en général 3 ou 5) afin d’être hautement disponible. Cette haute disponibilité est indispensable car si l’ensemble Zookeeper ne fonctionne pas, c’est tout les systèmes distribués l’utilisant qui ne peuvent plus fonctionner.

Dans le contexte de SolrCloud, tous les nœuds Solr se connectent à un des membres de l’ensemble Zookeeper (sauf au membre qui est le leader). Ils envoient régulièrement des pings au serveur Zookeeper auquel ils sont connectés afin de leur indiquer qu’ils sont actifs. Le serveur Zookeeper concerné répond avec une approbation, signalant ainsi qu’il est également actif. En cas de non réponse, le nœud Solr se connecte à un autre serveur de l’ensemble Zookeeper.

Le principe est que chaque membre Zookeeper conserve les données en mémoire. Les requêtes en lecture sont donc très rapides et sans accès disque. Lors d’une écriture par l’un des clients, l’ensemble Zookeeper se synchronise. Cette synchronisation implique des échanges entre les différents membres. Le membre qui reçoit l’écriture la transmet au « leader » qui lui-même la transmet aux autres membres (les « followers »).

Architecture de Zookeeper

Le point important est que suite à une écriture, toutes les lectures par les clients sont bloquées tant que tous les membres Zookeeper n’ont pas écrit sur disque (dans leur transaction log) les données modifiées. D’un autre coté, les clients ne lisent pas fréquemment dans Zookeeper car ils gardent un cache du contenu de Zookeeper. Ce dernier les notifie au moyen de « watches » en cas de modifications .

Les données Solr dans Zookeeper sont :

  • les configuration globales : autoscaling, security.json
  • les configuration des collections
  • l’état des collections (state.json, leaders, …)
  • l’état des nœuds Solr (live_nodes, overseer)

Des écritures sont réalisées dans Zookeeper lorsque :

  • un nœud Solr démarre ou s’arrête
  • une configuration de collection est envoyée
  • une collection est créée, supprimée ou mise à jour (donc lors de presque tous les appels aux Collection API, Core API et Config API)

Des écritures NE SONT PAS réalisées dans Zookeeper lorsque :

  • un objet client SolrJ est créé (mais il y a bien lecture dans Zookeeper)
  • des données sont indexées (Solrj, HTTP, DIH)
  • des données sont recherchées (Solrj, HTTP)

Des lectures sont réalisées dans Zookeeper lorsque :

  • un objet client SolrJ est créé
  • un nœud Solr démarre
  • Zookeeper notifie d’une modification suite à une écriture

Ces explications permettent de comprendre en quoi Zookeeper est critique pour Solr, mais surtout de comprendre les conditions qui peuvent fortement solliciter Zookeeper. A savoir :

  • Un grand nombre de nœuds Solr (les arrêts, les démarrages et les notifications de modifications)
  • Un grand nombre de d’appels aux API de gestion des collections (création, suppression, resharding, …)

Si un environnement SolrCloud ne subit pas d’arrêt / démarrage de nœuds Solr et si les collections sont stables avec uniquement des indexations et des recherches, l’ensemble Zookeeper est donc très peu sollicité.

Monitoring de Zookeeper

Préparation et configuration

Afin de pouvoir réaliser une monitoring de Zookeeper complet, nous pouvons utiliser plusieurs sources d’information :

  • Les logs de la JVM de Zookeeper pour contrôler les GC
  • Les métriques systèmes au moyen du node-exporter Prométheus
  • Les données fournies pas la JVM de Zookeeper au moyen du JMX exporter de Prometheus
  • Les métriques Zookeeper au moyen du Zookeeper exporter de Prometheus

Les métriques obtenus sont exploitables sous forme de graphes et d’alertes dans Prometheus et Graphana. Je ne reviens pas ici sur l’installation de ces composants car je l’ai déjà fait dans cet article « Monitoring SolrCloud avec Prometheus et Grafana« .

Pour les exemples de requêtes Prometehus indiquées ci-dessous, les serveurs Zookeeper se nomment zk1, zk2 et zk3.

Les exporters sont configurés pour utiliser les ports suivants :

  • node-exporter : 9100
  • jmx-exporter : 7070
  • zk-exporter : 7080

Les log des garbages collections de la JVM

Ces logs ne sont pas utilisés pour monitorer directement le fonctionnement de la JVM, mais ils permettent d’obtenir une analyse de son fonctionnement au moyen d’un outil tel que gceasy.io. Gceasy fournit un rapport du foncyionnement de la JVM (usage de la mémoire heap, fréquence et performances des GC, …).

Les métriques systèmes (node-exporter)

Pour un environnement SolrCoud de petite et moyenne dimension (moins de 20 serveurs Solr) avec peu de créations, suppressions et modifications de collections, la charge sur les serveurs ne doit pas être un sujet d’inquiétude. Cependant, par principe et pour une analyse à posteriori de problèmes sur l’environnement SolrCloud, les métriques systèmes suivants sont monitorés pour chaque serveur Zookeeper.

Métrique Description et requête Prometheus
Charge CPU Les charges CPU « user », « system » et « iowait » sont plus particulièrement à monitorer.

Requêtes pour le serveur zk1
avg by (mode)(irate(node_cpu_seconds_total{instance= »zk1:9100″,mode= »user »}[1m])) * 100
avg by (mode)(irate(node_cpu_seconds_total{instance= »zk1:9100″,mode= »system »}[1m])) * 100
avg by (mode)(irate(node_cpu_seconds_total{instance= »zk1:9100″,mode= »iowait »}[1m])) * 100

Alertes
Déterminer les charges CPU lors d’un fonctionnement nominal sans erreur.
Générer une alerte si ces valeurs sont doublées.
Générer une alerte si la charge CPU « user » est supérieur à 20%.
Générer une alerte si la charge CPU « iowait » est supérieur à 5%.
Load CPULe Load CPU à 1, 5 et 15 minutes

Requêtes pour le serveur zk1
node_load1{instance= »zk3:9100″}
node_load5{instance= »zk3:9100″}
node_load15{instance= »zk3:9100″}

Alertes
Si load supérieur au nombre de cpu.
Espace disque L’un et/ou l’autre de ces métriques peuvent être utilisés.

* Le pourcentage d’espace disque utilisé sur les partitions des données et des transactions logs.

Requêtes pour le serveur zk1 et une partition « /dev/xxx »
100-(node_filesystem_avail_bytes{instance= »zk1:9100″,device= »/dev/xxx »} / node_filesystem_size_bytes{instance= »zk1:9100″,device= »/devxxx »}*100)

Alertes
Si espace disque utilisé > 50%

* L’espace disque restant sur les partitions des données et des transactions logs.

Requêtes pour le serveur zk1 et une partition « /dev/xxx »
node_filesystem_avail_bytes{instance= »zk1:9100″,device= »/dev/xxx »} / 1024 / 1024

Alertes
Si espace disque restant < 1 Go
I/O disque Latence en ms pour les écritures (à l’exception du démarrage, Zookeeper ne fait que écrire).

Requêtes pour le serveur zk1 et un device « xxx »
rate(node_disk_write_time_seconds_total{instance= »zk1:9100″,device= »xxx »}[1m])/rate(node_disk_writes_completed_total{instance= »zk1:9100″,device= »xxx »}[1m])

Alertes
Si latence > 1ms
Erreurs réseauLe but est de détecter des erreurs de transmission ou de réception de paquets. On surveille le pourcentage de paquets en erreur par rapport aux nombre de paquets transmis et reçus.

Requêtes pour le serveur zk1 et une interface « xxx »
rate(node_network_transmit_errs_total{instance= »zk1:9100″,device= »xxx »}[1m]) /rate(node_network_transmit_packets_total{instance= »zk1:9100″,device= »xxx »}[1m])*100
rate(node_network_receive_errs_total{instance= »zk1:9100″,device= »xxx »}[1m]) /rate(node_network_receive_packets_total{instance= »zk1:9100″,device= »xxx »}[1m])*100

Alerte
Si > 1%

Les métriques JMX de la JVM (jmx-exporter)

Il s’agit de surveiller le fonctionnement de la JVM de Zookeeper. Nous nous intéresserons plus particulièrement à l’usage de la mémoire heap et des GC.

Métrique Description et requête Prometheus
Heap utiliséeLes 3 mesures suivantes sont à surveiller pour bien suivre l’utilisation de la mémoire heap

* Mémoire heap courante (Mo)

Requêtes pour le serveur zk1
java_lang_Memory_HeapMemoryUsage_used{instance= »zk1:7070″} / 1024 / 1024

* Mémoire heap maximum sur la dernière heure (Mo)

Requêtes pour le serveur zk1
max_over_time(java_lang_Memory_HeapMemoryUsage_used {instance= »zk1:7070″}[1h])/1024/1024

* Mémoire heap maximum sur la dernière journée (Mo)
Utiliser ce métrique pour déterminer la valeur de heap à configurer. Ne pas allouer plus de deux fois cette valeur avec le paramètre Xmx.

Requêtes pour le serveur zk1
max_over_time(java_lang_Memory_HeapMemoryUsage_used {instance= »zk1:7070″}[1d])/1024/1024

* Mémoire heap minimum sur les 5 dernières minutes (Mo)
Utilisée cette valeur pour alerter sur une mémoire heap haute de manière durable. Pourquoi monitorer la mémoire heap minimun sur les 5 dernières minutes ? Parce que la JVM a tendance à utiliser un maximum de mémoire heap avec d’en libérer lors des GC et donc la taille maximum est atteinte très fréquemment sans que cela soit un signe de problème. Par contre, les GC font redescendre très régulièrement la quantité de mémoire heap utilisée. Lorsque cette quantité de mémoire heap utilisée ne descend plus, c’est donc les GC ne remplissent plus leur rôle et qu’un problème risque de ce produire à très court terme.

Requêtes pour le serveur zk1
min_over_time(java_lang_Memory_HeapMemoryUsage_used
{instance= »zk1:7070″}[5m])/1024/1024

Alerte
Si supérieur à 80% de la mémoire heap maximum (Xmx)
Fréquence des GCAvec le garbadge collector G1GC, on surveille 2 types de GC : « G1 Young Generation » et  » G1 Old Generation ». On désire connaitre le nombre de GC par seconde (moyenne sur la dernière minute).

Requêtes pour le serveur zk1
rate(jvm_gc_collection_seconds_count{instance= »zk1:7070″,gc= »G1 Young Generation »}[1m])
rate(jvm_gc_collection_seconds_count{instance= »zk1:7070″,gc= »G1 Old Generation »}[1m])

Alerte
Déterminer le nombre de GC lors d’un fonctionnement nominal.
Générer une alerte si ce nombre est doublé.
Durée des GCOn désire connaitre la durée des GC (moyenne sur la dernière minute).

Requêtes pour le serveur zk1
rate(jvm_gc_collection_seconds_sum{instance= »zk1:7070″ ,gc= »G1 Young Generation » }[1m]) / rate(jvm_gc_collection_seconds_count{instance= »zk1:7070″ ,gc= »G1 Young Generation » }[1m])
rate(jvm_gc_collection_seconds_sum{instance= »zk1:7070″ ,gc= »G1 Old Generation » }[1m]) / rate(jvm_gc_collection_seconds_count{instance= »zk1:7070″ ,gc= »G1 Old Generation » }[1m])

Alerte
Déterminer le durée des GC lors d’un fonctionnement nominal.
Générer une alerte si cette durée est doublée.

Les métriques Zookeeper ( Zookeeper-exporter)

Le Zookeeper-exporter utilise les commandes 4lw (https://zookeeper.apache.org/doc/r3.5.5/zookeeperAdmin.html#sc_zkCommands).

Métrique Description et requête Prometheus
Requêtes en attenteNombre de requêtes mises en file d’attente par le serveur.

Requêtes pour le serveur zk1
zk_outstanding_requests{instance= »zk1:7080″}

Alerte
Si > 3
Synchronisations en attenteCe métrique n’est disponible que sur le leader. On additionne les valeurs en provenance de tous les serveurs pour inclure le leader quel qu’il soit.

Requêtes
sum without (instance,job,zk_instance)(zk_pending_syncs)

Alerte
Si > 3
Nombre de followersCe métrique n’est disponible que sur le leader. On additionne les valeurs en provenance de tous les serveurs pour inclure le leader quel qu’il soit. On doit obtenir le nombre de serveurs moins 1.

Requêtes
sum without (instance,job,zk_instance)(zk_synced_followers)

Alerte
Si différent du nombre de serveurs moins 1
Fichiers ouvertsNombre de fichiers ouverts. On surveille que la limite n’est pas atteinte.

Requêtes pour le serveur zk1
zk_open_file_descriptor_count / zk_max_file_descriptor_count * 100

Alerte
Si > 80%

Conclusions

De part son architecture Zookeeper est très performant et peut gérer des architectures distribuées très volumineuses. Le risque de problèmes apparaît principalement lors d’un arrêt d’un des membres de l’ensemble ou lorsque les serveurs du système distribué réalisent beaucoup d’écritures (redémarrages de serveurs, manipulations massives des collections pour SolrCloud, …). Pour un environnement SolrCloud stable et sans manipulation massive des collections, une très importante charge d’indexation et de recherche ne sollicite pas particulièrement Zookeeper. Il n’en demeure pas moins qu’un monitoring de Zookeeper doit absolument être mis en place. Cet article a indiquer les métriques clés à suivre.

Vous souhaitez bénéficier d’une expertise Solr ou intégrer une ressource ponctuelle à vos projets ? Rendez vous sur la page Contact