Varnish est un reverse-proxy HTTP « accélérateur » libre (licence BSD) vous permettant de soulager vos serveurs web potentiellement mis à mal par des applicatifs web gourmands en ressources ou peu, voire pas, optimisés.
Malgré son jeune âge (la version 1.0 date de 2006), il s'est rapidement distingué de ses rares concurrents que sont l'inévitable Apache, le vénérable Squid et le désormais célèbre Nginx pour plusieurs raisons. La première, c'est que contrairement aux autres il a été conçu selon la doctrine UNIX " Écrire des programmes qui effectuent une seule chose et qui le font bien " : il assure la fonction de reverse-proxy HTTP et rien d'autre (pas même HTTPS).
Son concepteur et développeur principal -- Poul-Henning Kamp [PHK]http://en.wikipedia.org/wiki/Poul-Henning_Kamp , développeur pour FreeBSD et réputé pour ses avis techniques très tranchés -- lui loue une conception résolument moderne qui exploite au mieux les capacités du matériel récent (64 bits, SMP/multi-cœurs) et explique dans ses très instructives notes d'architecture[ARNOT] que Varnish se repose largement sur les fonctionnalités du système d'exploitation pour gérer l'organisation de la mémoire (utilisation intensive de la mémoire virtuelle et de la mémoire partagée) et des threads ; je vous recommande chaudement la lecture de ces notes d'architecture, ne serait-ce que pour comprendre la différence de performance entre Squid -- décrit par PHK comme un archétype de la programmation des années 1975 -- et Varnish.
Car l'autre raison du succès de Varnish, c'est accessoirement l'un des logiciels les plus performants que j'ai eu l'occasion de voir tourner en production : il ne fait que ce pour quoi il a été créé et tout autre fonctionnalité superflue telle que la gestion des logs est déléguée à des programmes tiers qui ont accès aux données manipulées par le serveur par l'intermédiaire de segments de mémoire partagée, réduisant ainsi le nombre d'appels système effectués par le programme principal.
1. Installation
Les exemples illustrant cet article ont été testés sur une Debian Lenny 64 bits : les développeurs de Varnish proposent un paquet Debian à jour pour la version stable, la procédure d'installation est documentée sur le site officiel [VARNISH]http://www.varnish-cache.org/. De l'aveu de son créateur [VARNPLF]http://www.varnish-cache.org/docs/2.1/phk/platforms.html, les systèmes d'exploitation sur lesquels Varnish tourne le mieux sont GNU/Linux et FreeBSD, toutefois il a été confirmé qu'il fonctionne également sur Mac OS X, Solaris et NetBSD ; pour les autres UNIX (OpenBSD, HP-UX, AIX...) et Windows, vous pouvez oublier.
2. Configuration
Le premier aspect qui surprendra l'administrateur système chevronné lorsqu'il a terminé d'installer Varnish et qu'il dégaine Vi pour éditer le traditionnel /etc/varnish/varnish.conf, c'est qu'il n'y a pas de varnish.conf. En effet, Varnish se configure en deux temps : tous les paramètres relatifs au daemon varnishd tels que la taille du cache ou encore les interfaces réseau sur lesquelles écouter sont à passer sous forme d'options à la ligne de commandes. Deuxième effet kiss cool : la configuration déterminant le comportement du reverse-proxy vis-à -vis de l'applicatif web qu'il devra cacher se fait à l'aide de fichiers de configuration à la syntaxe unique -- le VCL, pour Varnish Configuration Language.
Un des points faibles de Varnish s'il en existe, c'est sa documentation : loin d'être inexistante [VARNISHDOC]http://www.varnish-cache.org/docs/2.1/, elle est cependant mal organisée et certains exemples utilisent parfois des directives qui ont été renommées, ce qui n'aide pas à configurer ce logiciel à la syntaxe déjà pas évidente. Néanmoins, avec un peu de bonne volonté et de Google-fu, on arrive rapidement à ses fins pour peu que l'on n'ait pas des besoins très complexes.
2.1. varnishd
Lors d'une installation sous Debian, la configuration de la ligne de commandes accompagnant le lancement de varnishd se fait via le fichier /etc/default/varnish, qui contient par défaut plusieurs modèles plus ou moins complets parmi lesquels vous pourrez choisir et compléter selon vos besoins ; pour les puristes, la dernière méthode vous propose tout simplement de vous débrouiller comme un grand en vous mettant à disposition une variable vide que vous définissez avec les options de votre choix et qui sera passée telle quelle à varnishd lors du lancement via le script de démarrage /etc/init.d/varnish. Voici un premier exemple minimaliste qui permet de placer un reverse-proxy Varnish en amont de votre serveur web servant les pages web dynamiques :
|
|
DAEMON_OPTS="-a :80 \ -b localhost:8080 \ -u varnish \ -g varnish \ -s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G" |
Cette configuration indique à varnishd qu'il doit écouter sur toutes ses interfaces réseau sur le port 80, que le backend (le serveur dit " source " ou " origine ", c'est-à -dire celui qui sert les pages web dynamiques) est à interroger via la boucle locale sur le port 8080, qu'il doit changer l'UID et le GID pour adopter celui de l'utilisateur varnish et du groupe varnish et enfin qu'il dispose d'une quantité de stockage de 1 Go utilisant le fichier /var/lib/varnish/$INSTANCE/varnish_storage.bin ($INSTANCE étant défini par défaut à la valeur retournée par uname -n, correspondant au nom court de la machine) ; il est possible d'indiquer à Varnish une taille de cache exprimée en pourcentage d'espace disponible sur la partition sur laquelle le fichier est stocké en suffixant la valeur par le symbole % (par défaut 50%).
Le manuel de varnishd(1) recommande de pré-créer le fichier hébergeant le cache de Varnish afin d'éviter la fragmentation et donc une potentielle perte de performance. Le plus simple est donc de créer un fichier avec ce bon vieux copain Dédé comme suit (si vous allouez 1 Go pour le cache comme dans l'exemple précédent) :
|
|
# dd if=/dev/zero of=/var/lib/varnish/`uname \ -n`/varnish_storage.bin bs=1024 count=1000000 |
Démarré ainsi, Varnish accomplira sa mission sans toutefois que vous ayez de contrôle sur son comportement concernant la mise en cache : celui-ci est régi par certaines en-têtes HTTP retournées par le client et le serveur source, en particulier celles ayant trait à la gestion du cache. Voici quelques commandements de base que le reverse-proxy suivra à moins que vous ne lui explicitiez de procéder autrement :
- ne pas cacher les requêtes contenant des cookies ;
- ne pas cacher les requêtes contenant une directive Authorization (authentification HTTP) ;
- utiliser le paramètre max-age de l'en-tête Cache-Control pour déterminer combien de temps cacher un objet (TTL) ; si rien ne le précise, Varnish gardera un objet " cachable " en mémoire pendant 120 secondes.
2.2. VCL
L'autre grande force de Varnish est son extrême flexibilité : vous pouvez intégralement personnaliser son comportement et adapter les règles énoncées ci-dessus selon vos envies ou vos contraintes. Pour ce faire, les développeurs du logiciel ont mis au point un langage de programmation dédié -- le VCL, pour Varnish Configuration Language -- qui a la particularité d'être traduit en langage C puis compilé et chargé dynamiquement par le binaire varnishd telle une bibliothèque de fonctions classique !
Afin de commencer en douceur et ne pas se faire (trop) peur, je suggère de ne pas se baser sur le fichier VCL fourni dans le paquet Debian (/etc/varnish/default.vcl) : déplacez-le en sûreté afin de pouvoir y revenir plus tard et éditez un nouveau fichier, par exemple /etc/varnish/conf.vcl.
Donnons un peu de contexte à nos exemples : imaginons un site web dont la partie web dynamique (PHP) tourne sur deux frontaux Apache et dont le contenu statique (images, scripts JavaScript, CSS...) est servi par un seul frontal Nginx.
La première étape dans la configuration de Varnish consiste à déclarer le(s) serveur(s) source(s) -- appelés " backends " dans la terminologie du VCL. L'étape suivante de la configuration concerne la modulation du comportement du reverse-proxy pour la mise en cache des objets HTTP. En interne, Varnish appelle des sous-routines qui sont exécutées à des moments-clés du traitement des requêtes : ce sont elles que vous allez altérer à l'aide d'actions pour influer sur les réactions du logiciel ; si vous n'appelez pas d'action au sein d'une sous-routine, Varnish exécutera le code VCL prévu dans le fichier de configuration par défaut.
Il existe actuellement dix sous-routines, toutefois seulement deux sont réellement utiles et concentrent 99% des modifications habituelles :
- vcl_recv : cette sous-routine est exécutée lorsqu'une requête HTTP envoyée par le navigateur d'un client a été reçue et analysée par varnishd : c'est à ce moment que vous décidez si cette requête doit être servie ou non, si des propriétés doivent être modifiées, voire supprimées et enfin, à quel backend la transmettre. Typiquement, c'est à cette étape que vous pouvez décider d'ajouter ou de supprimer des en-têtes HTTP ou encore supprimer des cookies présentés par un navigateur.
- vcl_fetch : cette sous-routine est exécutée lorsque Varnish a fini de réceptionner la réponse du backend à la requête précédemment transmise : vous pouvez ici retenter la même requête avec un backend différent si la réponse obtenue ne vous satisfait pas (erreur 404 ou 500), ajouter ou supprimer certaines en-têtes HTTP renvoyées par le serveur source (X-Powered-By, au hasard) ou encore supprimer des cookies renvoyés à tort et à travers par un certain applicatif web dont je tairai le nom -- qui commence par " Word " et finit par " Press ".
Voici les différentes actions qu'il est possible d'invoquer à l'issue d'une condition vérifiée ou non dans une sous-routine :
- pass : invoquer cette action aura pour effet de " passer " la requête originale à un backend (si appelée depuis vcl_recv) ou la réponse d'un backend au client (si appelée depuis vcl_fetch) sans la cacher.
- lookup : dans la sous-routine vcl_recv, ordonne à Varnish de servir le contenu caché correspondant à la requête du client (s'il en existe) même si tout indique qu'elle devrait être passée directement à un serveur source ; il n'est pas possible de l'invoquer depuis la sous-routine vcl_fetch.
- deliver : indique à Varnish de servir les données cachées au client.
- pipe : d'usage plus rare, cette action indique à Varnish de se court-circuiter et de se limiter à transférer les octets entre le client et le backend et vice versa sans même analyser le contenu des données qui lui passeront au travers.
- esi : indique à Varnish qu'il doit interpréter les balises ESI[VARNESI] trouvées dans la réponse du backend avant de la retourner au client.
- Voici maintenant les structures internes que vous allez pouvoir manipuler dans les sous-routines vues à l'instant :
- req ("request") : représente la requête provenant du client une fois que Varnish l'a intégralement reçue : l'essentiel des modifications que vous voudriez y apporter s'effectue dans la sous-routine vcl_recv.
- beresp ("backend response") : cet objet représente la réponse du backend. En manipulant cet objet, vous pourrez altérer les en-têtes HTTP du serveur source consulté par Varnish dans la sous-routine vcl_fetch.
- obj ("object") : cet objet représente l'objet HTTP stocké en cache. Seule sa propriété obj.ttl est modifiable, les autres ne sont accessibles qu'en lecture seule.
Outre ces sous-routines, actions et objets, le VCL vous propose des opérateurs tels qu'on en trouve dans tous les langages de programmation afin de définir la logique de votre politique de mise en cache :
- = opérateur d'assignation
- == opérateur de comparaison
- ~ opérateur de correspondance (" match ")
- ! opérateur de négation
- && "ET" logique
- || "OU" logique