/ DevOps

Au coeur de nos tests de performance : de JMeter à GOReplay (1/2)

Avant même de présenter l'usage que nous avons de GOReplay au sein de notre chaîne de Continous Delivery, il me paraît intéressant de remonter le temps afin d'expliquer les évolutions majeures concernant la gestion des tests de performances concernant le site web https://www.pagesjaunes.fr

2009/ Tests de perf "manuels"

Afin de réaliser les tests de performances, nous avons une infrastructure dédiée (eh oui, la chance !). Même si cette infrastructure n'est pas identique à la PROD, nous avons réussi à l'organiser de façon à ce que cela ne soit pas problématique dans l'analyse de nos résultats. En effet, des tests de rupture sont réalisés pour trouver les points de contention, des tests de vieillissement sont réalisés pour s'assurer du bon comportement des applicatifs et le résultat des tests est extrapolé.
A cette période, pas de virtualisation chez nous (à part sur les plateformes de dévs), que du serveur physique !

JMeter, malgré son interface rustique, est d'une aide précieuse depuis longtemps. Cet outil est en effet au coeur de notre système de validation. A cela, on ajoute Maven 2 avec des versions alpha des plugins chronos-jmeter-maven-plugin et chronos-report-maven-plugin afin de lancer JMeter et de générer quelques rapports. Paix à leurs âmes, ces plugins ne sont plus des nôtres aujourd'hui.
D'un point de vue techno, on est sur une stack classique Java 1.5 sur de l'Apache Tomcat et du struts en présentation. Côté JVM, la jrockit est utilisée ce qui nous permet en dév, d'utiliser Mission Control afin de faciliter les analyses de comportement (consommation mémoire, CPU, synchronisation...).

Petite précision organisationnelle :

  1. Sortie d'une version majeure tous les mois (ou mois et demi) avec une gestion en cycle tuilé (8 semaines de dév, 4 semaines de mise au point)
  2. C'est l'équipe d'architecture qui a la responsabilité des tests de performances. Cette responsabilité inclut l'écriture, l'exécution, l'analyse des scénarios JMeter. Mais pas que. Il reste ensuite à corriger et évangéliser les dévs sur les bonnes pratiques de performance. Dernier point, il reste le PV de mise en production à signer avec son sang. Bon vu qu'il y avait une case Accepté avec réserve, on en profitait. En même temps, 8 semaines de dév + le temps d'avoir une plateforme qui fonctionne, on avait entre 1 et 2 semaines avant MEP pour donner le verdict... avant que ça ne plante en PROD :o

PV_J1B

2010/ Tests de perf "semi-automatisés"

Pas de grands changements dans cette période, l'organisation n'ayant pas radicalement évoluée.

Ajout d'Hudson pour le lancement des tests afin de faciliter l'exécution la nuit et l'enchaînement des tests des différentes applications métiers.

Courant 2011, on remplace Hudson par son fork Jenkins et on intègre jmeter-plugins pour notamment utiliser le Performance Monitoring. Couplé à un plugin maven développé maison (que l'on aurait dû rendre opensource à l'époque !), ça nous permet d'ajouter aux rapports générés des métriques systèmes et métiers (à l'aide entre autres de JMX) durant les tirs de charges.

Voilà pourquoi on parle de tests semi-automatisés. Toute la partie lancement, ordonnancement, redémarrage des serveurs entre chaque test est automatisé, mais reste un process lancé manuellement une fois la plateforme opérationnelle. Les tests ne sont pas lancés dans une chaîne de build après le commit d'un développeur.

2014/ Tests de perf "automatisés"

On rentre dans une année charnière côté Direction Technique. Un vaste chantier de refonte de l'architecture du site ainsi que de notre chaîne de build est lancé. L'approche micro-services sous forme d'API REST est logiquement choisie. La partie organisationnelle change également pour passer d'équipes spécialisées à une organisation en Collaborative Team (mélange des différents corps de métiers au sein d'une même équipe - PO, dév, recetteurs...). Une partie de l'équipe d'archi reste transverse, alors qu'une équipe technique pour construire la chaîne de build est créée avec dévs, intégrateurs, recetteurs et archis.

Côté performance, on souhaite avoir 2 phases de tests pour chaque API dans notre chaîne de build.
Une 1ère phase afin d'avoir un retour très rapide après le commit du développeur qui doit se faire sans avoir à déployer l'API dans un conteneur web.
Une 2nde phase plus longue qui arrive en fin de chaîne de build et qui teste l'API dans un environnement proche de la production.
Dans ces 2 types de test, des critères de performance de l'API doivent permettre de valider ou non l'éligibilité de celle-ci pour un passage en production.

Voici un schéma pour résumer notre flow de notre chaîne de Continous Delivery.
Chaine_CD-1

Voici les technos utilisés par nos différents modules lors du démarrage de la construction de la chaîne de build :

  • 15% PHP (uniquement pour la couche de présentation)
  • 70% JAVA
  • 5% NodeJS
  • 10% Middlewares (Varnish, Tomcat...)

Etape 1B : TI de performance

Pour cette étape, on ne souhaite, dans un 1er temps, faire des tests que sur les API JAVA. On veut également que la responsabilité de ces tests soit portée par les Collaborative Team, notamment par les développeurs.
Pour cela, on s'appuie sur Contiperf qui est un framework JAVA qui se base sur JUnit. Impeccable pour les développeurs d'API JAVA. Quelques annotations à ajouter sur la méthode de test afin de spécifier le nombre d'itérations, le nombre de threads en //, la période de chauffe ainsi que les résultats attendus (percentiles, moyenne)

Aujourd'hui, on utiliserait plutôt JMH pour faire ce type de test.

Voici le REX après plusieurs mois d'expérimentation :

  • Les coûts de maintenance sont élevés (seuils d'acceptance trop volatiles)
  • 1 seul problème décelé qui l'aurait été à l'étape de perf suivante
  • Développement lié à JAVA. Coût à prévoir pour l'adapter aux autres technos
  • Intéressant pour faire du vrai micro-benchmark sur du socle-technique, mais pas sur des API métiers.

On a donc décidé de mettre fin à cette étape de tests début 2015, en se consacrant uniquement aux tests de performances d'étape 3. On peut maintenant confirmer que cela n'occasionne pas un manque dans nos procédures de tests automatisés, sauf peut-être sur notre socle-technique.

Etape 3 : Tests de performance

Pour cette étape, on souhaite, dans un 1er temps, faire des tests sur l'ensemble des modules exceptés nos middlewares. On souhaite également que la responsabilité de ces tests soit portée par les Collaborative Team : recetteurs ou développeurs.
Début 2014, GOR (ex GOReplay) nous fait de l'oeil, mais 2 points nous rebutent.

  1. Le projet est jeune
  2. GOR ne s'intégrait pas dans la philosophie de notre chaîne
    • Besoin de tester un module unitairement
    • Plusieurs mois de dévs avant la MEP. Pas de trafic en entrée de ces modules en prod.
  3. Il fallait aller vite...

Qu'à cela ne tienne, on reviendra à la charge avec notre GOR plus tard !

On décide donc de partir sur quelque chose de plus classique, JMeter, que l'on avait déjà à moitié automatisé les années précédentes, mais en améliorant l'intégration dans une chaîne de build. En contrainte, les tests ne doivent pas dépasser 20mn par module.

Pour cette étape 3, on dédie une infra spécifique à nos tests de performance automatisés, et cette fois-ci on n'utilise que de la VM, enfin presque, sauf pour notre Back-Office ! De plus on a une 2ème infra identique pour nous permettre de faire des tirs manuels, analyser les problèmes, écrire les scénarios.

On migre sur des plugins maven plus récents et maintenus : jmeter-maven-plugin et jmeter-analysis-maven-plugin. Il y a 2 critères d'acceptance du test de performance :

  1. Le taux d'erreurs doit être inférieur au seuil fixé
  2. Le débit (nombre de req/s) doit + ou - être égale à x % du seuil fixé.

Tout ça, c'est beau sur le papier ! Mais qu'en est-il vraiment ?

Et ben, c'est moins joli... L'adoption côté développeurs / recetteurs reste compliqué. En même temps, on ne leur en voudra pas, qui prend réellement plaisir à écrire un scénario JMeter :D ?

Non, on vous assure que ce n'est pas pour ça qu'on voulait refiler le bébé aux CT !

Les seuils de débits sont à ré-étalonner à chaque modification du scénario et on s'en aperçoit souvent une fois que ça a planté dans notre chaîne de build. La représentativité de la production est difficile à prévoir API par API. Quelle est la répartition des requêtes sur chacune des API ? Les requêtes tueuses sont souvent absentes de nos corpus de requêtes. Et puis la fameuse question : quel est l'impact si ça devait partir comme ça en production ?
A cela, on ajoute une maintenance coûteuse quand celle-ci est faite, car après plusieurs mois, on s'est aperçu que les scénarios de certains modules n'avaient pas évolué.
Dernier point, on a une solution de feature flipping pour activer des nouvelles fonctionnalités en production, sauf que les modules étaient validés sans forcément les feature activées. Bref, après plusieurs incidents en prod, on s'aperçoit que la chaîne de validation de performance est perfectible.

Il faudrait la rendre plus proche de la réalité, plus simple à comprendre et à maintenir, et donc moins coûteuse. Et bien pour ça, il ne vous reste plus qu'à attendre le second article semaine prochaine !