Monitoring d'une installation PHP5-FPM
Il est un temps pas si lointain où il était difficile de savoir ce qui se passait sur ses serveurs web au niveau PHP. Tout était mélangé dans un processus Apache2 et savoir ce que PHP consomme comme mémoire, comme processus était chose quasi impossible.
C’est désormais beaucoup plus facile si vous utilisez un socle de serveurs web avec php5-fpm. Si ce n’est pas le cas, regardez si votre hébergeur supporte ce mode de fonctionnement de PHP. Si vous ête sur serveur dédié, n’attendez plus pour passer à php5-fpm ! Outre une meilleure visibilité côté PHP, vous aurez également de meilleures performances pour vos applications PHP.
Configuration de php5-fpm pour la supervision
php5-fpm expose, à l’instar de Nginx ou Apache2 ses statistiques, métriques internes sur une URL particulière qu’il reste à définir dans le fichier de configuration du pool; soit /etc/php5/fpm/pool.d/www.conf
par défaut sur une Ubuntu.
;listen = 127.0.0.1:9000
listen = /var/run/php-fpm.sock
pm.status_path = /status
ping.path = /ping
Commenter/décommentez la ligne listen
qui vous convient en fonction du fait que vous accédiez à votre installation php5-fpm via TCP ou socket.
Il faut ensuite configurer le serveur web en frontal de l’installation php5-fpm afin d’exposer ces valeurs au travers d’une URL. Cette configuration diffère en fonction du serveur. Voici la configuration pour les deux serveurs web les plus utilisés sur la toile - au moins d’après BuiltWith -.
Configuration avec Apache 2
configuration du fichier /etc/apache2/conf.d/php5-fpm.conf
pour Apache2
<IfModule mod_fastcgi.c>
AddHandler php5-fcgi .php
Action php5-fcgi /php5-fcgi virtual
Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
#FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
FastCGIExternalServer /usr/lib/cgi-bin/php5-fcgi -socket /var/run/php-fpm.sock
</IfModule>
Même remarque que précédemment concernant la directive FastCGIExternalServer
.
et dans l’hôte virtuel par défaut par exemple, ajouter ce bloc :
<FilesMatch "^ping|status$">
SetHandler php5-fcgi
</FilesMatch>
Il est bien sûr fortement conseillé de restreindre l’accès à ces URLs sur la boucle locale ou par IP afin que n’importe qui ne puisse pas accéder à ces informations.
Configuration avec Nginx
Si vous utilisez Nginx plutôt que Apache2 en frontal de votre installation php5-fpm, vous pouvez alors utiliser le bloc de configuration suivante :
location ~ ^/(status|ping)$ {
access_log off;
allow 127.0.0.1;
allow 10.10.10.10#your-ip;
deny all;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
}
Les métriques collectées
Les données accessibles à travers l’URL mise en place sont mises à jour en temps réel. Ce que vous voyez représente donc votre installation PHP au moment de l’interrogation.
Pour voir si cela fonctionne, utilisez par exemple curl http://localhost/status
sur la ligne de commande. Si tout est correctement configuré, vous obtenez la sortie suivante :
pool: www
process manager: dynamic
start time: 24/Mar/2014:13:16:10 +0100
start since: 2551
accepted conn: 7843
listen queue: 0
max listen queue: 0
listen queue len: 0
idle processes: 8
active processes: 2
total processes: 10
max active processes: 10
max children reached: 0
Où
pool
est le nom du pool au sens php5-fpm.process manager
dont la valeur est àstatic
,dynamic
ouondemand
.start time
est la date et l’heure à laquelle php5-fpm a été démarré.start since
est le nombre de secondes ecoulées depuis le dernier démarrage de php5-fpm.accepted conn
est le nombre de requêtes servies par le pool.listen queue
est le nombre de requêtes dans la queue des connections en attente.max listen queue
est le nombre maximale de requêtes dans la queue des connections en attente depuis le dernier démarrage.listen queue len
est la taille de la socket pour la queue des connections en attente.idle processes
est le nombre de processus en attente.active processes
est le nombre de processus actifs.total processes
est le nombre de processus total; soit en attente + actifs.max active processes
est le nombre maximal de processus actifs depuis le dernier démarrage.max children reached
est le nombre de fois où la limite de processus alloués a été atteinte.
Formats de sortie
Par défaut, la sortie est formatée en texte simple. En passant html
, xml
ou json
dans la requête, vous obtenez le format de sortie correspondant. Ainsi curl http://127.0.0.1/status?json
donne la sortie formatée JSON suivante :
{"pool":"www","process manager":"dynamic","start time":1395663370,"start since":2708,"accepted conn":8779,"listen queue":0,"max listen queue":0,"listen queue len":0,"idle processes":3,"active processes":26,"total processes":29,"max active processes":50,"max children reached":1}
Une valeur importante à noter dans cette sortie est le max children reached
qui doit être à zéro sous peine de devoir refuser des requêtes par manque de processus enfants pour les accepter.
Métriques supplémentaires
Si vous en voulez plus, vous pouvez ajouter full
à votre demande pour obtenir une sortie beaucoup plus détaillée. Ainsi, curl "http://127.0.0.1/status?json&full"
donne la sortie suivante (tronquée car très longue) :
{"pool":"www","process manager":"dynamic","start time":1395663370,"start since":3898,"accepted conn":14567,"listen queue":0,"max listen queue":0,"listen queue len":0,"idle processes":9,"active processes":1,"total processes":10,"max active processes":50,"max children reached":1, "processes":[{"pid":7492,"state":"Idle","start time":1395666547,"start since":721,"requests":344,"request duration":165244,"request method":"GET","request uri":"/index.php","content length":0,"user":"-","script":"/var/www/index.php","last request cpu":78.67,"last request memory":45350912},{"pid":7481,"state":"Idle","start time":1395666411,"start since":857,"requests":420,"request duration":143462,"request method":"POST","request uri":"/index.php","content length":7356,"user":"-","script":"/var/www/index.php","last request cpu":97.59,"last request memory":46923776},{"pid":7482,"state":"Idle","start time":1395666426,"start since":842,"requests":411,"request duration":172243,"request method":"POST","request uri":"/index.php","content length":7584,"user":"-","script":"/var/www/index.php","last request cpu":92.89,"last request memory":46923776}]}
Les infos données sont celles-ci :
pid
est le numéro d’identification du processus « PID ».state
est l’état du processus parmiIdle, Running
…start time
est la date et l’heure à laquelle le processus a été démarré.start since
est le nombre de secondes ecoulées depuis le démarrage du processus.requests
est le nombre de requêtes servies par le processus.request duration
est la durée en µs des requêtes.request method
est la méthode utilisée dans la requête (GET, POST, …).request URI
L’URI demandée avec la chaîne de caractères requêtée.content length
est la longueur de la requête (seulement avec POST).user
est l’utilisateur (PHP_AUTH_USER) ou-
si non renseigné.script
est le script principal appelé ou-
si non renseigné.last request cpu
est le pourcentage de CPU consommé par la dernière requête. Toujours à 0 si le processus est dans un état autre que Idle car ce calcul est fait quand la requête est traitée.last request memory
est la quantité de mémoire maximale utilisée par la dernière requête. Toujours à 0 sir le processus est dans un état autre que Idle car ce calcul est fait quand la requête est finie de traiter.
Voilà de quoi faire mine de rien, que ce soit pour correctement dimensionner votre installation PHP ou la monitorer. Reste à collecter ceci de façon automatique et à intervalles réguliers avec votre solution de supervision.
Collecte avec Collectd
Vu que l’URL de statut permet de formater la sortie en JSON, nous allons utiliser le plugin de Collectd cURL-JSON pour la parser. Le bénéfice supplémentaire d’utiliser Collectd est de pouvoir envoyer toutes ces métriques dans RRD ou mieux, Graphite, mais pas seulement…
Nous ajoutons ce bloc de configuration à Collectd. Il fait correspondre les libellés des métriques fournies par php5-fpm et les types gérés par Collectd.
LoadPlugin curl_json
<Plugin curl_json>
<URL "http://serveur_hostname_or_ip/status?json">
Instance "www"
<Key "accepted conn">
Type "fpm_connections"
</Key>
<Key "listen queue">
Type "fpm_queue"
</Key>
<Key "max listen queue">
Type "fpm_queue_max"
</Key>
<Key "listen queue len">
Type "fpm_queue_lenght"
</Key>
<Key "idle processes">
Type "fpm_ps_idle"
</Key>
<Key "active processes">
Type "fpm_ps_active"
</Key>
<Key "total processes">
Type "fpm_ps_total"
</Key>
<Key "max active processes">
Type "fpm_ps_active_max"
</Key>
<Key "max children reached">
Type "fpm_child_max_reached"
</Key>
</URL>
</Plugin>
Il faut encore définir les « types » de données collectées en le précisiant dans le fichier de Collectd types.db
. Cela permet à Collectd de savoir comment définir une métrique (compteur, jauge…).
# types.db
fpm_connections value:COUNTER:0:65535
fpm_queue value:GAUGE:0:65535
fpm_queue_max value:GAUGE:0:65535
fpm_queue_lenght value:GAUGE:0:65535
fpm_ps_idle value:GAUGE:0:65535
fpm_ps_active value:GAUGE:0:65535
fpm_ps_active_max value:GAUGE:0:65535
fpm_ps_total value:GAUGE:0:65535
fpm_child_max_reached value:GAUGE:0:65535
Il y a sûrement de la place pour faire mieux mais la base fournie devrait vous permettre d’améliorer tout ça. N’oubliez pas de partager vos résultats !
Graphe avec Grafana
Voilà en tout cas le résultat dans un graphe Grafana bien sûr.
Le nom des métriques est un peu « pourri » mais je n’ai pas trouvé de moyen pour le moment de renommer ces métriques avant injection dans Graphite. J’en suis arrivé à la conclusion que je ne pouvais rien y faire mais si quelqu’un a une solution, je suis preneur.
Quoiqu’il en soit du nommage des métriques, ce graphe présente l’ensemble de celles afférentes aux processus php5-fpm. Ce type de graphe est très utile pour correctement dimensionner un serveur php5-fpm.
Et un autre graphe que j’aime voir rester plat, toutes valeurs à zéro.
Il serait sûrement plus judicieux de faire autre chose qu’un graphe sur ce genre de valeurs censées rester à zéro. La première chose qui vient à l’esprit : Les notifications. Mais chaque chose en son temps !
À suivre…
Il nous faut encore superviser un serveur Apache2, Nginx, MySQL et bien sûr le système pour être proche d’une supervision interne complète d’un serveur web « classique ». À compléter bien sûr d’un monitoring externe comme peut en proposer Check my Website !