evoformations/support/varnish.tex

514 lines
18 KiB
TeX
Raw Normal View History

% 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<EFBFBD>sentation}
Varnish est un reverse proxy HTTP dans le but premier est la mise en cache de
contenu. Il est <20>galement capable de g<>rer plusieurs backend, avec
r<EFBFBD>partition de charge et d<>tection de panne.
Varnish, d<>velopp<70> en C, se concentre principalement sur la performance sur des infrastructures
<EFBFBD> haut et tr<74>s haut trafic.
Un autre point fort est son langage de configuration, qui permet de param<61>trer
finement le comportement de Varnish aux diff<66>rentes <20>tapes du traitement de la
requ<EFBFBD>te.
Le d<>veloppement de Varnish a commenc<6E> en 2005, et il est distribu<62> sous licence
BSD.
\section{Installation}
Varnish est disponible dans les d<>p<EFBFBD>ts de Debian Squeeze en version 2.1.3. Il
existe <20>galement un backport du paquet de Wheezy, qui fournit la version 3.0.2.
Cette version apporte de nombreuses am<61>lioration et fonctionnalit<69> dans la
gestion du load-balancing entre les backends.
Installation du paquet:
\begin{verbatim}
# aptitude install varnish
\end{verbatim}
\section{Configuration}
Les possibilit<69>s offertes pour la configuration de Varnish sont assez vastes,
elles seront abord<72>s par grands th<74>mes.
\subsection{Param<EFBFBD>trage de base}
Tout d'abord, il est n<>cessaire de renseigner quelques informations de base au
d<EFBFBD>mon \texttt{varnishd}. Cette configuration se passe dans le fichier
\texttt{/etc/default/varnish}. Plusieurs cas de figure sont propos<6F>s <20> titre
d'exemple dans ce fichier, en voici un autre avec quelques optimisations
suppl<EFBFBD>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<61>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<71>tes HTTP <20>
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<69> plus loin dans ce chapitre).
\item[\texttt{-f /etc/varnish/default.vcl}] \hfill \\
Cette option indique le fichier de configuration <20> 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<63> en m<>moire 3~Go, ainsi que
dans un fichier plat sur le disque, qui sera limit<69> <20> 10~Go.
\item[\texttt{-p thread\_pools=<Number of CPU cores>}]
2012-05-22 03:42:54 +02:00
\item[\texttt{-p thread\_pool\_add\_delay=2}]
\item[\texttt{-p thread\_pool\_max=5000}] \hfill \\
L'option \texttt{-p} permet de modifier diff<66>rents param<61>tres d'ex<65>cution.
De nombreux param<61>tres peuvent <20>tre modifi<66>s, la liste compl<70>te avec leur
description se trouve ici:
\url{https://www.varnish-cache.org/docs/2.1/reference/varnishd.html}.
2012-05-22 03:42:54 +02:00
\texttt{thread\_pools} indique le nombre de groupe de threads <20> lancer. Cette
valeur ne devrait pas d<>passer le nombre de c\oe{}ur disponible sur le syst<73>me
(pour des raisons de performance). Pour \texttt{threa\_poo\_ad\_delay}, il
s'agit du temps en milisecondes <20> attendre avant la cr<63>ation d'un nouveau
thread. Et enfin \texttt{threa\_poo\_max} repr<70>sente le nombre total de
thread maximum <20> ne pas d<>passer, tout pool confondus.
\item[\texttt{umask 022}] \hfill \\
Varnish s'attend <20> avoir un umask <20> 022 pour s'ex<65>cuter
correctement. \'Etant donn<6E> qu'il n'est pas forc<72> dans le script d'init,
nous le pla<6C>ons ici manuellement.
\end{description}
\subsection{Aper<EFBFBD>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<EFBFBD>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<65> AVANT le d<>but de la requ<71>te au backend.
On peut donc choisir vers quel backend renvoyer la requ<71>te. On peut aussi
de modifier la requ<71>te (modifier des ent<6E>tes HTTP, supprimer des demandes
de cookies, etc\dots). Seul les actions \texttt{set req.} sont possibles.
\item \texttt{vcl\_fetch} est appel<65> APR<50>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<6E>te HTTP Host:
if (req.http.host ~ "^regex$")
# Pr<50>sence d'un cookie
if (req.http.cookie) {
# Condition sur l'URL demand<6E>e
if (req.url ~ "^/regex$")
# Si le backend est accessible
if (req.backend.healthy)
# Pr<50>sence ent<6E>te Accept-Encoding
if (req.http.Accept-Encoding)
# Condition sur la requ<71>te faite
if (req.request != "GET" && req.request != "HEAD")
# Pr<50>sence de l'ent<6E>te X-Forwarded-For
if (req.http.x-forwarded-for)
# Condition sur les ent<6E>tes envoy<6F>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<71>te
unset req.http.cookie;
remove req.http.cookie;
# Supprimer un certain nombre d'ent<6E>tes HTTP
remove req.http.X-Forwarded-For;
remove req.http.Accept-Encoding;
# Positionner un certain nombre d'ent<6E>tes HTTP pour la requ<71>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<6E>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<65>marre la demande au backend (et incr<63>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<6E>tes HTTP de contr<74>le du cache. Par d<>faut,
celle ci sont lues et pris en compte, mais on peut red<65>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 <20> 0 (dans \texttt{vcl\_deliver}.
Pour que le changement de TTL le soit <20>galement cot<6F> client, on r<><72>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 <20>tre int<6E>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 <20> 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<65>
\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<69> (dans l'exemple il sera
utilis<EFBFBD> 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
<EFBFBD>tre int<6E>ressant d'ajuster certains param<61>tres:
\begin{itemize}
\item Dans l'exemple ci dessus, le director est en mode round-robin. Le trafic
est alors r<>parti <20>quitablement entre les backend. On peut d<>finir un
<20>poids<64> 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<6F>es sur un backend. En en
positionnant une sur chacun des backends, Varnish saura qu'il devra ignorer
le backend satur<75> et en choisir un autre, afin de ne pas le surcharg<72>.
\begin{verbatim}
backend www00 {
.host = "192.0.2.8";
.port = "80";
.max_connections = 80;
}
\end{verbatim}
\item Il est possible <20>galement de r<>partir les requ<71>tes sur les backends
suivant des crit<69>res sur la requ<71>te. Le mode du director <20> 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<69>re utilis<69> est l'IP du client
(\texttt{client.identity = req.ip}. Les autres crit<69>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 <20>tait capable de g<>rer plusieurs backend en contr<74>lant
la r<>partition des requ<71>tes sur chacun d'eux. Il est aussi capable de d<>tecter
une panne sur un backend, et de prendre en compte cet <20>v<EFBFBD>nement pour modifier
son comportement: utiliser un autre backend, ou renvoyer ces fichiers en cache.
\subsubsection{D<EFBFBD>tection lors de la requ<71>te}
Il est possible d'ajuster diff<66>rent param<61>tres indiquant le temps d'attente
maximum tol<6F>r<EFBFBD> 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<73> ce d<>lai, Varnish ira interroger le
backend suivant.
\subsubsection{Surveillance p<>riodique des backends}
La m<>thode pr<70>c<EFBFBD>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<71>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<6D>chera pas Varnish de continuer <20> lui envoyer des requ<71>tes.
Varnish offre la possibilit<69> de surveiller r<>guli<6C>rement ses backends et les
marquer <20>ventuellement comme <20>down<77>. 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<71>te HTTP que Varnish devra envoyer au backend, ainsi que
diverses directives. Le backend devra obligatoirement retourn<72> un code HTTP 200,
il sera consid<69>r<EFBFBD> comme <20>down<77> 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 <20> avoir une r<>ponse en moins d'une seconde. Les directives
\texttt{.window} et \texttt{.threshold} permet de d<>finir un cycle d'hyst<73>r<EFBFBD>sis:
pour que le backend soit vu comme <20>tant <20>down<77>, il faut que, sur une quantit<69> de
8, 6 checks ont <20>chou<6F>. Et inversement, pour qu'il repasse <20>up<75>, il faut que 6
checks sur les 8 ont r<>ussi.
\subsubsection{Saint mode}
2012-05-22 17:35:03 +02:00
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<71>tes (sous r<>serve bien s<>r
qu'elle y soit). Il est donc possible d'<27>teindre temporairement son unique
backend, et que Varnish continu <20> 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<73>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
<EFBFBD>magiquement<EFBFBD> sans untervention manuelle.
\subsubsection{Backend de spare}
Il est <20>galement possible de d<>clarer un backend de spare, qui sera utilis<69>
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<6D>re partie de ce chapitre, un couple IP, port avait <20>t<EFBFBD> d<>fini pour
faire <20>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 <20>galement n<>cessaire. Par d<>faut
lors de l'installation du paquet Debian, le fichier \texttt{/etc/varnish/secret}
est cr<63><72> contenant la cl<63> permettant de s'authentifier. On peut se connecter en
utilisant \texttt{telnet}, mais la commande d<>di<64>e \texttt{varnishadm} est plus
adapt<EFBFBD>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<65>cuter les commandes
Varnish disponibles. Il est <20>galement possible de passer une commande
directement en argument de \texttt{varnishtop}.
Par exemple, pour pouvoir vider la totalit<69> du cache Varnish:
\begin{verbatim}
# varnishadm -T localhost:6082 -S /etc/varnish/secret purge.url ".*"
\end{verbatim}
On peut <20>galement v<>rifier l'<27>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<6F>rations, comme l'arr<72>t et le red<65>marrage du processus, le
chargement de nouvelles r<>gles VCL <20> chaud, etc\dots
\section{Gestion des logs}
2012-05-22 17:35:03 +02:00
Varnish permet de loguer de nombreuses informations, notamment tr<74>s utiles pour
analyser sont fonctionnement.
Par d<>faut, Varnish n'<27>crit pas ses logs dans un fichier, mais dans un segment
m<EFBFBD>moire, ce qui permet d'augmenter grandement les performances. Quand l'espace
est plein, Varnish r<><72>crit par dessus en repartant de l'origine, ce qui fait
que la m<>moire allou<6F>e pour les logs n'augmente pas.
Plusieurs outils permet ensuite de r<>cup<75>rer les lignes de log en m<>moire, et
de les exploiter, soit pour les visualiser en direct, soit pour les <20>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 <20>galement de lire le
segment m<>moire dans lequel varnish <20>crit ces logs, mais ils peuvent <20>galement
<EFBFBD>crire ce contenu dans des fichiers, afin d'en conserver une trace. Ils peuvent
donc <20>tre utilis<69> 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<63>s
HTTP au format NCSA (comme ceux g<>n<EFBFBD>r<EFBFBD>s par les serveurs web tel que Apache).
Pour les activer, il faut <20>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}