Retour d’expérience sur un débuggage Varnish

Ce post relate mon retour d'expérience par rapport à un débuggage turpentine/varnish, qui a été l'occasion d'avoir une première approche de ces technologies. Je commencerai tout d'abord par rappeler les aspects théoriques puis pratiques de la mise en place d'un système de cache turpentine/varnish, avant d'expliquer les outils et méthodes qui ont rendu le débuggage possible.

I Principe de fonctionnement et maquettage initial

I.1 Principe

Le principe du cache est de permettre de ne pas avoir besoin de remonter jusqu'à la source pour récupérer des données, si on se trouve dans des conditions adéquates. Dans le cas présent, la RFC 2616 (IETF) est la référence : elle spécifie des mécanismes permettant d'indiquer si la mise en cache est autorisée, si le cache est suffisamment frais par rapport aux indications du serveur et aux demandes d'un client, et fournit également le nécessaire pour valider la fraîcheur d'une réponse.

Ce fonctionnement peut être satisfaisant pour des données "statiques" non personnalisées, telles que par exemple des images. En revanche, dans le cadre de pages dynamiques, telles que les pages issues de Magento, le calcul des réponses peut rester quand même assez long. On arrive alors à l'idée de souhaiter mettre en cache des morceaux de pages qui ne bougent pas : cette idée se traduit par la mise en oeuvre de l'Edge Service Includes (ESI), norme du W3C, qui permet de subdiviser une page HTML en plusieurs pages, un peu à la manière d'iframes, mais c'est au serveur de cache d’agréger les différents bouts avant de fournir la réponse au client.

Dans le cadre des infrastructures Magento, on retrouve la mise en oeuvre d'une partie des mécanismes décrits dans la RFC 2616 et dans l'ESI dans le couple Varnish/Turpentine.
Varnish est un serveur de cache, qui fonctionne comme un frontal par rapport à un ou plusieurs serveurs "sources" (backends). La configuration de varnish, potentiellement très riche et hautement personnalisable, est stockée dans un fichier de configuration. Varnish fait passer une requête ou une réponse par une série de "routeurs", qui influent sur la suite de leur traitement. C'est justement ces "routeurs" que l'on configure principalement dans le fichier de configuration. Les routeurs qui sont le plus fréquemment utilisés sont :

  • vcl_recv : début du traitement des requêtes clientes
  • vcl_hash : calcul d'une clef de hash pour vérifier si l'information désirée se trouve dans le cache
  • vcl_fetch : traitement d'une réponse reçue du backend (avec par exemple, la gestion ESI)
  • vcl_deliver : modification éventuelle de la réponse avant envoi à un client.

Outre le fichier de configuration, il est possible de modifier dynamiquement la configuration Varnish via une interface de management en ligne de commande accessible depuis le réseau (par exemple, via du telnet).

Turpentine, qui est un module développé par la société Nexcessnet, se base sur un fichier "de référence" de configuration de Varnish, qui est personnalisé en fonction des réglages que l'administrateur aura stocké dans Magento, et utilise également les possibilités de management dynamique via l'interface de management.

De plus, Turpentine utilise la modularité de Magento (dont notamment la notion de blocks) pour mettre en oeuvre ESI : certaines portions d'une page dynamique ne sont donc plus chargées directement, mais sont référencées par une URL qui contient l'ensemble des paramètres qui sont nécessaires à sa bonne génération. Il est donc important de comprendre que Turpentine en lui-même ne rajoute pas de niveau de cache supplémentaire, mais cherche à fournir aux administrateurs et aux développeurs les moyens d'optimiser la mise en cache du contenu. Un travail important de configuration "par défaut" a déjà été fourni avec Turpentine, mais je pense qu'il est néanmoins opportun de mener une réelle réflexion par rapport au contenu pour pouvoir optimiser l'utilisation du cache - ce qui est hors du cadre de cet article.

Une fois que les mécanismes de cache sont mis en place, il s'agit de diriger (par exemple, via des enregistrements DNS adéquats, ou du port forwarding) les requêtes des clients vers Varnish, qui va réduire, dans le temps, le nombre de requêtes envoyées à Magento.

I.2 Maquettage initial

La première étape de mise en place de l'infrastructure est donc de configurer un minimum Turpentine pour lui fournir les informations basiques qui sont nécessaires à la création du fichier de configuration (ex : indication du backend source, réglage du secret partagé pour l'authentification avec Varnish), et de faire utiliser ce fichier à varnish (le plus simple étant de spécifier le fichier au lancement en ligne de commande).

A des fins de maquettage de développement, on peut facilement faire cohabiter Apache (donc Magento) et Varnish sur le même ordinateur : il suffit par exemple de faire tourner Apache et Varnish sur des adresses différentes, et de jouer sur la résolution de noms. Cependant, il ne faut pas oublier qu'il faut encore disposer d'un accès "direct" à Magento (sans passer par le cache),et que Magento stocke les URL de base des sites dans sa base de données.
Il s'agit donc de disposer pour un même nom de deux résolutions différentes : une qui passe par le cache, et une autre qui attaque directement Magento. De ce fait, le plus simple est d'utiliser deux machines pour disposer simultanément de deux résolutions différentes, configurées dans les fichiers locaux (/etc/hosts).

Généralement, un développeur n'a à sa disposition qu'une seule machine physique et souhaite travailler en "milieu clos" : on en arrive donc à utiliser des machines virtuelles que l'on connecte ensemble. On installe donc sur sa machine un logiciel de virtualisation tel que VirtualBox et, si on le souhaite, on peut ajouter des interfaces réseaux qui pourront être utilisées pour ponter les machines virtuelles entre elles. Par exemple en ajoutant des interfaces tap qui sont des interfaces de niveau 2 pouvant être créées avec la commande ip (en tant que root).


ip tuntap add dev tap<N> mode tap

où <N> est un numéro d'interface.

Il est à noter que la mise en oeuvre d'une machine virtuelle comme client de test n'est pas une chose futile, car si on utilise par exemple un système de type live-CD (par exemple Knoppix), et ce sans mettre de disque dur, on dispose d'un environnement qui restera toujours identique (autre possibilité : machine virtuelle avec disque dur virtuel non persistant).

Une fois que les VM sont démarrées et que l'on a configuré le réseau (ip et /etc/hosts), on vérifie qu'on a une bonne connectivité réseau (vérification par ping et vérification de la bonne résolution de noms), avant de commencer des réglages dédiés au débuggage.

II Débuggage : maquettage et processus

II.1 Maquettage pour débuggage du cache

Le maquettage pour le débuggage va chercher à rendre plus utilisables des trames réseaux, de sorte que le contenu des trames soit "directement lisible" lorsqu'on utilise un logiciel tel que wireshark. Pour ce faire, il va surtout s'agir de supprimer les possibilités de compression : au niveau d'Apache, on désactive le module deflate (il suffit de simplement ne pas le charger, par exemple sur une Ubuntu en veillant qu'il n'y ait pas de deflate.load dans /etc/apache2/mods-enabled) pour ne plus permettre cette compression.

Au niveau de PHP, on rajoute la directive zlib.output_compression = Off dans le php.ini pour désactiver la compression gzip. De la sorte, on arrive donc à obtenir des captures de trames qui ne seront pas compressées en sortie d'Apache.

Cependant, Varnish est capable de faire de l'encodage gzip en sortie de lui-même, et ne permet pas (à ma connaissance) de désactiver simplement cette fonctionnalité. L'astuce que j'ai employé est donc de changer le comportement du client. En effet, Firefox permet dans le about:config de changer les méthodes de compression autorisées, via le paramètre network.http.accept-encoding: on remplace la valeur par défaut par "Identity", qui est une valeur indiquée dans la RFC 2616. De la sorte, Varnish ne compressera plus non plus le résultat en sortie.

On peut donc effectuer depuis la machine de test des captures de trames réseaux à deux niveaux : au niveau d'Apache ou au niveau de Varnish, la différenciation se faisant sur les ip mises en jeu. Et ces trames sont directement utilisables dans Wireshark, puisque le contenu d'un Follow TCP Stream est lisible.
Il ne faut pas oublier que l'on peut attaquer l'interface de management de varnish en précisant les bonnes options à une seconde instance de varnishd (dont notamment l'option -T), ce qui permettra par exemple de purger certaines expressions rationnelles (regexp) du cache.
Enfin on désactive les caches Magento ainsi que le mod_expires d'Apache, afin de réduire le champ d'investigation.

II.2 Débuggage

Le débuggage consiste d'abord en la reproduction de l'erreur : dans le cas présent, il s'agit de constater qu'un bug n'est pas constaté lorsqu'on attaque directement Magento, mais qu'il est présent lorsqu'on passe par le système de cache Varnish.

Il faut ensuite identifier le contenu posant problème, en jouant si nécessaire avec la purge du cache Varnish : par exemple, si un contenu est adéquat lors du premier chargement, mais n'est pas mis à jour de manière adéquate lorsqu'on change de page, il peut s'agir d'un paramétrage de cache incorrect. Dans ce cas, on peut penser à mettre ces URL dans la blacklist de Turpentine, de sorte à ne pas mettre en cache la page en question. On peut faire des tests successifs de chargement soit en tuant et relançant Varnish, soit en utilisant l'interface d'administration de Varnish pour bannir manuellement des URL.

Néanmoins, les problèmes sont généralement plus complexes, surtout lorsqu'il s'agit d'un contenu qui est situé dans une page PHP. Il s'agit alors de commencer par trouver l'URL qui pose problème, et ce, à la fois au niveau du dialogue entre Apache et Varnish, qu'au niveau du dialogue entre Varnish et le naviguateur, les deux pouvant être différents, notamment à cause de l'ESI.

Les deux types de dialogue peuvent être capturés grâce à Wireshark depuis la machine virtuelle hébergeant Magento, en sélectionnant par exemple la bonne interface de capture, ou en capturant sur l'interface Any et en filtrant ensuite les flux via les IP mises en jeu. Wireshark sera capable de chercher directement dans les paquets capturés des chaînes de caractères,car le contenu en lui-même ne sera pas compressé dans les captures. Par ailleurs, on pourra également s'aider des headers HTTP spécifiques au cache (par exemple Max-Age), pour diagnostiquer le problème rencontré.

On peut ensuite suivre le flux TCP avec la fonctionnalité dédiée pour retrouver l'URL de la requête. Deux cas peuvent se présenter à ce moment :

  • soit l'URI est une URI Magento classique : dans ce cas, on se retrouve dans une situation classique, et on pourra activer les hints de Magento (affichage des noms des classes de blocks et des noms de fichiers de template) pour arriver rapidement au cœur du code source posant problème ; on pensera aussi à utiliser Xdebug pour faire du pas à pas et comprendre la cause du problème

 

  • soit l'URI est une URI de chargement de bloc turpentine : reconnaissable à son début : turpentine/esi/getBlock : là, c'est plus difficile de trouver le code incriminé, le plus simple est de travailler sur le fichier turpentine-esi.xml (enfin, plutôt sur une copie de ce fichier qui sera chargée grâce au système de chargement par priorité des fichiers de layout) afin de donner aux blocs des valeurs de TTL "uniques" (c'est à dire, qui permettent de différencier les blocks). On cherchera également à connaître le cheminement exact qui déclenche le chargement du fichier, en regardant par exemple le Referer HTTP de la requête : s'il s'agit du chargement d'un autre block ESI, on est dans une situation complexe que je pense préférable de simplifier, en supprimant la configuration ESI du block interne, de sorte qu'on n'ai qu'un seul niveau d'appel ESI. Lorsqu'on aura identifié le block en question, il s'agira de regarder de près son fonctionnement, et l'environnement qu'il utilise (en particulier, les appels au registre de Magento): en effet, pour qu'un bloc soit bien rendu lors de l'appel à turpentine/esi/getBlock, il faut d'abord que l'URL d'appel contienne l'ensemble des données nécessaires pour recréer le même environnement d'exécution que dans un appel classique. A cette fin, on analysera le fonctionnement du bloc (et au besoin on le surchargera) pour stocker dans le registre Magento les données nécessaires au bon fonctionnement du bloc, et on ajoutera dans la configuration turpentine du bloc en question les clefs de registre supplémentaires nécessaires, tout en jetant un oeil dans les logs Magento pour vérifier s'il n'y a pas de message par rapport à la taille de l'environnement passé en paramètre (dans la query string).

J'ai ainsi pu résoudre les problématiques de cache qui m'ont été soumises.

En conclusion, cette expérience m'a permis non seulement de découvrir la mise en cache avec Varnish, Magento et Turpentine, mais elle a également été une occasion de progresser par rapport à mon utilisation de Wireshark dans un contexte de débuggage, notamment avec la non-compression des flux et les recherches afférentes. Mais également à travers l'utilisation d'un opérateur de filtrage que je n'avais jamais utilisé auparavant dans Wireshark, qui est le tilde ("~") : il m'a permis en particulier de filtrer des requêtes HTTP par rapport au début de l'URL demandée, et ce afin de constater la présence dans mes captures d'inclusions ESI.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.