Artisan Numérique

/système/performances/apache/ Optimisation d'apache

Quelle que soit la manière dont on utilise Apache, vient un moment où il est nécessaire de régler un peu les performances en fonction de l'usage. Alors l'objectif de ce papier n'est pas de fournir un guide exhaustif de tout ce qu'il est possible d'optimiser dans ce sacré morceau de code, mais plutôt de permettre d'éviter quelques problèmes de base.

Reverse DNS

Les serveurs DNS peuvent mettre du temps à répondre. La première chose que nous allons donc faire est de ne pas rechercher la correspondance texte des adresses IP clientes. Cela se configure très simplement dans la configuration principale /etc/httpd/conf/httpd.conf

HostnameLookups Off
Pas de recherche DNS

Comme pour tout ce qui suit, un redémarrage d'apache est nécessaire pour que le paramètre soit pris en compte.

Keep-Alive

Le Keep-Alive est une fonctionnalité datant de la version 1.1 du protocole HTTP qui permet de garder un certain laps de temps la connexion ouverte après qu'une requête soit servie. L'objectif est ainsi de ne pas obliger le client à en ré-ouvrir une connexion s'il a encore besoin de données.

KeepAlive on
MaxKeepAliveRequests 200
KeepAliveTimeout 10
Timeout 30
paramétrage du keep-alive

Une fois le mode Keep-Alive activé, nous définissons le nombre maximum de requêtes (entendre connexions) concurrent qu'il ne faut pas fermer de suite. Pour déterminer la bonne valeur, nous imaginons ici 50 utilisateurs qui lancent 4 requêtes simultanées par navigateur pour charger leur page. Pour les satisfaire il va nous falloir garder 50*4=200 connexions ouvertes.

Ensuite viennent deux TimeOut, le premier va déterminer le temps (en secondes) avant qu'une connexion soit fermée faute de demande, et le second, le temps (en secondes) pour fermer une connection ouverte mais qui n'a pas été utilisée (ex. le GET n'est jamais arrivé). Ces deux paramètres sont à régler selon votre inspiration.

Les liens symboliques

Le système de fichier sur lequel apache va puiser ses données n'est pas sans impact sur les performances. D'un point de vue général il est bon d'éviter les liens symboliques car sinon apache devra effectuer un appel système supplémentaire par partie de nom de fichier pour résoudre ce lien.

<Directory />
  Options -FolllowSymLinks -SymLinksIfOwnerMatch
</Directory>
Refus des liens symboliques

.htaccess

Ensuite vient le problème des fichiers .htaccess. En effet, apache va par défaut chercher un tel fichier dans chaque dossier traversé. Ce comportement s'il n'est pas souhaité peut être bloqué, par dossier, comme ceci :

<Directory />
  AllowOverride None
</Directory>
Refus des .htaccess

Dossiers réseau

Les deux options qui suivent, non seulement ont un impact sur les performances, mais aussi sur la stabilité d'apache. En effet, par défaut le coco se débrouille assez mal avec les systèmes de fichier réseau (NFS, CIFS, etc.). Pour éviter ces soucis, nous devons désactiver soit par dossier (avec une directive Directory) soit globalement, l'utilisation du memory-mapping et de l'appel à la fonction SendFile :

EnableMMAP off
EnableSendFile off
Pour les systèmes de fichier réseau

Multi-Processing Modules

Apache dispose d'un système évolué de gestion des requêtes concurrentes par greffon. Sous Linux nous disposons principalement du module worker et du module prefork. prefork fonctionne à l'ancienne mode unixienne, chaque requête est prise en charge par un serveur qui est hébergé dans son propre processus. Worker quant à lui fait la même chose mais en utilisant un thread à la place du processus. D'un point de vue vitesse, l'utilisation de l'un ou de l'autre n'est pas très déterminant et la vraie différence tient à ce qu'un processus est plus lourd en mémoire qu'un thread.

Maintenant c'est un peu limité comme choix dans la mesure où certains modules apache ne sont de toute façon pas totalement thread safe. Du coup, prefork reste le passage obligé. L'exemple typique est mod_php, rien que cela.

Les vrais gains de performances sont donc obtenus en paramétrant correctement le MPM prefork :

<IfModule mpm_prefork_module>
    ...
    MaxClients 200
    ServerLimit 100
    StartServers 4
    MinSpareServers 1
    MaxSpareServers 8
    MaxRequestsPerChild 1000
    ...
</IfModule>
paramétrage prefork

Tout commence avec MaxClients qui détermine le maximum de requêtes pouvant être servies en parralèle, et donc le nombre maximum de serveurs à lancer. Ici nous reprenons bêtement le réglage donnée pour le Keep-Alive, à 4 requêtes par utilisateur. MaxClients ne peut normalement pas dépasser 256 serveurs. Si vous avez besoin de plus, il faut spécifier une nouvelle borne max avec ServerLimit.

MinSpareServers et MaxSpareServers indiquent le nombre minimum et maximum de serveurs à conserver lorsqu'ils ne sont plus utilisés, y compris par le keep-alive. Il n'est donc pas nécessaire de mettre de grosses valeurs ici sauf pour un site à haute charge qui va chercher à économiser au maximum le temps de création des serveurs. J'ai tendance à prévoir 2 utilisateurs à 4 requêtes pour ce paramètres, soit 8 serveurs.

StartServers a un peu moins d'intérêt, il consiste en gros à préchauffer apache en lançant un certain nombre de serveurs au démarrage. Alors évidement si vous mettez 100 à StartServers et 8 à MaxSpareServers, vous allez perdre 92 serveurs quelques secondes après le lancement...

MaxRequestsPerChild est quant à elle la "faucheuse" du système. Lorsque le serveur n'est plus très frai, disons après 1000 requêtes servies, il est automatiquement tué.

Le cas FasterFox

FasterFox peut être décrit comme "a pain in the ass" comme disent nos amis outre-manche. Cette extension permet de faire deux choses magnifiques : pré-charger (ie prefetching) des choses en avance pour que vous les ayez en cache quant vous avez éventuellement envie d'aller les voir, augmenter le nombre de connexions concurrentes (ie pipelining) à des valeurs dépassant largement les préconisations des RFC (jusqu'à 8 en mode "turbo"). Résultat des courses, des utilisateurs bien peu civiques qui pourrissent la bande passante, souvent pour des prunes et vous explosent vos quotas de connexions simultanées.

Pour bloquer le prefetching la solution est très simple, il suffit d'ajouter dans votre robot.txt :

User-agent: Fasterfox
Disallow: /
blockage du prefetching

En effet, FasterFox a malgré tout la courtoisie de vérifier ce fichier avant de faire son petit carnage.

Pour limiter le nombre de connexions par IP (aka pipelining) c'est un peu plus délicat et sûrement moins critique que le prefetching. En effet, comme le fait justement remarquer Daniel dans son commentaire, il n'est pas évident que 8 connexions pendant 1 secondes soient pires pour les camarades que 4 connexions pendant 2 secondes. De plus, autre remarque judicieuse du même auteur, limiter le nombre de connexions par IP, c'est prendre le risque de bloquer des utilisateurs légitimes derrières un Proxy ou plus banalement derrière un NAT.

Si vous tenez malgrès tout à mettre en oeuvre cette limitation, cela consiste à ajouter à votre apache le module mod_limitipcon qui comme son nom l'indique va limiter le nombre de connections allouées à une adresse IP à un moment T donné. Ce module dépend du module mod_status qu'il convient donc d'activer aussi.

<IfModule mod_status.c>
  ExtendedStatus On
  <IfModule mod_limitipconn.c>
            <Location /un/chemin/a/limiter>
                MaxConnPerIP 4
            </Location>

            ErrorDocument 503 \
            "Ce serait sympathique de ne pas dépasser 4 connections en parralèle, merci."
    </IfModule>
</IfModule>
blockage du pipelining abusif

Conclusion

Il y a bien d'autres paramétrages sur lequel jouer pour obtenir les meilleurs performances, mais celles présentées ici sont pour moi le meilleur rapport qualité/prix. A noter qu'un mauvais réglage, surtout dans la partie MPM, peut sans aucun problème multiplier (ou diviser) par 4 le temps de chargement d'une page.