Le daemon php-fpm est intéressant pour exécuter les codes PHP pour le serveur web. Son usage est obligatoire avec nginx, et possible avec apache2 en remplacement de l’ancien mod_php. Les avantages sont multiples : meilleures performances, plus grande souplesse de configuration et de répartition de la charge, et possibilité de cloisonner l’environnement d’exécution avec chroot. La configuration par défaut des workers doit être modifiée pour correspondre à la charge du site web et aux possibilités matérielles du serveur.

Sommaire

Les symptômes à éviter dans les logs

Si vous vous contentez de laisser les réglages par défaut sur votre pool PHP copié depuis le fichier de démonstration, vous risquez de remplir les capacités de traitement de votre serveur et de le rendre injoignable. La possibilité de configurer php-fpm est un réel avantage qui permet de définir un seuil de ressources au-delà duquel le serveur préfère faire patienter ses nouvelles requêtes plutôt que d’exploser en plein vol.

Dans /var/log/php7.0-fpm.log (configuré dans /etc/php/7.0/fpm/php-fpm.conf) on peut trouver l’avertissement suivant.

Cela a de fortes chances de se produire si vous définissez la limite pm.max_children setting à 5 et qu’un afflux de visiteurs (dans mon cas entre 400 et 1000 par jour) accède à votre site dynamique (dans mon cas WordPress). Ce message signifie que les nouveaux visiteurs ne pourront pas être traités.

Une autre possibilité est un avertissement comme quoi le serveur semble trop chargé. Dans l’exemple ci-après il y a un maximum de 50 processus autorisés, 17 en cours de fonctionnement, 7 en attente de travail. Malgré tout php-fpm décide d’en rajouter 8 de plus pour faire face au serveur qui semble « busy ». Je ne sais pas trop sur quoi il se base pour dresser ce constat, possiblement la charge CPU et la quantité de RAM disponible. Nous sommes encore loin de la limite pm.max_children ce qui signifie que les nouveaux visiteurs seront ajoutés à la liste d’attente du système d’exploitation et traités selon les capacités du matériel. La génération des pages sera juste plus lente.

 

Les politiques de gestion de processus php-fpm

Tout d’abord, le serveur web ne devrait théoriquement instancier php-fpm que pour des pages php et pas les pages statiques, à l’aide d’une règle de Proxy.

Les pools php-fpm peuvent gérer leurs processus selon trois politiques : static, dynamic et ondemand.

En mode static, un nombre de processus fixe est défini pour traiter les exécutions PHP. L’avantage c’est qu’on peut définit un seuil de ressources à ne pas dépasser, au prix de laisser des visiteurs sans réponse.

En mode ondemand, le service va constamment jongler avec les processus qui seront créés puis détruits à chaque visite ou presque. L’avantage c’est de garder un faible nombre de processus en fonctionnement, et de limiter les fuites mémoires (les processus sont détruits). L’inconvénient c’est une latence légèrement plus élevée du fait que le noyau doit lancer des processus.

En mode dynamic, c’est un mélange des deux. Un nombre de processus minimum est lancé (pm.start_servers) sans jamais dépasser la valeur de pm.max_children. Entre deux traitements de scripts PHP, les processus sont en état idle. Le nombre de processus idle est régulé entre pm.min_spare_servers et pm.max_spare_servers quitte à en tuer si besoin. Une chose est sure : pm.max_spare_servers ne peut pas être inférieur à pm.start_servers ni supérieur à pm.max_children.

On peut aussi réguler le nombre de processus idle avec deux leviers. Le premier est pm.process_idle_timeout qui laisse aux processus un sursis de quelques secondes (au cas où une requête arrive) avant de les tuer. Le second est pm.max_requests qui définit le nombre maximum de traitements possibles par processus au-delà duquel il est plus sage de le tuer plutôt que de risquer une fuite mémoire.

Vérifier le status du service php-fpm

En ligne de commande on peut lancer service php7.0-fpm status pour obtenir des informations intéressantes.

On peut également configurer une page de status visible depuis un navigateur, dans la configuration du pool.

Règles de dimensionnement des workers php-fpm (pm.max_children et autres)

Les règles générales sont valables quelque soit la politique de gestion des processus (static, ondemand, dynamic) mais les exemples sont basés sur du dynamic.

La consommation mémoire

Le serveur ne doit pas tomber à court de RAM, auquel cas il se mettrait à swapper (et à baisser en performances d’un facteur 10000 ou plus) ou à tuer des processus aléatoirement dans un dernier instinct de survie du noyau (qui instancie OOM-killer quand il n’y a plus de RAM et plus de swap).

Il faut donc s’assurer que tous les processus php-fpm (pm.max_children) ne consommeront pas toute la RAM. Vous pouvez estimer la consommation mémoire d’un processus, ou encore la mesurer avec la commande suivante (source).

La valeur RSS (Resident Set Size) indique la consommation mémoire du processus (sans ses librairies ou SWAP) tandis que la valeur SZ est très floue, c’est la quantité théorique d’espace requis si le processus devait écrire toutes ses pages mémoires en swap.

Pour mesurer la consommation mémoire, une autre méthode plus agréable est d’utiliser htop pour filtrer par nom de « Command » puis trier « MEM% » par ordre dégressif.

Si vous avez 16 Go de RAM et que vous vous rendez compte que chaque processus PHP consomme 20 Mo en moyenne, vous ne pourrez jamais dépasser 800 processus simultanés.

La charge CPU

Si vous ne tolérez que des exécutions les plus rapides possibles, alors le nombre de processus en activité ne doit pas dépasser le nombre de cœurs. C’est un cas théorique.

Ensuite, dès que vous franchirez un multiple du nombre de cœurs, vous augmenterez le temps d’exécution d’un facteur 1. Je me vois mal dépasser 10 fois le nombre de cœurs pour un ralentissement d’un facteur 10 (exemple : 80 processus autorisés sur un 8 cœurs).

Le nombre de processus en activité est compris entre pm.max_children-pm – max_spare_servers et pm.max_children – min_spare_servers.