Supervision CPU via SNMP

Récemment nous avons rencontré divers problèmes au boulot concernant la supervision du CPU : remontées à 100% alors que “top” indiquait 0%, parfois des remontées en “UNKNOWN” sans raison évidente, etc.

Modes de supervision de la charge CPU

J’écourte l’intro faite sur le wiki du boulot : on peut en gros compter sur 2 sources d’information pour superviser le CPU d’une machine Linux/Unix :

  • des commandes système : top (top -b -n 1 + grep/head/sed), vmstat, voire si vous avez de la chance procinfo ou autres.
  • via SNMP, qui propose deux types de compteurs : les compteurs moyennés (ssCpu*) et bruts (ssCpuRaw*). Pour des raisons pratiques, on va se concentrer sur ce mode.

Il y a aussi la charge CPU calculée directement par le système, bien sûr. Mais son calcul comme son interprétation sont complexes, varient en fonction du nombre de processeurs, et, accessoirement, la définition varie d’un Unix-like à un autre. Cela dit si ça vous intéresse, il existe plusieurs plugins (peut-être ça aussi, indirectement).

Je viens de trouver ce plugin qui m’a bien pu, et qui gère apparemment le multi-processeur. Mais ça gâche la suite de l’article alors… revenons donc à la terre ferme.

A. Les compteurs ssCpu

Référence: http://net-snmp.sourceforge.net/docs/mibs/ucdavis.html#ssCpuIdle

Le démon snmpd fournit en standard des compteurs ssCpuIdle, ssCpuUser, etc., directement exprimés en pourcentages, qui devraient permettre de superviser l’occupation du CPU sur une période récente. D’ailleurs on ne sait pas exactement laquelle, je n’ai pas trouvé de référence à ce sujet. Mais ces compteurs sont dépréciés, considérés comme non fiables, et inutilisables en l’état sur une plateforme multi-processeurs ou multi-coeurs (le total arrive, selon les implémentations, à 100% ou N*100%). Le script check_snmp_load.pl, utilisé avec l’option “netsc”, utilise un de ces compteurs, le ssCpuIdle. Ce script est fourni en standard avec le bundle Nagios3 que j’ai au boulot.

Mais il semble notamment que ce compteur n’est pas fiable au-delà de 62 jours d’uptime sur les RHEL5 (tickets sur le bugtracker RedHat ici et ici). Ce genre de bêtise, ça donne quand même envie de se flinguer, merci RedHat…

B. Compteurs ssCpuRaw

Référence: http://net-snmp.sourceforge.net/docs/mibs/ucdavis.html#ssCpuRawIdle

Les compteurs ssCpuRaw* fonctionnent sur un mode différent :

$ snmpwalk -v 2c -c public myserver.domain.tld 1.3.6.1.4.1.2021.11 |grep CpuRaw
UCD-SNMP-MIB::ssCpuRawUser.0 = Counter32: 7184735
UCD-SNMP-MIB::ssCpuRawNice.0 = Counter32: 3142
UCD-SNMP-MIB::ssCpuRawSystem.0 = Counter32: 7208896
UCD-SNMP-MIB::ssCpuRawIdle.0 = Counter32: 1116976279
UCD-SNMP-MIB::ssCpuRawWait.0 = Counter32: 4329138
UCD-SNMP-MIB::ssCpuRawKernel.0 = Counter32: 6038492
UCD-SNMP-MIB::ssCpuRawInterrupt.0 = Counter32: 53534
UCD-SNMP-MIB::ssCpuRawSoftIRQ.0 = Counter32: 1116870

Il s’agit de compteurs permettant de jauger l’utilisation du Cpu depuis l’initialisation du compteur. Pour connaitre l’utilisation du Cpu sur une période récente, il faut donc prendre les valeurs à un instant t, prendre les valeurs à un instant t+1, et faire la soustraction. C’est ce que faisait notre script au boulot, “check_net-snmp_cpu_usage.pl”, récupéré d’une instance Nagios1.3/Oreon.

Seulement ce script fait les choses “bêtement”, il prévoit une intervalle fixe entre les deux checks pour faire la différence. Or ces compteurs ne sont pas mis à jour en temps réel, mais à intervalles plus ou moins réguliers, en général 2 secondes, mais ça peut monter à 10 ou 15 secondes en cas de forte charge du serveur (à la louche). Résultat, soit on règle l’attente à 15 secondes et dans la majorité des cas on attendra 15 secondes inutilement, soit on règle ça a des valeurs moindres, et de temps en temps le script sortira en UNKNOWN.

Comment faire ? premier essai : check_cpu_load.rb

Voir mon espace Github

Ce script fait la même chose que check_net-snmp_cpu_usage.pl mais de façon un peu plus intelligente : il récupère une première série de valeurs. Puis chaque seconde il en récupère une nouvelle, et il attend que le nombre de cycles CPU total écoulé entre sa mesure en cours et la première série soit suffisamment important. Ensuite (ou au bout de 15 secondes par sécurité), il fait le calcul et sort. Si les compteurs sont mis à jour rapidement et que le delta est représentatif, il peut sortir la mesure au bout d’1 ou 2 secondes. Sinon, il attend d’avoir une mesure plus représentative, au cas où le serveur travaille peu (peu de cycles CPU), ou si les compteurs ne sont pas mis à jour (serveur saturé).

Deuxième essai : check_cpu_avg.rb

Voir mon espace Github

Mais cette mesure n’est pas très pertinente : vérifier toutes les 5 minutes ce que fait le CPU d’une machine sur les 3,4,5 dernières secondes n’est pas représentatif. Il suffit de lancer la commande à la main plusieurs fois de suite pour s’en convaincre, admirez les écarts en quelques secondes sur mon serveur de supervision :

CPU Used OK: 10.17% | Wait=1.52%, System=0.99%, User=6.67%, Nice=0.00%, Idle=89.83%
CPU Used OK: 23.66% | Wait=2.49%, System=1.77%, User=17.63%, Nice=0.00%, Idle=76.34%
CPU Used OK: 11.39% | Wait=1.84%, System=1.12%, User=7.31%, Nice=0.00%, Idle=88.61%
CPU Used OK: 42.97% | Wait=1.59%, System=3.43%, User=34.52%, Nice=0.00%, Idle=57.03%
CPU Used CRITICAL: 99.01% > 90 | Wait=1.64%, System=13.64%, User=70.09%, Nice=0.00%, Idle=0.99%
CPU Used CRITICAL: 92.06% > 90 | Wait=1.02%, System=4.76%, User=16.44%, Nice=65.08%, Idle=7.94%
CPU Used CRITICAL: 100.00% > 90 | Wait=0.00%, System=3.41%, User=6.31%, Nice=86.86%, Idle=0.00%
CPU Used OK: 41.59% | Wait=4.00%, System=3.16%, User=8.58%, Nice=22.70%, Idle=58.41%
CPU Used OK: 30.33% | Wait=19.17%, System=1.51%, User=8.14%, Nice=0.00%, Idle=69.67%
CPU Used CRITICAL: 94.22% > 90 | Wait=0.35%, System=11.70%, User=70.46%, Nice=0.00%, Idle=5.78%

=> ça ne varie pas toujours autant certes, mais lorsque c’est le cas, ça bouge autant que dans un top, ce qui n’est pas le but recherché. A la limite on se moque (?) des pics de charge instantanés. L’objectif est plutôt de détecter si une machine reste à 100% de CPU trop longtemps, auquel cas une application a peut-être un problème, ou la machine est peut-être sous-dimensionnée.

L’idée consiste à ne faire qu’un “passage” de la commande SNMP, de stocker les résultats dans un fichier, et de regarder le delta au check Nagios suivant. C’est ce que fait check_cpu_avg.rb. Il sort en UNKNOWN s’il ne trouve pas de fichier stockant un check précédent, et sinon, il fait la différence (qui devient donc une moyenne sur la période entre les deux checks, 5 minutes environ pour nous), stocke les nouvelles valeurs dans le fichier et renvoie le résultat de la différence.

Peut-être qu’il est plus pertinent de superviser à la fois ce genre d’indicateur et le LOAD au sens Linux. Il faudrait en creuser la définition du load précédemment cité). C’est un peu obscur pour moi pour le moment, mais si un lecteur a un avis, défoulez-vous !