Optimiser la configuration mémoire d’Apache 2 et eAccelerator
J’ai eu l’occasion d’expliquer dans différents articles précédents l’utilité d’un système de cache d’opcode pour contribuer à améliorer de façon très substantielle les performances d’un serveur d’application web en php5. Vous pouvez notamment lire ce que j’en dis dans l’article sur l’optimisation (accélération) de WordPress, et apprendre ici comment monter une configuration LAMP sous Debian à partir de zéro, incluant eAccelerator. Enfin, cet article explique comment re-compiler eAccelerator pour l’installer ou le mettre à jour. Je vais maintenant aborder le sujet de l’optimisation de l’utilisation de la mémoire RAM pour tirer pleinement partie du gain de performance procuré par le cache d’opcode.
Gestion de la mémoire partagée
Sous un système GNU/Linux, eAccelerator fait appel à la mémoire vive partagée (shared memory, abrégée shm) pour stocker le code PHP pré-compilé et éviter des accès disque dur au serveur d’application web. Pour obtenir une accélération maximale de vos applications, l’idéal est que la mémoire allouée à eAccelerator soit suffisamment étendue pour qu’il puisse y stocker l’ensemble des scripts constituant votre application web (ou en tout cas tous ceux qui sont le plus souvent utilisés). Ceci afin de limiter au maximum les accès disques qui sont toujours beaucoup plus lent que les accès en mémoire vive.
Par défaut, si vous avez suivi les instructions d’installation données dans les pages de références vers lesquels je donne des liens au premier paragraphe de cet article, eAccelerator est configuré pour utiliser 16 Mo de RAM comme on peut le constater avec phpinfo() :
Dès que votre application web va commencer à fonctionner en accueillant des visiteurs, vous allez voir la mémoire disponible (Memory Available) diminuer, au fur et à mesure que les différents scripts compilés et mis en cache par eAccelerator se voient allouer de la mémoire (Memory Allocated).
Si votre application est modeste, avec peu de scripts PHP, les 16 Mo de mémoire RAM alloués par défaut peuvent suffire, sachant qu’eAccelerator prend le relais sur le disque (dans une arborescence hachée sous le répertoire /var/cache/eaccelerator) pour les fichiers moins utilisés. Par contre, si vous vous rendez compte que la mémoire disponible pour eAccelerator est immédiatement saturée par votre application, et pour des projets web qui comportent de très nombreux fichiers PHP (c’est pas exemple le cas des gestionnaires de contenus sophistiqués comme WordPress), vous gagnerez surement à allouer plus de mémoire à eAccelerator.
Cela peut se faire simplement en éditant le fichier de configuration d’eAccelerator :
sudo vi /etc/php5/conf.d/eaccelerator.ini
Le paramètre qui définit la quantité de mémoire RAM allouée à
sudo /etc/init.d/apache2 restart
Il y a cependant une limite à cette façon de procéder.
Le problème de la limite système
La plupart des systèmes GNU/Linux limitent en effet la mémoire partagée qu’il est possible d’allouer de façon assez draconienne : si vous augmentez de façon arbitraire la quantité de mémoire shm à allouer à eAccelerator, celui-ci ne va pas tarder à refuser de démarrer. Ceci est dû au dépassement de la limite système fixée par la configuration de votre noyau GNU/Linux (paramètre shmmax).
Il est heureusement assez facile d’augmenter cette limite, et c’est même une optimisation système très recommandable sur un serveur moderne équipé de grandes quantités de RAM, et qui doit par ailleurs faire tourner des applications qui gagnent à utiliser de grandes quantités de mémoire cache, comme des bases de données.
Pour vérifier la limite shmmax actuelle de votre système, utilisez cette commande :
sudo sysctl -a | grep shm
Sur un système Debian 6 installé par défaut, cette valeur est fixé à 33554432 octets, soit 32 Mo. C’est le maximum qu’il est possible d’attribuer à eAccelerator sans repousser la limite système.
Pour augmenter provisoirement la limite afin de procéder à des tests, on peut tout simplement procéder comme suit :
sudo echo nouvelle_valeur > /proc/sys/kernel/shmmax
Où “nouvelle_valeur” est donné en octets (Mo x 1024 x 1024)
Pour augmenter de façon permanente cette limite (pour qu’elle reste fixée après un redémarrage système), il faut la régler dans le fichier /etc/sysctl.conf : on peut tout simplement rajouter la ligne :
kernel.shmmax = nouvelle_valeur
…tout à la fin de ce fichier.
Equilibrer les besoins mémoire entre Apache et eAccelerator
L’optimisation de l’utilisation mémoire d’un serveur d’application web pour PHP5 est une démarche délicate qui est très étroitement dépendante de la morphologie de l’application déployée et de ses volumétries de trafic.
Certaines applications PHP peuvent être très gourmandes en mémoire et saturer rapidement la mémoire disponible pour Apache avec seulement quelques instances : une fois que la mémoire est saturée, le serveur va se mettre à swapper sur disque et va rapidement s’effondrer à cause de la baisse de performances d’ensemble du système.
Il est donc indispensable de surveiller et de régler finement les paramètres d’utilisation de la mémoire par Apache quand on rencontre des problèmes d’optimisation d’un serveur web. Le gain de performances obtenu par l’utilisation d’un cache d’opcode est tel qu’il est pratiquement toujours préférable d’allouer plus de mémoire à eAccelerator, quitte à restreindre le nombre d’instance Apache disponibles en parallèle pour accueillir les connexions : comme Apache travaillera plus vite en n’ayant pas à effectuer la compilation au vol de chaque fichier PHP à partir du disque, l’instance sera libérée plus rapidement et redeviendra disponible. Il suffit donc d’un plus petit nombre d’instances présentes en mémoire à chaque instant pour traiter le même nombre de requêtes entrantes sur une même période de temps.
A ce sujet : voir cet autre article consacré entièrement à l’optimisation de la configuration d’Apache 2.
La plupart des problèmes de performance mémoire d’Apache avec mod-php sont liés à la réutilisation des instances : à chaque nouvelle connexion, Apache réutilise un processus déjà existant pour économiser des ressources système. Or les instances “réservent” une quantité de mémoire nécessaire à l’exécution des scripts PHP, mais ne “relachent” jamais cette mémoire. Le résultat est que les processus Apache on tendance à “gonfler” énormément en consommant de très grosses quantité de mémoire. Il y a deux pistes principales pour limiter ce problème :
– Limiter la réutilisation des processus Apache en les forçant à “mourir” plus souvent (ce qui cause la libération de la mémoire) : cela fait redescendre la consommation mémoire mais augmente la charge CPU du fait du travail accru pour détruire et recréer des instances. Si votre système sature en mémoire mais est peu chargé en CPU, comme c’est souvent le cas, c’est une option à considérer.
– Installer un serveur web léger en parallèle d’Apache pour traiter toutes les requêtes “statiques” qui ne nécessitent pas de charger le moteur d’application PHP. Lire à ce sujet mon article sur le “moteur hybride” Apache/mod-php + NginX.
Quoi qu’il en soit, pour faire les bons choix, il est indispensable d’observer la façon dont l’application web se comporte en production, en mesurant l’utilisation de la mémoire à l’aide des utilitaires top, phpinfo() et d’observer les cycles de fonctionnement sur de longues périodes avec Munin. Ces observations permettent de faire les ajustements nécessaires pour trouver le meilleur équilibre dans l’utilisation de la mémoire RAM disponible entre le cache d’opcode, les instances Apache, et potentiellement le query-cache du serveur de base de données MySql. Nous en reparlerons peut-être dans un prochain article…
Liens utiles
- Optimiser la configuration mémoire d’Apache
- eAccelerator shared memory tweaking @ ipsure (en anglais)
- eAccelerator settings wiki (en anglais)
- PHP tuning : opcode caching @ Lxer (en anglais)
- eAccelerator configuration issues @ sitebuddy.com (en anglais)