Blog

Mettre en place un suivi des codes HTTP dans cacti

Écrit le 24 01 2017 par Kévin MET _

UPDATE DU 06/02/2017 :

Pour illustrer un peu mes propos, j'ajoute deux screenshots sur un projet qui fait pas mal de hits sur un des serveurs que j'infogère. Si un jour il y a un problème dans le déploiement qui produit des erreurs 500, je prendrais un screenshot et je mettrais à jour l'article pour que vous voyez à quel point c'est flagrant. Pour le moment, je croise les doigts, ça n'est pas encore arrivé...

cacti http status code weekly
cacti http status code monthly

Introduction

Aujourd’hui un long billet sur ma façon de faire pour grapher les status code des logs d’apache et de nginx. Je vous préviens, si vous voulez le mettre en place et que vous n’êtes pas trop familier avec Cacti vous en aurez pour minimum 30 minutes mais si vous connaissez déjà bien le fonctionnement de Cacti je pense que 15 minutes devraient suffire.

Si vous êtes sysadmin et que vous êtes, comme tout bon sysadmin qui se respecte (ceci est du second degré), en guerre permanente avec les développeurs avec qui vous bossez, vous allez sûrement vous demandez "Mais pourquoi je devrais grapher les logs de leur applicatif, c’est à eux de regarder ça !" Et bien oui et non, car si lors du développement c’est bien à eux de regarder les logs pour corriger les erreurs, j’estime que lorsque l’appli est en production c’est à l’admin de pouvoir jeter un coup d’œil global sur les performances de l’applicatif et cela passe notamment par la surveillance des status code HTTP. En effet, lors de la mise en prod d’un commit foireux on va recevoir un mail de notre ami cacti nous disant que le seuil d’erreurs 500 à exploser et on va pouvoir, soit engueuler un dev, soit faire un rollback (car il est 2h du matin mais on ne manquera pas de l’engueuler le lendemain matin) :D Bref, vous aurez compris l’utilité d’un tel dispositif.

Avant de rentrer dans le vif du sujet, je sais qu’il existe d’autres méthodes pour faire cela et notamment ELK (Elasticsearch – Logstatsh – Kibana) mais je préfère avoir ce type de graphs directement dans cacti car celui-ci regroupe tout un tas d’autres graphiques utiles au sysadmin. Je n’ai rien contre ELK, au contraire même car c’est vraiment bien foutu, c’est juste que je trouve cela plus orienté développeur.

Fonctionnement

Le principe de fonctionnement est assez analogue à ce que font les plugins Cacti fournis par Percona pour ceux qui connaissent. Pour ceux qui ne connaissent pas, je vous conseille de les installer car il y a pas mal de type de graphiques disponibles en plus de tout ce qui concerne MySQL. Le fonctionnement est donc le suivant, sur le serveur cacti on a un script php qui est appelé par cacti et qui se connecte en ssh sur votre serveur web sur lequel il appelle un script perl qui parcours les logs. Cette information remonte donc jusqu’à cacti qui produit des RRD qui sont ensuite affichés sous forme de graphiques.

Script Perl sur le serveur web

On va commencer par déployer le script perl sur le serveur web qui va faire le comptage des status code de votre log. Je vous le colle ici mais vous pouvez également récupérer la dernière version sur mon dépôt git : https://github.com/nierdz/admintools/blob/master/cacti/logs/parse_www_logs.pl

Si vous ne voulez pas vous prendre la tête, je vous conseille de mettre ce script dans /opt/scripts/parse_www_logs.pl, sinon vous devrez faire une petite modification dans le fichier de conf du script PHP. J’expliquerai comment plus tard.


#!/usr/bin/perl

use strict;
use warnings;
use File::ReadBackwards ;
use POSIX qw(strftime);

# Time five minutes ago in apache format
my $five_minutes = strftime("%d/%b/%Y:%H:%M:%S",localtime(time-300));

# Empty hash containing the status codes
my %hash = (
	'200' =>0,
	'206' =>0,
	'301' =>0,
	'302' =>0,
	'304' =>0,
	'310' =>0,
	'400' =>0,
	'401' =>0,
	'403' =>0,
	'404' =>0,
	'499' =>0,
	'500' =>0,
	'503' =>0);

my $bw = File::ReadBackwards->new( '/var/log/nginx/mad-rabbit.com-ssl_access.log' ) or die "can't read log file $!" ;
while( defined( my $log_line = $bw->readline ) ) {
	$log_line =~ m/([0-9]{1,}\/[a-zA-z]{3}\/[0-9]{4}:[0-9]{2}:[0-9]{2}:[0-9]{2}).*HTTP\/[0-9].[0-9]" ([0-9]{3})/;
	my $date = $1;
	my $status = $2;

	if ($status == 200) {
	$hash{200}++;
	}
	if ($status == 206) {
	$hash{206}++;
	}
	if ($status == 301) {
	$hash{301}++;
	}
	if ($status == 302) {
	$hash{302}++;
	}
	if ($status == 304) {
	$hash{304}++;
	}
	if ($status == 310) {
	$hash{310}++;
	}
	if ($status == 400) {
	$hash{400}++;
	}
	if ($status == 401) {
	$hash{401}++;
	}
	if ($status == 403) {
	$hash{403}++;
	}
	if ($status == 404) {
	$hash{404}++;
	}
	if ($status == 499) {
	$hash{499}++;
	}
	if ($status == 500) {
	$hash{500}++;
	}
	if ($status == 503) {
	$hash{503}++;
	}

	# Stop the loop after retrieve 5 minutes of log
	if ($date le $five_minutes) {
		foreach my $k (sort(keys(%hash))) {
		print "$k $hash{$k}\n";
		}
	last;
	}
}

La particularité de ce script réside dans le fait qu’il est très rapide car il utilise File::ReadBackwards ce qui lui permet de lire le fichier de log en partant de la fin. J’ai fait ce choix car lorsqu’on a des projets qui génèrent plusieurs Mo de logs par heures cela peut vite devenir assez long de parser le fichier entier. J’avais commencé par écrire ce script en bash et je me suis donc rabattu sur perl pour pour cette raison.

En lisant le script vous allez voir que le fichier de log parsé est /var/log/nginx/mad-rabbit.com-ssl_access.log et qu’il faut donc modifier cette partie pour l’adapter à votre propre situation. Une fois le script dans le dossier, vous lui attribué les droits 700 en root:root :


root@web0.mad-rabbit.com:~ # chmod 700 /opt/scripts/cacti/parse_www_logs.pl 
root@web0.mad-rabbit.com:~ # chown root: /opt/scripts/cacti/parse_www_logs.pl
root@web0.mad-rabbit.com:~ # ls -l /opt/scripts/cacti/parse_www_logs.pl
-rwx------ 1 root root 1366 Jan 24 15:26 /opt/scripts/cacti/parse_www_logs.pl

Une fois que c’est fait on installe File::ReadBackwards en passant par CPAN ou directement par le gestionnaire de paquet de votre distribution. Dans le cas de Debian/Ubuntu, il s’agit du paquet libfile-readbackwards-perl. Ensuite on lance le script pour voir si tout fonctionne bien et on devrait obtenir un résultat similaire à cela :


root@web0.mad-rabbit.com:~ # /opt/scripts/cacti/parse_www_logs.pl 
200 45
206 0
301 1
302 0
304 0
310 0
400 0
401 0
403 0
404 0
499 0
500 0
503 0

On obtient bien les status code des logs sur les 5 dernières minutes et la sortie est formatée correctement pour être lu par le script php, on peut donc passer à l’étape suivante.

Script PHP sur le serveur Cacti

On commence par télécharger le script PHP sur mon dépôt git, je ne l’affiche pas ici car il est beaucoup trop long. https://github.com/nierdz/admintools/blob/master/cacti/logs/check_logs_by_ssh.php

Ce script est très largement inspiré du script ss_get_by_ssh.php fourni dans les plugins Cacti de Percona donc tout le mérite revient à eux. Celui-ci est assez bien commenté donc si vous voulez creuser le fonctionnement il vous suffit de le lire. Pour allez plus loin, vous pouvez également passer la variable $debug à TRUE.

Ce script est à placer dans le dossier scripts de cacti. Dans le cas d’une installation via le gestionnaire de paquet sur Debian/Ubuntu il s’agit du dossier /usr/share/cacti/site/scripts. Le script doit pouvoir être exécuté par l’utilisateur PHP qui lancera le script. Dans mon cas (installation via gestionnaire de paquet sur Debian) il s’agit de www-data car php est exécuté via mod_php dans apache. Attention donc, à adapter selon votre configuration.

En plus de ce script PHP, il faut IMPÉRATIVEMENT ajouter un fichier de configuration dans le dossier /etc/cacti qui sera lu par le script PHP. Le fichier est disponible ici : https://github.com/nierdz/admintools/blob/master/cacti/logs/check_logs_by_ssh.php.cnf


<?php
$ssh_user   = 'root';
$ssh_iden   = '-i /etc/cacti/id_rsa';
$logs_www_cmd = '/opt/scripts/cacti/parse_www_logs.pl';
?>

Ce deuxième fichier détermine la clé SSH privée à utiliser pour se connecter sur le serveur web. Dans mon cas, les clés sont dans /etc/cacti et il faudra donc les générer avec ssh-keygen auparavant. Une fois que vous avez généré vos deux clés, il faut attribuer les droits nécessaires à l’utilisateur qui lancera le script PHP, dans mon cas www-data.


-rw------- 1 www-data root 1675 Jan  9 20:36 /etc/cacti/id_rsa

Il faut ensuite copier la clé publique sur votre serveur web en utilisant ssh-copy-id par exemple ou encore directement via votre éditeur préféré donc vim ;)

Il faut également penser à modifier la variable $logs_www_cmd qui doit pointer sur votre script perl sur votre serveur web.

Pour vérifier que la connexion en SSH fonctionne bien avec votre clé et votre utilisateur PHP, vous devez faire une vérification en lançant la commande suivante :


root@sup1.mnt-tech.fr:~ # sudo -u www-data ssh -i /etc/cacti/id_rsa root@web0.mad-rabbit.com 'w'
Could not create directory '/var/www/.ssh'.
The authenticity of host 'web0.mad-rabbit.com (37.187.205.163)' can't be established.
ECDSA key fingerprint is e7:6d:d7:10:b3:d3:6c:36:3e:78:d9:f4:e9:e1:cf:a2.
Are you sure you want to continue connecting (yes/no)? yes
Failed to add the host to the list of known hosts (/var/www/.ssh/known_hosts).
 18:21:58 up 47 days,  1:05,  1 user,  load average: 0.30, 0.13, 0.08
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/1    17-86-190-109.ds 17:20   47:01   0.06s  0.06s -bash

On voit que www-data peut bien se connecter en root sur notre serveur web. On peut donc pousser plus loin les vérifications en lançant le script php avec www-data et en indiquant les paramètres nécessaires pour remonter les logs. Vous devriez avoir une sortie similaire à cela :


root@sup1.mnt-tech.fr:~ # sudo -u www-data php /usr/share/cacti/site/scripts/check_logs_by_ssh.php --host web0.mad-rabbit.com --type www --items gg,gh,gi,gj,gk,gl,gm,gn,go,gp,gq,gr,gs
gg:52 gh:0 gi:3 gj:0 gk:0 gl:0 gm:0 gn:0 go:0 gp:1 gq:0 gr:0 gs:0

On voit qu’on a bien les données qui remontent, c’est bon signe, on peut enfin passer à la configuration de Cacti

Configuration dans l’interface admin de Cacti

Pour commencer, on va importer le template aux petits oignons que je vous ai concocté avec amour :) Il est disponible sur mon dépôt git : https://github.com/nierdz/admintools/blob/master/cacti/logs/cacti_graph_template_http_status_code_gt.xml

Ce template contient le graph template (GT), le data template (DT) et le data input method (IM). Pour l’importer, il faut aller dans le menu "Import Templates" et cocher la case "Use custom RRA settings from the template".

Une fois que c’est fait il faudra peut-être modifier le data input method dans la partie input string. Cela dépend du chemin dans lequel se trouve votre script PHP mais dans la plupart des cas il n’y aura rien à faire de ce coté là.

Ensuite il faut ajouter le graph template à votre device, pour cela vous allez dans le menu "Devices" et sur votre serveur dans la partie "Associated Graph Templates" vous cliquez sur "Add graph templates" et vous ajoutez "HTTP Status Code GT". Il ne vous reste plus qu’à créer votre graph en allant dans le menu "Create graph for this host" puis en cochant la case "Create: HTTP Status Code GT" et "Create". Voilà, je vous avais dit que c’était long... mais franchement, ça en valait la peine non ? ;)

Et les futures machines ?

Vous vous demandez si cela va être aussi long pour chaque nouveau serveur que vous aurez envie de monitorer ? Heureusement que non car il suffira de mettre le script perl sur votre serveur web, de copier la clé publique sur votre nouveau serveur, d’ajouter le graph template, dans Cacti et de créer votre graph. Cette opération prend moins de 5 minutes montre en main !

Pour aller plus loin

Une fois que votre monitoring est en place, vous pouvez aller plus loin en utilisant par exemple le plugin thold qui vous permettra de recevoir des alertes mails en cas de dépassement d’un seuil sur les erreurs 500 ou autres. Un plugin bien pratique dans bien des situations, à installer de toute urgence si vous ne l’avez pas encore !

Et enfin, quand on a plusieurs frontaux web pour le même site et qu’on veut agréger les graphs en un seul gros graph pour jouer au concours du "qui à la plus grosse" avec ses collègues, on peut utiliser le plugin aggregate. Il vous permettra de voir d’un coup d’œil comment se comporte l’applicatif sur tous vos frontaux et c’est quand même bien pratique !

♥ Partage sur tes réseaux sociaux ♥
Kévin MET
Kévin MET

Auteur de ce blog et gérant de la société MNT-TECH, je publie sur ce blog lorsque le temps me le permet et lorsqu'un sujet qui me parait intéressant n'a pas encore été abordé en français. Toutes les informations techniques présentes sur cette page peuvent être réutilisées moyennant le fait de citer la source.