/ DevOps

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

Suite aux diverses problématiques liés à notre chaîne de validation des performances expliquées précédemment, on souhaite remettre à plat nos tests de performance d'étape 3. Pour cela, GOReplay nous parait être un bon candidat, surtout que nous avons déjà eu quelques expériences concluantes grâce à cet outil. Retour sur nos 1ers usages jusqu'à la transformation de notre chaîne de validation des performances.

ZOOM sur GOReplay

GOReplay est un outil opensource développé en GO par Leonid BUGAEV permettant de capturer, sauvegarder et rejouer du trafic HTTP. Les configurations de tests sont nombreuses et bien documentées. On peut faire par exemple de la réécriture d'url, du filtrage, de la limitation de débit. Dans la version PRO, il y a un support des protocoles binaires (thrift, protocol-buffers...)

Pour la capture, l'outil utilise la librairie libpcap (comme tcpdump / wireshark). La mise en place est donc transparente pour les architectures déjà en place. GOReplay doit, par contre, obligatoirement se trouver sous la brique gérant les certificats HTTPS.
https-1

Il est important de rappeler que seules les urls présentes en production seront répliquées. Donc tout développement nécessitant l'ajout d'url d'accès ne pourra pas être testé en charge tant que ces nouveaux développements ne seront pas présents en production. Il peut donc être nécessaire de faire des tests de charge "one shot" avec des outils comme JMeter / Gatlin.

2015/ GOR (ex GOReplay) nous sauve la mise

Pour rappel, 2014 c'est la refonte de l'architecture du site ainsi que de la chaîne de build. L'accostage de la nouvelle version en production est prévu entre mars et mai, en basculant progressivement le trafic de l'ancienne version (vintage) vers la nouvelle (NewPJ).

MEP_NewPJ

En réalité, il y a un décalage à courant mai de cette montée en charge afin d'avoir un contours fonctionnel acceptable.
Et c'est à ce moment-là que ça se corse pour l'équipe d'archi ^^. En effet, dès lors que l'on dépasse les 30% de trafic sur la nouvelle version du site, la majorité des composants de notre système se mette à dysfonctionner et cela en l'espace de quelques secondes.

Une course contre la montre se lance afin de déterminer l'origine des problèmes. En effet, une campagne de publicités sur plusieurs chaînes nationales est programmée et le décalage n'est plus possible. Il nous reste 4 semaines pour que la totalité du trafic soit dirigé sur le nouveau site.

Une équipe est créée, une pièce est dédiée. Y'a plus qu'à. L'équipe a carte blanche et peut même faire des tests en production ! Bon, on doit évidemment limiter au maximum l'impact côté utilisateurs. On se répartit donc les tâches en suivant 3 grands axes :

  1. Analyser les incidents précédents
  2. Tenter de reproduire l'incident sur notre plateforme de charge
  3. Ajouter le monitoring manquant en production pour faciliter l'analyse lors de futurs incidents

Dans le cas de cet article, on va s'attarder sur le second point. Pour reproduire, on injecte de la charge avec JMeter sur la plateforme dédiée. On utilise les jeux de données créés pour nos tests d'étape 3 sans succès. Rapidement, on génère un nouveau jeu de données avec les requêtes de production sur une période de +/- 5mn par rapport à un incident. Sans succès, également. On tente également d'injecter de la charge avec JMeter sur l'environnement de production, toujours sans succès.

En parallèle, une cartographie des hypothèses possibles est alimentée. L'objectif est de fermer le maximum de pistes afin de nous aider à déterminer l'origine du problème. La démarche classique. On arrive donc à cibler plus précisément le problème, sans pour autant le comprendre et déterminer l'origine du déclenchement. Bref, plusieurs pièces du puzzle sont présentes, mais l'assemblage reste compliqué. On décide de lancer un plan B qui consiste à bypasser une brique de notre architecture (gestion du feature flipping) qui semble être à l'origine de nos problèmes afin d'assurer la date de sortie.

Alors que le plan B est en cours de développement, on décide de faire entrer en jeu GOReplay. On l'utilise afin d'enregistrer, lors d'une phase d'incident, l'ensemble des requêtes sur le nouveau site et aussi pour les rejouer sur l'environnement de test dédié. L'avantage par rapport à JMeter : conserver la séquence de rejeu des requêtes comme sur la prod et pouvoir également faire de l'empilement tout en continuant à envoyer de la charge. Bingo ! On réussit à reproduire l'incident et cela dans les dernières heures avant l'adoption du plan B ! Après plusieurs tests, on finit par identifier l'origine de l'incident, les dernières pièces du puzzle sont identifiées. La phase de correction est lancée, le plan B est enterré. Le site finit par sortir juste avant les campagnes de publicité.

Seules 2 lignes de commande sont nécessaires pour rejouer le trafic de prod sur l'environnement de test.

# Capture du trafic en production
sudo gor --input-raw :80 --output-file requests.log --output-file-append

# Rejeu du trafic en test
# Vitesse de rejeu lié au dimensionnement de la plateforme
gor --input-file "requests.log|25%" --output-http "http://test.pagesjaunes.fr"

2015/ GOReplay en prod pour un rejeu manuel

Suite à l'intérêt démontré de GOReplay lors de notre phase d'accostage en production, nous avons mis en place, fin 2015, la capture de l'ensemble de nos requêtes HTTP au-dessus des 3 couches de cache (Varnish) de l'architecture comme le montre le schéma ci-dessous.

PROD

Les captures ne sont pas centralisées et sont donc stockées en local sur fichier. L'objectif est juste de pouvoir récupérer les fichiers afin de rejouer les requêtes sur un environnement dédié en cas de problème de production.

Cela nous a permis notamment de résoudre des problèmes concernant des requêtes tueuses de manière très rapide, comme par exemple

  1. La réception de requêtes avec le verbe PROFIND qui altéraient le comportement de notre infrastructure.
  2. La réception d'une requête d'un de nos partenaires générant plus de 26 000 requêtes à nos API à cause d'un cas métier mal borné.

Une fois de plus, on valide la facilité d'usage de GOReplay ainsi que la rapidité de reproduction d'un problème de production sur notre environnement de test. Maintenant, nous sommes convaincus qu'il est possible d'améliorer la qualité de nos tests de performance dans notre chaîne de Continous Delivery tout en diminuant le coût de maintenance.

2017/ Tests de performance à "99% autonomes"

L'objectif est donc de remplacer les tests automatisés via JMeter par une réplication du trafic de production sur notre environnement de test. Cela nous oblige à nous questionner sur l'objectif de nos tests ainsi que l'intérêt d'une plateforme dédiée.

A date, nous avons plus de 80 modules (middleware, API, front) dans 4 langages (PHP, Java, NodeJS, Python), il est donc facile d'imaginer la complexité de gérer des KPI par module, tout comme des scénarios. De plus, il n'y a toujours pas de tests en charge sur les middlewares, ce qui reste évidemment problématique.

KPI attendus

Le choix qui a été fait est de définir les seuils en fonction des performances serveurs tolérables en production sur les différents points d'entrées critiques.
Exemple de KPI : les temps serveur de la page d'accueil, de la liste réponse des professionnels, de la fiche des professionnels, de la liste réponse de notre API partenaires, de la liste réponse pour le mobile, etc.

Cette liste de KPI est testée pour chaque module qui passe dans notre chaîne de validation grâce aux requêtes répliquées en temps réel de notre production. Il n'y a donc plus de KPI spécifiques à un module et l'impact en termes de performance sur les nouveaux développements est clairement identifiable.

Quid des plateformes de test

4 plateformes fixes sont présentes avant le passage en production :

  1. Une plateforme HD (Haute disponibilité) sur laquelle sont installés les modules validés par le chaîne et éligible à partir en production. C'est cette plateforme que la majorité des acteurs utilisent pour faire des tests au quotidien.
  2. Une plateforme de bench automatique qui permet d'injecter de la charge à l'aide de JMeter lors des tests d'étape 3.
  3. Une plateforme afin d'effectuer les tests fonctionnels des modules en étape 3.
  4. Une plateforme pour l'équipe SEO afin de pouvoir simuler des crawls.

On décide de revoir cette partie-là pour simplifier notre gestion de plateforme. On conserve 1 plateforme HD que l'on redimensionne pour accepter 10% de charge de production, et on construit une seconde plateforme HD identique à la 1ère.

Ces 2 plateformes peuvent avoir 2 noms différents, en fonction de l'usage :

  • HD1 / HD2 : un nommage physique de nos plateformes
  • HDON / HDOFF : un nommage plus fonctionnel.
    • HDOFF : plateforme accueillant les tests d'étape 3 fonctionnels et de performance à l'aide de GOReplay.
    • HDON : plateforme accueillant également du trafic répliqué de prod, les tests de crawl effectué par l'équipe SEO, les développeurs, PO etc.

Lorsqu'un module est déployé sur la HDOFF, puis validé, la HDOFF devient la HDON, et l'ancienne HDON devient la HDOFF pour dépiler le prochain module à tester.

Focus sur GOReplay dans le contexte pagesjaunes.fr

Comme le montre le schéma suivant, GOReplay est installé sur chacun de nos frontaux en production afin de capturer 100% des requêtes http. Ces captures sont ensuite envoyées à un Gor aggregator permettant de sauvegarder sur fichier l'ensemble de ces requêtes, permettant de les rejouer à posteriori manuellement. Néanmoins, un pourcentage de ces requêtes est envoyé à 2 autres Gor aggregator filtrant les requêtes d'utilisateur en mode connecté (nextstep) et ajoutant des entêtes particuliers permettant de cibler les 2 plateformes évoquées précédemment : la HD OFF (pour valider que chaque module en étape 3 ne dégrade pas les KPI), et la HD ON (pour obtenir également des KPI comparable à la production)

Capture-d-e-cran-2017-10-24-a--08.26.38

Lors de cette réplication de trafic, nous avons besoin d'avoir des traitements particuliers pour faire notamment de la réécriture d'entêtes de host, du raccrochement de session dans le cadre des appels mobiles.
GOReplay permet cela en ajoutant un système de middleware, mais il est à coder entièrement et doit pouvoir tenir la charge à la vue du nombre de requête à traiter par seconde. On va donc éviter de réinventer la roue, et on va plutôt utiliser un middleware montant facilement en charge et permettant de faire le type manipulation que l'on souhaite. Pour cela, on va utiliser OpenResty. C'est juste un Nginx permettant de faire un peu de code LUA pour des traitements un peu plus sioux :). Exactement ce que l'on cherche à faire.

Exemple de la fonction de map nginx permettant de modifier l'entête Host.

map $http_host $served_host {
    default $http_host;
    www.pagesjaunes.fr www.cd.pagesjaunes.fr;
    ~*.mob.pagesjaunes.fr$ $cd_host;
    api.apipagesjaunes.fr api.cd.apipagesjaunes.fr;
    m.pagesjaunes.fr m.cd.pagesjaunes.fr;
    pagesjaunes.fr cd.pagesjaunes.fr;
    stat.pagesjaunes.fr stat.cd.pagesjaunes.fr;
}

server {
    listen 80;

    location / {
        if ($http_host ~ "(.*).mob.pagesjaunes.fr$") {
          set $tmp_mob $1;
          set $cd_host "${tmp_mob}.cd.mob.pagesjaunes.fr";
        }


        proxy_pass http://trafficmanager;
        proxy_set_header Host $served_host;
    }
}

Concernant la configuration de GOReplay rien de plus simple.

GOReplay sur nos frontaux

gor --input-raw :80 --output-tcp gor.local:28020 --http-disallow-header Host:host1 
    --http-disallow-header Host:host2 --output-tcp-stats --stats`

GOReplay en réception des captures de prod

gor --input-tcp :28020 --http-original-host --output-file /var/log/gor/%Y%m%d%H.log.gz 
    --output-file-append --output-tcp localhost:28021|10% 
    --output-tcp localhost:28022|5% --stats --output-tcp-stats

GOReplay pour filtrer des requêtes et dispatcher sur les HD

gor --input-tcp :28021 --http-original-host --output-http http://openresty 
    --http-set-header X-PJ-GOR:ON --http-disallow-header "Cookie: .*npj_sid=. *" 
    --stats --output-http-stats 

gor --input-tcp :28022 --http-original-host --output-http http://openresty 
    --http-set-header X-PJ-GOR:OFF --http-disallow-header "Cookie: .*npj_sid=. *" 
    --stats --output-http-stats

Tout cela nous apporte maintenant plusieurs choses :

  • des tests de vieillissement 24h/24 et 7j/7
  • des scénarios reflétant l'usage de nos utilisateurs et générés automatiquement
  • des KPI de tests comparables aux KPI de prod
  • la validation des features en charge avant ouverture en production
  • la validation des opérations de maintenance possible en charge

2018/ Nextstep ?

La gestion du mode connecté ainsi que la création de données ne font pas partie de nos tests de performance, car cela représente à l'heure actuelle un faible pourcentage de nos requêtes, mais celles-ci commençant à prendre de l'ampleur, il va falloir y penser. Surement des évolutions à prévoir au niveau de notre openresty afin de faire de l'association de session.

Le passage dans le cloud étant dans les cartons, il va falloir adapter notre chaîne de validation, il y aura donc certainement des modifications d'usage, mais ce dont on est certains, c'est que GOReplay va nous faciliter la tâche pour valider notre future infrastructure.