22
déc 13

Benchmark Apache 2.4.7 – Ressources statiques

En cours d’étude comparative (~qualitative) sur les différents services liés à l’hébergement de sites internet, je travaille actuellement sur la dernière version du légendaire serveur HTTP Apache.

 

L’objectif de ce benchmark est de comparer les performances de la version d’Apache en fonction du MPM.

 

Si ces dernières sont moyennes en Prefork (meilleurs résultats obtenus en 2.2.26), nous obtenons néanmoins de bons résultats en Worker et Event (et donc threaded).

 

Pour illustrer ces performances, ci-dessous un graphique montrant le nombre de requêtes par seconde sur une page static (html).

Histoire de titiller un peu les choses (et certains confrères), j’ai effectué les mêmes tests sur un Nginx installé via les packages Debian (1.2.1-2.2+wheezy2)

 

Les configurations des services sont celles livrées par défaut.

Bien entendu, il ne s’agit que de configuration par défaut, des optimisations pour chaque service restent à faire. (ex: MaxClient en prefork à 150, limite atteinte lors du test )

 

Le test consiste à exécuter consécutivement 50 fois Apache Bench sur chaque service:

ab -n 5000 -c 250 -k http://$URL:$PORT

 

Benchmark Apache 2.4.7 (threaded)

Benchmark Apache 2.4.7 (threaded)

 

Comme nous le voyons, l’utilisation d’Apache en mode « threaded » permet d’obtenir des résultats similaires, voir meilleurs qu’avec Nginx, sur des configuration par défaut.

 

L’objectif de mes travaux actuels ne consistant pas à confronter ces 2 outils, j’y reviendrai certainement afin de pousser l’analyse plus loin.

 

Configurations des services

Nginx

nginx version: nginx/1.2.1

worker_processes 4;
worker_connections 768;

Apache 2.4.7 MPM Prefork

Server version: Apache/2.4.7 (Unix)
Server built: Dec 17 2013 10:33:17
Server’s Module Magic Number: 20120211:27
Server loaded: APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture: 64-bit
Server MPM: prefork
threaded: no
forked: yes (variable process count)

<IfModule mpm_prefork_module>
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxRequestWorkers 150
MaxConnectionsPerChild 0
</IfModule>

Apache 2.4.7 MPM Worker

Server version: Apache/2.4.7 (Unix)
Server built:   Dec 21 2013 21:15:45
Server’s Module Magic Number: 20120211:27
Server loaded:  APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture:   64-bit
Server MPM:     worker
threaded:     yes (fixed thread count)
forked:     yes (variable process count)

<IfModule mpm_worker_module>
StartServers             3
MinSpareThreads         75
MaxSpareThreads        250
ThreadsPerChild         25
MaxRequestWorkers      400
MaxConnectionsPerChild   0
</IfModule>

Apache 2.4.7 MPM Event

Server version: Apache/2.4.7 (Unix)

Server built:   Dec 21 2013 10:08:21
Server’s Module Magic Number: 20120211:27
Server loaded:  APR 1.5.0, APR-UTIL 1.5.3
Compiled using: APR 1.5.0, APR-UTIL 1.5.3
Architecture:   64-bit
Server MPM:     event
threaded:     yes (fixed thread count)
forked:     yes (variable process count)

<IfModule mpm_event_module>
StartServers             3
MinSpareThreads         75
MaxSpareThreads        250
ThreadsPerChild         25
MaxRequestWorkers      400
MaxConnectionsPerChild   0
</IfModule>


4 Commentaires sur “Benchmark Apache 2.4.7 – Ressources statiques”

  • 1 Louis Vincens a écrit:

    Malheureusement, je ne comprends pas l’intérêt de bencher des configurations par défaut.

    Il me semble que le plus important est de bien comprendre l’architecture logicielle inhérante à chaque server web ou MPM.

    1 – MPM prefork

    Le processus père crée une socket d’écoute qu’il bind sur le port 80 du serveur par défaut.
    Il entre ensuite dans une boucle infinie en attente de requêtes entrantes.
    Pour chaque requête entrante, il fork un nouveau process qui va être chargé de traiter la requête et de la renvoyer au client.
    L’appel système fork() est un appel système extrêmement couteux en ressources car nécessitant des bacules en mode noyau, une copie du processus père intégrale en mémoire, etc …
    L’avantage du prefork est de fournir une isolation maximale au niveau du traitement des requêtes et peut même permettre certaines choses très interessantes au niveau du controle des process via ITK ou SUEXEC. Couplé à des control groups kernel ce contrôle peut même devenir extrêmement pointu en dédiant par exemple un certain coeur CPU pour un certain type de requêtes, maximisant ainsi les cache hits CPU etc …
    Pour moi le gros inconvéniant de ce type d’architecture est l’utilisation excessive de mémoire, de ce fait on ne tire pas bien parti des ressources hardware de la machine.
    Dans la configuration par défaut que tu as testé, avec un StartServers aussi bas et un « MaxConnectionsPerChild 0″, on doit fork() un nouveau process par requête, entrainant ainsi une perte énorme de performances.
    Tu devrais refaire le test avec un StartServers plus élevé afin que les appels systèmes fork() soient fait au démarrage du serveur et non à la volée, et ensuite les réutiliser pour traiter plusieurs requêtes consécutives en augmentant MaxConnectionsPerChild.

    2 – MPM worker

    Le processus père crée une socket d’écoute qu’il bind sur le port 80 du serveur par défaut.
    Il entre ensuite dans une boucle infinie en attente de requêtes entrantes.
    Pour chaque requête entrante, il crée un nouveau thread qui va être chargé de traiter la requête et de la renvoyer au client.
    La création d’un thread est une opération beaucoup moins couteuse pour le système, et l’utilisation mémoire est mieux optimisée car tous les threads partagent l’espace mémoire du processus père.
    Il faut quand même retenir que l’espace mémoire alloué à un process est limité … par exemple 4go sur un système 32bits. En 64 bits, cette limitation est beaucoup plus élevée mais elle existe aussi. De plus sous linux les threads étants gérés en mode noyaux, de nombreuses bascules en mode noyau et context switches sont a prévoir. Linux à du mal au dela de 300 threads par process.
    C’est pour ces raisons que en mode worker, apache fork quand même plusieurs process qui servent de conteneurs de threads.
    L’avantage de ce mode est donc une utilisation moinde des ressources, au prix d’une isolation moinde des requêtes. En effet, si 1 requête corromp l’espace mémoire commun, alors tu auras ThreadsPerChild 25 requêtes qui vont planter en même temps.
    De plus, il faut se rappeler que le vrai parallélisme n’existe pas et qu’on reste limité par le nombre de processeurs malgré un grand nombre de threads.

    3 – MPM Event // Nginx

    Le principe de ce type d’architecture est de n’avoir qu’un seul process qui crée une socket d’écoute qu’il bind sur le port 80 du serveur par défaut.
    Ensuite il entre dans une boucle et poll ( via select, epoll, etc … ) la socket à intervalle régulier. Chaque requête génère alors un évènement ( event ), qui est mis en attente et traité lorsque aucune lecture / écriture n’a lieu sur la socket.
    Ce type de fonctionnement est interessant car il permet une utilisation mémoire bien moindre et ne nécessite pas de bascules en mode kernel intempestives, cependant lorsque le serveur est soumis à un nombre trop important de requêtes, les temps de réponse sont alors dégradés.
    Concernant le nombre de process // threads, il est effectivement possible de déléguer certains traitements à des processus // threads fils. Par exemple, le processus père va poller la socket, mettre en attente les requêtes et des qu’il a de la disponibilité, au lieu de faire le traitement lui même, il va fork() un process en charge de la requête ou d’une partie de la requête.
    L’avantage, avec de bons load balancers, une config kernel adéquate ( somaxconn pas trop haut, backlog etc … ) est que ce mode permet un excellent compromis en terme de ressources utilisées et de performances.

    4 – Architectures en pipeline

    Ce type d’architecture est assez méconnu, mais le principe est d’avoir des process // threads qui ne traitent qu’une partie précise de la requête par exemple … 1 process pour parser la requete, 1 process pour vérifier le cache, 1 process pour faire des access disques etc …
    Les pipelines peuvent également être parallélisés.

    Pour conclure, tu l’auras compris, j’ai envie de dire qu’il n’est pas pertinent de bencher des MPM, serveurs webs au fonctionnement aussi différents car ils répondent chacun à un besoin bien précis et dépendent énormément de la configuration mise en place.

  • 2 Nicolas Martinez a écrit:

    Salut Louis ;)

    Comme tout technicien, tu es allé directement au graph et aux config, sans prendre compte de ce qui est expliqué plus haut ;)

    Je comprends ton argumentaire qui était prévisible venant de techniciens, néanmoins, il s’agit ici de traitement sur du statique et d’une configuration lambda. C’est le CdC de base.

    C’est justement voulu; je dis bien qu’il n’y a aucun sizing de fait, justement pour du « out of the box ».

    Je trouve donc intéressant de tordre le coup à de vieilles rumeurs et discours tout fait. Ce n’est pas pour rien que je fais ce clin d’œil à Nginx :D

    Car les faits sont la : sans sizing, ni besoin de compétences pointues, on obtient des résultats satisfaisants pour le grand nombre avec une consommation de ressource minimale.
    C’est l’objet de ce mini article.

    Attention, je ne dis pas qu’il ne faut pas aller plus loin, que c’est pour de la prod, bien au contraire.

    Je souhaite surtout que l’on aille au bout des choses, quelque soit le soft choisi, sans pour autant s’arrêter à ce que l’on peut lire ici ou la !

    Car, je pense que tu ne me contrediras pas, mais cela fait un bout de temps que la « mode » pousse à renier Apache VS Nginx, alors que dans la plupart des cas, il s’agit juste d’un problème de configuration.

    Si je devais faire un raccourci… beaucoup se plaignent d’Apache en le comparant à Nginx, alors qu’ils l’utilisent en MPM Prefork. Je suis d’accord avec toi: Comparer du Prefork avec de l’Event, ça n’a pas de sens.

    Montrer qu’Apache en Worker ou Event obtient des résultats similaires à Nginx et sans tuning, dénonce justement ces absurdités que l’on lit de bout en bout sur le net :)

    A+

  • 3 Louis Vincens a écrit:

    Je suis tout à fait d’accord avec toi, il faut savoir de quoi on parle et ne pas se fier à tout ce qui peut être répété de part et d’autre du net, c’est pourquoi je tenais à préciser le fonctionnement de chaque architecture pour tes futurs lecteurs.
    La popularité de Nginx vient principalement du fait qu’il monte en charge ( scale ) de manière beaucoup plus douce qu’un apache qui scale lui de manière exponentielle.
    Lorsqu’on parle de performances, il faut distinguer la consommation de ressources système et les temps de réponse utilisateurs qui sont deux choses distinctes souvent confondues.
    Donc je suis encore une fois d’accord avec toi pour dire que nginx n’est pas forcément plus performant que Apache, tout dépend du cas d’utilisation et des indicateurs qui vont définir la dite performance.

  • 4 Nicolas Martinez a écrit:

    Oui bien sur, j’avais bien saisie ton approche, je te connais « bien ». ;)

    Merci pour ta contribution en tout cas :)