Bien choisir ses sources de données dans un script Shell

closeCet article a été publié il y a 7 ans 11 mois 8 jours, il est donc possible qu’il ne soit plus à jour. Les informations proposées sont donc peut-être expirées.

Quand on est pressé, qu’on gère le maintien en condition opérationnelle, il arrive parfois de coder à la va-vite une petite routine pour « calmer » un peu le jeu en attendant qu’une tâche trop gourmande se termine par exemple, ou qu’une journée de travail de serveur plus chargée que d’habitude touche à sa fin. Moralité, pas testée, la routine peut provoquer un comportement tout à fait hasardeux par moments. Décryptage.

A cause d’un site pas forcément très bien codé, le serveur d’un client a tendance à vite surcharger en cas de pic de visites, les requêtes MySQL s’empilant comme les pots de Nutella quand j’habitais Rouen. Moralité, pour faire baisser la charge et permettre aux personnes de moins patienter, un administrateur a mis en place un script qui se lance toutes les cinq minutes, et redémarre MySQL si la charge dépasse une certaine valeur (pour 4 vCPU à 3GHz, oui oui ça commence à tabasser). Violent, mais le boulot est fait, la charge tombe presque immédiatement, même si on conviendra que c’est super crade. Le truc est court, ça tient en une ligne :

C’est assez peu lisible, même si on finit par s’y retrouver vite. Donc, ça va lire la valeur du load dans la sortie de la commande uptime, et si c’est supérieur à 20, ça coupe MySQL pendant 10 secondes et ça le redémarre. Super barbare, j’avais prévenu. Bref, ça fonctionne plutôt bien. Sauf à un moment, le client nous appelle, c’est plus possible, toutes les cinq minutes environ le site ne répond plus. Vérification rapide : étrange, le load est assez bas, entre 7 et 8 (c’est encore bas pour ce serveur). Aucune raison que ça redémarre, et pourtant, les logs de cron sont formels, ça redémarre toutes les cinq minutes. Ben merde.

Et la solution était une fois de plus vicieuse, et pas nécessairement facile à comprendre de prime abord. Ça tient à peu de chose :

On a bien la colonne 10 qui correspond au load, ici 0.92 (c’est pour l’exemple). Mais si on regarde la sortie toujours d’uptime à un autre moment :

Magie, la dixième colonne maintenant, c’est « average ». Ce piège, en fonction du moment dans la journée, on a pas exactement le même format de sortie. Et là, on a le droit à un concours entre JavaScript et Bash, puisque si comme dans la commande on test « average » par rapport au nombre 10 :

Ah ben tiens dis donc. Une chaine de caractères peut être supérieure à un nombre, mais quel racisme je vous jure ! Non plus sérieusement, en fait ça teste la valeur ASCII du premier caractère, et si on regarde de plus près :

Erf. Donc oui pas évident en fait, mais tout à fait logique. Ça fonctionnerait également en testant la valeur ASCII par rapport au nombre 10, notez bien.

La solution est assez simple pour garder ce comportement crade mais sans bug le temps de corriger réellement le tir : récupérer le load ailleurs. En effet, on peut plus facilement le trouver à cette adresse :

Eh oui, au moins cette sortie ne risque pas de décaler les valeurs de load, puisque c’est la première colonne. On corrige donc rapidement :

L’exemple est super dégueulasse, évidemment, mais l’esprit est là : si on apprend rapidement en développement Web à ne jamais faire confiance à l’utilisateur, parfois, il faut aussi se méfier d’outils pourtant connus depuis très, très longtemps. Ils peuvent encore nous surprendre des années après si l’on est pas assez prudent.

Au passage, c’est aussi un plaidoyer contre toutes les installations/exécutions aveugles de scripts récupérés sur le web. Les fameux  » curl | sh  » à proscrire, rappelez-vous.

2 Commentaires
Le plus ancien
Le plus récent
Commentaires en ligne
Afficher tous les commentaires
Djerfy
11/05/2016 19:55

Je m’en souviens de celui la ! ?

Httqm
29/12/2017 12:47

Et histoire de faire un peu plus « propre » (même si, bon, ok, on part de loin ;-), on peut aussi remplacer :
cat fichier | awk optionsDeAwk | sh
par :
awk optionsDeAwk fichier | sh