514 lines
18 KiB
TeX
514 lines
18 KiB
TeX
% Copyright (c) 2004-2010 Evolix <info@evolix.fr>
|
|
% Permission is granted to copy, distribute and/or modify this document
|
|
% under the terms of the GNU Free Documentation License, Version 1.2
|
|
% or any later version published by the Free Software Foundation;
|
|
% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
|
|
% A copy of the license is included at http://www.gcolpart.com/howto/fdl.html
|
|
|
|
\chapter{Varnish}
|
|
|
|
Site officiel: \url{https://www.varnish-cache.org/}
|
|
|
|
\section{Présentation}
|
|
|
|
Varnish est un reverse proxy HTTP dans le but premier est la mise en cache de
|
|
contenu. Il est également capable de gérer plusieurs backend, avec
|
|
répartition de charge et détection de panne.
|
|
|
|
Varnish, développé en C, se concentre principalement sur la performance sur des infrastructures
|
|
à haut et très haut trafic.
|
|
|
|
Un autre point fort est son langage de configuration, qui permet de paramétrer
|
|
finement le comportement de Varnish aux différentes étapes du traitement de la
|
|
requête.
|
|
|
|
Le développement de Varnish a commencé en 2005, et il est distribué sous licence
|
|
BSD.
|
|
|
|
\section{Installation}
|
|
|
|
Varnish est disponible dans les dépôts de Debian Squeeze en version 2.1.3. Il
|
|
existe également un backport du paquet de Wheezy, qui fournit la version 3.0.2.
|
|
Cette version apporte de nombreuses amélioration et fonctionnalité dans la
|
|
gestion du load-balancing entre les backends.
|
|
|
|
Installation du paquet:
|
|
\begin{verbatim}
|
|
# aptitude install varnish
|
|
\end{verbatim}
|
|
|
|
\section{Configuration}
|
|
|
|
Les possibilités offertes pour la configuration de Varnish sont assez vastes,
|
|
elles seront abordés par grands thèmes.
|
|
|
|
\subsection{Paramétrage de base}
|
|
|
|
Tout d'abord, il est nécessaire de renseigner quelques informations de base au
|
|
démon \texttt{varnishd}. Cette configuration se passe dans le fichier
|
|
\texttt{/etc/default/varnish}. Plusieurs cas de figure sont proposés à titre
|
|
d'exemple dans ce fichier, en voici un autre avec quelques optimisations
|
|
supplémentaires:
|
|
\begin{verbatim}
|
|
DAEMON_OPTS="-a 192.0.2.1:80 \
|
|
-T localhost:6082 \
|
|
-f /etc/varnish/default.vcl \
|
|
-S /etc/varnish/secret \
|
|
-s malloc,3G
|
|
-s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,10G
|
|
-p thread_pools=<Number of CPU cores>
|
|
-p thread_pool_add_delay=2
|
|
-p thread_pool_max=5000"
|
|
umask 022
|
|
\end{verbatim}
|
|
|
|
Et voici quelques explications sur les paramètres:
|
|
\begin{description}
|
|
\item[\texttt{-a 192.0.2.1:80}] \hfill \\
|
|
Il s'agit du couple IP,port sur lequel Varnish attendra les requêtes HTTP à
|
|
traiter.
|
|
\item[\texttt{-T localhost:6082}] \hfill \\
|
|
Il s'agit du couple IP,port sur lequel sera accessible l'interface
|
|
d'administration de Varnish (traité plus loin dans ce chapitre).
|
|
\item[\texttt{-f /etc/varnish/default.vcl}] \hfill \\
|
|
Cette option indique le fichier de configuration à utiliser.
|
|
\item[\texttt{-S /etc/varnish/secret}] \hfill \\
|
|
\item[\texttt{-s malloc,3G}]
|
|
\item[\texttt{-s file,/var/lib/varnish/\$INSTANCE/varnish\_storage.bin,10G}] \hfill \\
|
|
On indique ici qu'une partie du cache sera stocké en mémoire 3~Go, ainsi que
|
|
dans un fichier plat sur le disque, qui sera limité à 10~Go.
|
|
\item[\texttt{-p thread\_pools=<Number of CPU cores>}]
|
|
\item[\texttt{-p thread\_pool\_add\_delay=2}]
|
|
\item[\texttt{-p thread\_pool\_max=5000}] \hfill \\
|
|
L'option \texttt{-p} permet de modifier différents paramètres d'exécution.
|
|
De nombreux paramètres peuvent être modifiés, la liste complète avec leur
|
|
description se trouve ici:
|
|
\url{https://www.varnish-cache.org/docs/2.1/reference/varnishd.html}.
|
|
|
|
\texttt{thread\_pools} indique le nombre de groupe de threads à lancer. Cette
|
|
valeur ne devrait pas dépasser le nombre de c\oe{}ur disponible sur le système
|
|
(pour des raisons de performance). Pour \texttt{threa\_poo\_ad\_delay}, il
|
|
s'agit du temps en milisecondes à attendre avant la création d'un nouveau
|
|
thread. Et enfin \texttt{threa\_poo\_max} représente le nombre total de
|
|
thread maximum à ne pas dépasser, tout pool confondus.
|
|
\item[\texttt{umask 022}] \hfill \\
|
|
Varnish s'attend à avoir un umask à 022 pour s'exécuter
|
|
correctement. \'Etant donné qu'il n'est pas forcé dans le script d'init,
|
|
nous le plaçons ici manuellement.
|
|
\end{description}
|
|
|
|
\subsection{Aperçu de la syntaxe du langage VCL}
|
|
|
|
La syntaxe VCL est complexe mais puissante. On découpe un fichier VCL en
|
|
plusieurs sous-routines dans lesquelles on définit des actions/comportements en
|
|
fonction de certaines conditions.
|
|
|
|
Concrètement, 99~\% des règles sont faites dans les 2 sous-routines
|
|
\texttt{vcl\_recv} et \texttt{vcl\_fetch}.
|
|
|
|
\begin{itemize}
|
|
\item \texttt{vcl\_recv} est appelé AVANT le début de la requête au backend.
|
|
On peut donc choisir vers quel backend renvoyer la requête. On peut aussi
|
|
de modifier la requête (modifier des entêtes HTTP, supprimer des demandes
|
|
de cookies, etc\dots). Seul les actions \texttt{set req.} sont possibles.
|
|
\item \texttt{vcl\_fetch} est appelé APRÈS la réception de la réponse du
|
|
backend. Les actions \texttt{set req.} sont possibles, mais aussi \texttt{set
|
|
beresp.} (pour \emph{backend response}).
|
|
\end{itemize}
|
|
|
|
Voici donc des règles typiques:
|
|
\begin{verbatim}
|
|
sub vcl_recv {
|
|
|
|
if (req.http.host ~ "(www\.example\.com|example\.com)") {
|
|
set req.backend = default;
|
|
}
|
|
|
|
if (req.url ~ "^/images") {
|
|
unset req.http.cookie;
|
|
}
|
|
}
|
|
|
|
sub vcl_fetch {
|
|
if (req.url ~ "\.(png|gif|jpg)$") {
|
|
unset beresp.http.set-cookie;
|
|
set beresp.ttl = 3600s;
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
Voici un certain nombre de \emph{conditions} possibles:
|
|
\begin{verbatim}
|
|
# Condition sur l'entête HTTP Host:
|
|
if (req.http.host ~ "^regex$")
|
|
# Présence d'un cookie
|
|
if (req.http.cookie) {
|
|
# Condition sur l'URL demandée
|
|
if (req.url ~ "^/regex$")
|
|
# Si le backend est accessible
|
|
if (req.backend.healthy)
|
|
# Présence entête Accept-Encoding
|
|
if (req.http.Accept-Encoding)
|
|
# Condition sur la requête faite
|
|
if (req.request != "GET" && req.request != "HEAD")
|
|
# Présence de l'entête X-Forwarded-For
|
|
if (req.http.x-forwarded-for)
|
|
# Condition sur les entêtes envoyés
|
|
if (req.http.Authorization || req.http.Cookie)
|
|
# Condition
|
|
if (req.http.Cache-Control ~ "no-cache")
|
|
# Si la réponse du backend permet la mise en cache
|
|
if (beresp.cacheable)
|
|
# Condition sur le temps de mise (Cache-Control: max-age a priori)
|
|
if (beresp.ttl < 120s)
|
|
# Condition sur le statut des réponses
|
|
if (obj.status == 404 || obj.status == 503 || obj.status == 500)
|
|
\end{verbatim}
|
|
|
|
Voici un certain nombre d'\emph{actions} possibles:
|
|
\begin{verbatim}
|
|
# Renvoyer vers un backend
|
|
set req.backend = baz;
|
|
# Supprimer les cookies dans la requête
|
|
unset req.http.cookie;
|
|
remove req.http.cookie;
|
|
# Supprimer un certain nombre d'entêtes HTTP
|
|
remove req.http.X-Forwarded-For;
|
|
remove req.http.Accept-Encoding;
|
|
# Positionner un certain nombre d'entêtes HTTP pour la requête
|
|
set req.http.X-Forwarded-For = client.ip;
|
|
set req.http.Accept-Encoding = "gzip";
|
|
set req.http.Accept-Encoding = "deflate";
|
|
# Positionner un certain nombre d'entêtes HTTP pour la réponse
|
|
set obj.http.expires = "Mon, 1 Jan 2007 00:00:01 GMT";
|
|
set obj.http.X-foo = "bar";
|
|
# Renvoyer une erreur HTTP
|
|
error 404 "Page not found";
|
|
\end{verbatim}
|
|
|
|
Enfin, voici les \emph{comportements} possibles:
|
|
\begin{verbatim}
|
|
# Renvoie vers le backend (pas de cache)
|
|
return (pass);
|
|
# Renvoie la version en cache (si possible)
|
|
return (lookup);
|
|
return (deliver);
|
|
# Renvoie "directement" vers le backend sans inspection du contenu
|
|
return (pipe)
|
|
# Redémarre la demande au backend (et incrémente le compteur de restarts)
|
|
return (restart);
|
|
\end{verbatim}
|
|
|
|
\subsection{Gestion du cache}
|
|
|
|
En se positionnant entre le client et le serveur applicatif, Varnish permet de
|
|
lire et surcharger si besoin les entêtes HTTP de contrôle du cache. Par défaut,
|
|
celle ci sont lues et pris en compte, mais on peut redéfinir le comportement
|
|
dans la configuration.
|
|
|
|
Voici quelques exemples d'utilisation typique:
|
|
|
|
\paragraph{Forcer le TTL pour certains contenu}
|
|
\begin{verbatim}
|
|
sub vcl_fetch {
|
|
if (req.url ~ "\.(png|gif|jpg)$") {
|
|
set beresp.ttl = 5d;
|
|
set beresp.http.magicmarker = "1";
|
|
}
|
|
}
|
|
|
|
sub vcl_deliver {
|
|
if (resp.http.magicmarker) {
|
|
unset resp.http.magicmarker;
|
|
set resp.http.Age = "0";
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
\texttt{beresp.http.magicmarker} permet de marquer l'objet pour pouvoir ensuite
|
|
remettre son age à 0 (dans \texttt{vcl\_deliver}.
|
|
|
|
Pour que le changement de TTL le soit également coté client, on réécrit le
|
|
header HTTP \emph{Cache-Control} en ajoutant (dans le premier \texttt{if}:
|
|
\begin{verbatim}
|
|
set beresp.http.cache-control = ``max-age=432000'';
|
|
\end{verbatim}
|
|
|
|
\paragraph{Indiquer si un objet provient du cache ou pas dans les headers HTTP}
|
|
Dans un but de debugage, il peut être intéressant d'indiquer si un contenu
|
|
provient du cache de Varnish ou non. Cela se fait simplement comme ceci:
|
|
\begin{verbatim}
|
|
sub vcl_deliver {
|
|
if (obj.hits > 0) {
|
|
set resp.http.X-Cache = "HIT";
|
|
} else {
|
|
set resp.http.X-Cache = "MISS";
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
\subsection{Gestion du load-balancing}
|
|
|
|
Tout d'abord, il faut définir au moins un backend pour que l'ensemble puisse
|
|
fonctionner correctement. Cela se fait à l'aide de la directive
|
|
\texttt{backend}, comme ceci:
|
|
\begin{verbatim}
|
|
backend www00 {
|
|
.host = "192.0.2.8";
|
|
.port = "80";
|
|
}
|
|
|
|
backend www01 {
|
|
.host = "192.0.2.14";
|
|
.port = "80";
|
|
}
|
|
\end{verbatim}
|
|
|
|
Il est ensuite possible de grouper ces backends dans un cluster, appelé
|
|
\emph{director} dans le langage de Varnish:
|
|
\begin{verbatim}
|
|
director baz round-robin {
|
|
{ .backend = www00; }
|
|
{ .backend = www01; }
|
|
}
|
|
\end{verbatim}
|
|
|
|
Et enfin, on indique dans quel cas il sera utilisé (dans l'exemple il sera
|
|
utilisé dans tous les cas, pas de condition):
|
|
\begin{verbatim}
|
|
sub vcl_recv {
|
|
set req.backend = baz;
|
|
}
|
|
\end{verbatim}
|
|
|
|
Il s'agit ici de la configuration la plus simple possible. Maintenant, il peut
|
|
être intéressant d'ajuster certains paramètres:
|
|
\begin{itemize}
|
|
\item Dans l'exemple ci dessus, le director est en mode round-robin. Le trafic
|
|
est alors réparti équitablement entre les backend. On peut définir un
|
|
«poids» pour chacun des backends, afin de jouer sur la répartition du trafic
|
|
entre eux:
|
|
\begin{verbatim}
|
|
director baz random {
|
|
{
|
|
.backend = www00;
|
|
.weight = 6;
|
|
}
|
|
{
|
|
.backend = www01;
|
|
.weight = 4;
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
Pour cela, on change le mode du director pour \emph{random}.
|
|
\item Une directive importante est \texttt{.max\_connections}. Elle permet de
|
|
limiter le nombre de connexions concurrentes envoyées sur un backend. En en
|
|
positionnant une sur chacun des backends, Varnish saura qu'il devra ignorer
|
|
le backend saturé et en choisir un autre, afin de ne pas le surchargé.
|
|
\begin{verbatim}
|
|
backend www00 {
|
|
.host = "192.0.2.8";
|
|
.port = "80";
|
|
.max_connections = 80;
|
|
}
|
|
\end{verbatim}
|
|
|
|
\item Il est possible également de répartir les requêtes sur les backends
|
|
suivant des critères sur la requête. Le mode du director à utiliser est
|
|
alors \emph{client}:
|
|
\begin{verbatim}
|
|
director baz client {
|
|
{ .backend = www00; }
|
|
{ .backend = www01; }
|
|
}
|
|
|
|
sub vcl_recv {
|
|
set req.backend = baz;
|
|
set client.identity = req.ip;
|
|
}
|
|
\end{verbatim}
|
|
Dans l'exemple ci-dessus, le critère utilisé est l'IP du client
|
|
(\texttt{client.identity = req.ip}. Les autres critères possibles sont le
|
|
user-agent (\texttt{req.http.user-agent}), l'URL (\texttt{client.url}) ou encore
|
|
un cookie de session (\texttt{req.http.cookie}).
|
|
|
|
\end{itemize}
|
|
|
|
\subsection{Gestion du failover}
|
|
|
|
Nous avons vu que Varnish était capable de gérer plusieurs backend en contrôlant
|
|
la répartition des requêtes sur chacun d'eux. Il est aussi capable de détecter
|
|
une panne sur un backend, et de prendre en compte cet évènement pour modifier
|
|
son comportement: utiliser un autre backend, ou renvoyer ces fichiers en cache.
|
|
|
|
\subsubsection{Détection lors de la requête}
|
|
|
|
Il est possible d'ajuster différent paramètres indiquant le temps d'attente
|
|
maximum toléré par Varnish lors de l'interrogation d'un backend:
|
|
\begin{verbatim}
|
|
backend www00 {
|
|
.host = "192.0.2.6";
|
|
.port = "80";
|
|
.connect_timeout = 1s;
|
|
.first_byte_timeout = 3s;
|
|
.between_bytes_timeout = 2s;
|
|
}
|
|
\end{verbatim}
|
|
Les directives sont assez explicites. Passé ce délai, Varnish ira interroger le
|
|
backend suivant.
|
|
|
|
\subsubsection{Surveillance périodique des backends}
|
|
|
|
La méthode précédente est essentielle, mais pas suffisante en elle-même; en
|
|
effet, même si Varnish basculera sur un autre backend en cas de saturation du
|
|
premier, le traitement de la requête sera ralenti par l'expiration des délais
|
|
d'attente. D'autre part, une erreur HTTP 5xx par exemple sur un backend
|
|
n'empêchera pas Varnish de continuer à lui envoyer des requêtes.
|
|
|
|
Varnish offre la possibilité de surveiller régulièrement ses backends et les
|
|
marquer éventuellement comme «down». Pour cela, il est nécessaire de lui
|
|
indiquer comment les surveiller, avec la directive
|
|
\texttt{.probe}:
|
|
\begin{verbatim}
|
|
backend www00 {
|
|
.host = "192.0.2.6";
|
|
.port = "80";
|
|
.probe = {
|
|
.request = "GET / HTTP/1.1"
|
|
"Host: www.example.com"
|
|
"User-Agent: test Varnish"
|
|
"Connection: close"
|
|
"Accept-Encoding: text/html" ;
|
|
.timeout = 1s;
|
|
.interval = 5s;
|
|
.window = 8;
|
|
.threshold = 6;
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
On définit la requête HTTP que Varnish devra envoyer au backend, ainsi que
|
|
diverses directives. Le backend devra obligatoirement retourné un code HTTP 200,
|
|
il sera considéré comme «down» si ce n'est pas le cas.
|
|
|
|
Dans l'exemple ci-dessus, les checks sont fait sur un intervalle de 5 secondes,
|
|
et s'attend à avoir une réponse en moins d'une seconde. Les directives
|
|
\texttt{.window} et \texttt{.threshold} permet de définir un cycle d'hystérésis:
|
|
pour que le backend soit vu comme étant «down», il faut que, sur une quantité de
|
|
8, 6 checks ont échoué. Et inversement, pour qu'il repasse «up», il faut que 6
|
|
checks sur les 8 ont réussi.
|
|
|
|
\subsubsection{Saint mode}
|
|
|
|
Dans le cas où tous les backends sont HS, il est possible de faire en sorte que
|
|
Varnish utilise sont cache pour répondre aux requêtes (sous réserve bien sûr
|
|
qu'elle y soit). Il est donc possible d'éteindre temporairement son unique
|
|
backend, et que Varnish continu à délivrer son contenu.
|
|
Au niveau de la configuration, cela se configure comme ceci:
|
|
\begin{verbatim}
|
|
sub vcl_fetch {
|
|
if (beresp.status == 500) {
|
|
set beresp.saintmode = 10s;
|
|
restart;
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
Le \texttt{beresp.saintmode} spécifie de ne plus retenter d'interroger le
|
|
backend en cas d'erreur 500 avant un certain temps (ici 10 semonces). En effet,
|
|
autant laisser le backend tranquille, une erreur 500 ne se résolvant rarement
|
|
«magiquement» sans untervention manuelle.
|
|
|
|
\subsubsection{Backend de spare}
|
|
|
|
Il est également possible de déclarer un backend de spare, qui sera utilisé
|
|
automatiquement (et seulement) si plus aucun backend ne sont disponibles:
|
|
\begin{verbatim}
|
|
backend wwwspare {
|
|
.host = "192.0.2.32";
|
|
.port = "80";
|
|
}
|
|
sub vcl_recv {
|
|
if (!req.backend.healthy) {
|
|
set req.backend = wwwspare;
|
|
}
|
|
}
|
|
\end{verbatim}
|
|
|
|
\section{Administration}
|
|
|
|
Dans la première partie de ce chapitre, un couple IP, port avait été défini pour
|
|
faire écouter une interface d'administration de Varnish. Cette interface permet
|
|
d'envoyer un certain nombre de commande d'administration au démon varnishd.
|
|
Pour s'y connecter, une authentification est également nécessaire. Par défaut
|
|
lors de l'installation du paquet Debian, le fichier \texttt{/etc/varnish/secret}
|
|
est créé contenant la clé permettant de s'authentifier. On peut se connecter en
|
|
utilisant \texttt{telnet}, mais la commande dédiée \texttt{varnishadm} est plus
|
|
adaptée:
|
|
\begin{verbatim}
|
|
# varnishadm -T localhost:6082 -S /etc/varnish/secret
|
|
200 154
|
|
-----------------------------
|
|
Varnish HTTP accelerator CLI.
|
|
-----------------------------
|
|
Type 'help' for command list.
|
|
Type 'quit' to close CLI session.
|
|
\end{verbatim}
|
|
|
|
On est alors dans un mode interactif, où l'on peut exécuter les commandes
|
|
Varnish disponibles. Il est également possible de passer une commande
|
|
directement en argument de \texttt{varnishtop}.
|
|
|
|
Par exemple, pour pouvoir vider la totalité du cache Varnish:
|
|
\begin{verbatim}
|
|
# varnishadm -T localhost:6082 -S /etc/varnish/secret purge.url ".*"
|
|
\end{verbatim}
|
|
|
|
On peut également vérifier l'état du démon, via la commande \texttt{status}:
|
|
\begin{verbatim}
|
|
# varnishadm -T localhost:6082 -S /etc/varnish/secret status
|
|
\end{verbatim}
|
|
|
|
Ainsi que d'autres opérations, comme l'arrêt et le redémarrage du processus, le
|
|
chargement de nouvelles règles VCL à chaud, etc\dots
|
|
|
|
|
|
\section{Gestion des logs}
|
|
|
|
Varnish permet de loguer de nombreuses informations, notamment très utiles pour
|
|
analyser sont fonctionnement.
|
|
|
|
Par défaut, Varnish n'écrit pas ses logs dans un fichier, mais dans un segment
|
|
mémoire, ce qui permet d'augmenter grandement les performances. Quand l'espace
|
|
est plein, Varnish réécrit par dessus en repartant de l'origine, ce qui fait
|
|
que la mémoire allouée pour les logs n'augmente pas.
|
|
|
|
Plusieurs outils permet ensuite de récupérer les lignes de log en mémoire, et
|
|
de les exploiter, soit pour les visualiser en direct, soit pour les écrire dans
|
|
un fichier de log.
|
|
|
|
L'outil \texttt{varnishtop} permet d'afficher les logs en temps réel:
|
|
\begin{verbatim}
|
|
# varnishtop
|
|
\end{verbatim}
|
|
|
|
\texttt{varnishlog} et \texttt{varnishncsa} permettent également de lire le
|
|
segment mémoire dans lequel varnish écrit ces logs, mais ils peuvent également
|
|
écrire ce contenu dans des fichiers, afin d'en conserver une trace. Ils peuvent
|
|
donc être utilisé manuellement, mais des scripts d'init sont fourni pour
|
|
permettre de les lancer au démarrage en mode démon.\\
|
|
\texttt{varnishncsa}, comme son nom l'indique, permet d'avoir les logs d'accès
|
|
HTTP au format NCSA (comme ceux générés par les serveurs web tel que Apache).
|
|
|
|
Pour les activer, il faut éditer respectivement les fichiers de configuration
|
|
\texttt{/etc/default/varnishlog} et \texttt{/etc/default/varnishncsa}:
|
|
\begin{verbatim}
|
|
VARNISHLOG_ENABLED=1
|
|
\end{verbatim}
|
|
\begin{verbatim}
|
|
VARNISHNCSA_ENABLED=1
|
|
\end{verbatim}
|
|
|
|
Puis lancer les démons:
|
|
\begin{verbatim}
|
|
# /etc/init.d/varnishlog start
|
|
# /etc/init.d/varnishncsa start
|
|
\end{verbatim}
|