Dans ce premier billet, je vous présentais la solution de test de charge dans les nuages BlazeMeter, basée sur l’outil Apache JMeter, illustré par un petit test. Dans ce nouveau billet, on passe aux choses sérieuses : le (très) gros test de charge.
Mon objectif était le suivant : faire un tir de charge avec 100 000 utilisateurs virtuels actifs.
Pour faire ce type de gros test, depuis les nuages, il faut un serveur cible (ou une solution cible). Pour ma part, j’ai eu la possibilité d’avoir un prêt d’un très gros serveur situé dans un centre de données à Poitiers/France connecté directement sur une importante dorsale Internet via une liaison à 100 MBits/s. Autrement dit le serveur était bien placé sur Internet pour être attaqué par la solution BlazeMeter.
Le serveur en question est un Dell R815, ayant 4 CPU de 12 cœurs chacun (AMD Opteron 6176), soit 48 coeurs, accompagné de 256 Go de mémoire RAM, connecté à 1 Gbits/s (en ip bonding) sur un commutateur en liaison avec un pare-feu BSD (en mode NAT). Le système d’exploitation dudit serveur est GNU/Linux Debian 6.0.5 en 64 bits.
Le serveur était totalement disponible pour ce test (pas d’autres services tournant dessus).
L’idée étant de faire un test de charge pour « tester » la solution BlazeMeter, et non pas de tester la performance du serveur cible, j’ai choisi de ne mettre qu’un serveur Web Apache avec 3 pages HTML statiques. Le serveur Apache est configuré avec mod_deflate et mod_cache.
Le scénario de test sera le suivant :
- page d’accueil (+assertion réponse)
- page A (+assertion réponse)
- page B (+assertion réponse)
- pas de ressources statiques (pour préserver la bande passante)
- 5 secondes de pause entre chaque page
- 1 itération par minute pour chaque utilisateur virtuel
- les requêtes HTTP utilisent l’implémentation HttpClient 4
Concernant le nombre d’utilisateurs, avec BlazeMeter il y a deux options possibles :
- soit on surcharge les valeurs via l’interface d’administration Web,
- soit, comme d’habitude, on le définit dans son scénario depuis son JMeter ;
J’ai choisi de configurer le plan de test avec 10 000 utilisateurs dans mon élément Groupe d’unités directement depuis JMeter. Sachant que le nombre de « moteur » (engine) JMeter dans BlazeMeter sera de 10 (cf capture suivante), cela fera ainsi 10 x 10 000 = 100 000 utilisateurs virtuels actifs pour mon gros scénario de tir.
NB. 10 000 utilisateurs par injecteur, c’est possible, car le scénario est simple : juste 3 requêtes+assertions, pas d’extras.
Voici ci-dessous le formulaire d’envoi du scénario JMeter dans BlazeMeter. Ce sera donc 10 JMeterEngines (les injecteurs), plus 1 console (le contrôleur).
Également au niveau des propriétés avancées, j’ai choisi des serveurs « Large » (faisant référence à la grille de tarifs Amazon EC2) car 10 000 utilisateurs par injecteurs, il faut des serveurs costauds. Par ailleurs, j’ai réduit la durée de « location » des machines JMeterEngine à 1h, car mon tir est programmé pour durer 3000 secondes (50 min). Ces options ont un impact sur le « coût » du tir.
Voilà mon scénario est enregistré dans BlazeMeter, il va consommer 22 crédits de test : 10 x 2 Large Servers + 1 x 2 Large Console = 22.
Ensuite, et bien, on lance le tir ! et on suit les résultats depuis la console BlazeMeter
[…wait&see…]
Et voici le graphique des résultats :
Sur ce graphique, on voit que :
- J’ai arrêté volontairement le tir à 80 000 utilisateurs, car le temps de réponses était en train de se dégrader (pas la peine de continuer)
- La dégradation du temps de réponse a commencé vers 55 000 utilisateurs
Sur le tableau ci-dessous, on voit de belles choses :
Au total, plus de 3 millions de requêtes HTTP en 27 min, soit environ 117 000 requêtes par min (~1940 requêtes HTTP par seconde !).
Comme on peut le deviner à travers les valeurs médianes et minimum, le temps de réponse quand tout va bien est inférieur à 100 ms. Bien entendu les valeurs moyenne et 90e centile sont hautes à cause du problème vers 55 000 utilisateurs.
En jouant un peu sur les courbes du graphique à afficher, on voit que la latence (les premiers 8192 bits reçus par JMeter pour la réponse HTTP) se dégrade en même temps et de la même façon que le temps de réponse général. On peut commencer à penser que le problème vient du réseau.
Si on choisi de voir les erreurs « NON_HTTP_RESPONSE », elles apparaissent également en même que la dégradation du temps de réponse.
Le détail des messages (BlazeMeter permet de télécharger l’ensemble des fichiers jmeter.log des injecteurs) :
-
I/O exception (java.net.NoRouteToHostException) caught when connecting to the target host: No route to host
-
I/O exception (org.apache.http.NoHttpResponseException) caught when processing request: The target server failed to respond
Visiblement il y a eu une saturation niveau réseau.
Ci-dessous, le graphique de la bande passante du pare-feu (mutualisé) devant le serveur, on voit que l’on monte jusqu’à presque 70 Mbits/s en débit sortant (rappel, la liaison Internet est de 100 Mbit/s). Donc à priori la bande passante de liaison à Internet n’est pas (encore) saturée.
Ci-dessous le graphique de l’interface réseau du serveur ciblé (interface 1 Gbits/s en ip bonding sur 2 interfaces physiques). Noter que les valeurs sont en Mo/s (MBps) donc il faut multiplié par 8 (grossièrement) pour avoit la valeur en Mbits/s. Ici on voit bien qu’un plafond a été atteint niveau réseau à travers le sorte de « plat » après 20 min de tir.
Coté CPU, on est tranquille. Normal, vu que ce sont des pages statiques et en cache Apache (sans parler de la configuration du serveur).
Bon, que conclure… ?
Une chose est sûre, il y a eu de la saturation niveau réseau. Par contre, difficile de savoir exactement de quel élément du réseau : le parefeu, les commutateurs, les routeurs, les injecteurs JMeter (10 000 VU c’est trop?)…. Je n’ai pas malheureusement accès à toute la supervision des équipements réseau.
Si on veut réussir ce tir avec 100 000 utilisateurs, il faudrait mettre plus de JMeterEngine (disons 20), pour écarter un problème éventuel de ce coté. Et aussi avoir la supervision du routeur et des commutateurs sur le chemin réseau.
Mais cela peut aussi venir d’un problème dans la pile TCP/IP ou dans la configuration du Apache ciblé…
Beaucoup de conjectures possibles.
Bon, il ne s’agit que d’un test de « test de charge » avec BlazeMeter. Et ce test là, il est réussi : avec BlazeMeter on peut faire de très gros volumes !
Pour aussi me dédouaner de ne pas avoir réussi à faire les 100 000 utilisateurs, je rappelle qu’il s’agit d’un seul serveur ciblé derrière une seule liaison Internet, et que généralement pour un tel nombre d’utilisateurs ont des architectures distribuées, découpées et découplées, etc. ou bien carrément des CDN (content delivery network). En gros, on aurait une architecture d’hébergement dans les nuages, à la BlazeMeter… tiens !
./
Post scriptum
En réalité, j’ai fait plusieurs exécutions de ce gros test de charge. Car j’ai rencontré des problèmes ! et oui, même si le tir présenté ci-dessus n’est pas allé jusqu’au bout, j’ai dû faire quelques optimisations, suite à des exécutions du tir n’ayant pas réussies, pour avoir ce résultat.
Voici quelques commentaires :
Tout d’abord, j’avais configuré un Apache (le apache2-mpm-worker fourni par la distribution Debian 6) en désactivant les modules inutiles, et en changeant les valeurs habituelles pour la concurrence d’accès
J’ai tout d’abord eu ce message d’erreur lors d’un tir :
(24)Too many open files: file permissions deny server access: /var/www/pagea.html, referer: http://monsite/index.html
Bien que le « ulimit -n » étaient à 65 535 (dans /etc/security/limits.conf).
Pour résoudre ce problème, j’ai activé le mod_cache d’Apache, les fichiers ont été placés en mémoire Apache, et donc plus d’accès disque.
Puis dans un second tir, c’est le netfilter (iptables) qui a posé problème. En effet, j’avais fait un « iptables -nvL » sur le serveur cible pour m’assurer qu’il n’y avait pas de firewall Linux actif. Et ce que je ne savais pas, c’est cette commande charge les module du noyau Linux liés à netfilter (le parefeu de Linux), même si il n’y a pas de règles.
Du coup, j’ai eu ce message dans les logs du kernel Linux du serveur après un test de tir échoué :
nf_conntrack: table full, dropping packet.
La solution était de décharger les modules netfilter et compagnie :
rmmod iptable_nat rmmod ipt_MASQUERADE rmmod nf_nat rmmod nf_conntrack_ipv4 rmmod nf_conntrack rmmod nf_defrag_ipv4
Annexes :
Extraits de la configuration Apache :
Valeurs totalement mises arbitairement :
ServerLimit 1024 StartServers 128 MinSpareThreads 512 MaxSpareThreads 1024 ThreadLimit 512 ThreadsPerChild 128 MaxClients 131072 MaxRequestsPerChild 16384 CacheEnable mem / MCacheSize 32768 MCacheMaxObjectCount 1000 MCacheMinObjectSize 1 MCacheMaxObjectSize 30000
Pile TCP Linux sur le serveur (copié/collé de quelque part…) :
net.ipv4.ip_local_port_range = 1024 65536 net.core.rmem_max=16777216 net.core.wmem_max=16777216 net.ipv4.tcp_rmem=4096 87380 16777216 net.ipv4.tcp_wmem=4096 65536 16777216 net.ipv4.tcp_fin_timeout = 3 net.core.netdev_max_backlog = 30000 net.ipv4.tcp_no_metrics_save=1 net.core.somaxconn = 262144 net.ipv4.tcp_max_orphans = 262144 net.ipv4.tcp_max_syn_backlog = 262144 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 2
Fichier /etc/security/limits.conf
root hard nofile 65535 root soft nofile 65535 * hard nofile 65535 * soft nofile 65535
Bonjour Milamber.
Congratulations on an excellent post!
Thank you for taking the time to review BlazeMeter for a second time.
I really like the fact that you’ve demonstrated the entire process of tuning a website configuration using load testing.
Looking forward to your next post.
Sincerely,
Alon Girmonsky
http://blazemeter.com