Varnish 3 propose une solution pour streamer du contenu : il permet de délivrer à un client un flux (audio, vidéo…) tout en le cachant pour les prochains utilisateurs. Malheureusement dans sa version actuelle, il ne permet de streamer du contenu qu’à un seul client . Les autres clients sont placés en file d’attente. Martin Blix Grydeland a développé une nouvelle implémentation qui sera disponible dans la prochaine release majeure. En attendant, il existe une version de Varnish patchée : Varnish 3.0.2s. Cet article est donc basé sur cette version.

Plus d’infos sur le blog de Varnish.

Un cas d’usage possible

J’ai utilisé le streaming dans un autre contexte que celui de l’audio/vidéo : je dispose d’un proxy qui permet à mes serveurs d’accéder à certaines ressources sur Internet. Malheureusement, ce proxy ne cache pas (ou mal) et il n’est pas possible de le remplacer. Pour mettre à jour mes différentes distributions, je voulais pouvoir cacher tous les paquets (les .deb, les .rpm entre autres). Je vais détailler comment le faire simplement avec Varnish et pourquoi le streaming me rend bien service ! Comme d’habitude, cette installation se passe sur une Debian Squeeze fraichement installée.

Installation de Varnish 3.0.2s

Il existe quelques paquets deb et rpm sur ce repo. Si vous ne trouvez pas votre bonheur, il reste la compilation :

  • Installez les prérequis :
apt-get install build-essential automake libtool pkg-config libpcre3-dev python-docutils
wget http://repo.varnish-cache.org/source/varnish-3.0.2-streaming.tar.gz
  • On configure et on compile :
cd varnish-cache-streaming
sh autogen.sh
./configure --prefix=/usr/local --localstatedir=/var
make
make install

Varnish est installé et configuré. Il se lance via /etc/init.d/varnish start. Rien ne diffère au niveau configuration, utilisation du Varnish “classique”. Les configurations, outils présentés dans mes articles précédents sont toujours valables.

Une configuration de test

Voici la configuration utilisée pour mes tests. Elle est volontairement épurée, le but étant de présenter la fonction streaming. J’ai ici un seul backend : mon proxy. Pour un certain nombre d’extension, je vais mettre en cache pour 3 jours. Pour les requêtes concernant, par exemple, la liste des paquets Debian, j’interroge systématiquement le backend (je m’assure ainsi de fournir toujours une liste à jour). On a quelque chose qui ressemble à :

# Le proxy qui cache pas
backend default {
    .host = "x.x.x.x";
    .port = "8080";
}

# Ne pas cacher ces fichiers, toujours interroger le backend
sub vcl_recv {
  if (req.url ~ "(Packages\.gz|Packages\.bz2|Release|Translation-en\.bz2|\.db.*)") {
        return(pass);
  }
}

# On change le TTL des extensions deb|rpm|xz|gz|bz2|cvd
sub vcl_fetch {
    if (req.url ~ "\.(deb|rpm|xz|gz|bz2|cvd)" && req.url !~ "(Packages\.gz|Packages\.bz2|Release|Translation-en\.bz2|\.db.*)") {
        set beresp.ttl = 3d;
    }
}

Cette configuration semble fonctionner correctement… Sauf dans le cas de gros packages qui prennent du temps à être téléchargés sur le backend par Varnish : le système de packaging, pacman dans cet exemple, n’apprécie pas :

Erreur : échec de récupération du fichier 'gcc-4.6.2-7-x86_64.pkg.tar.xz' depuis mir1.archlinux.fr : Operation too slow. Less than 1024 bytes/sec transferred the last 10 seconds
Avertissement : échec de récupération de certains fichiers depuis core

C’est vrai pour un apt-get install ou un yum install. Lorsqu’un client demande pour la première fois un paquet, Varnish doit le télécharger et le cacher avant de le délivrer et ce temps-là peut être problématique pour l’utilitaire. Même impact avec un simple wget :

wget http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.5.tar.bz2
--2012-02-08 23:26:28--  http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.5.tar.bz2
Résolution de varnish... x.x.x.X
Connexion vers varnish|x.x.x.x|:80...connecté.
requête Proxy transmise, en attente de la réponse...ERREUR de lecture (Connexion terminée par expiration du délai d'attente) de l'en-tête.
Nouvel essai.

La solution du problème

La solution du problème est donc l’activation du streaming. Varnish est ensuite capable de streamer à plusieurs clients un objet. Quand l’objet est totalement téléchargé, il sera livré directement du cache aux clients suivants. Il suffit simplement de rajouter set beresp.do_stream = true; dans la boucle vcl_fetch de l’exemple précédent :

sub vcl_fetch {
    if (req.url ~ "\.(deb|rpm|xz|gz|bz2|cvd)" && req.url !~ "(Packages\.gz|Packages\.bz2|Release|Translation-en\.bz2|\.db.*)") {
        set beresp.do_stream = true;
        set beresp.ttl = 3d;
    }
}

Après un /etc/init.d/varnish reload, les tests précédents fonctionnent :

wget  http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.5.tar.bz2
--2012-02-08 23:35:30--  http://www.kernel.org/pub/linux/kernel/v3.0/linux-3.2.5.tar.bz2
Résolution de varnish.. x.x.x.x
Connexion vers varnish x.x.x.x|:80...connecté.
requête Proxy transmise, en attente de la réponse...200 OK
Longueur: 78124665 (75M) [application/x-bzip2]
Sauvegarde en : «linux-3.2.5.tar.bz2.8»

14% [================>                             ] 11 595 205  1,1M/s

Ou encore depuis une archlinux :

:: Récupération des paquets du dépôt extra...
 apache-2.2.22-2-x86_64          883,3 KiB   1,4M/s 00:00 [------------] 100%
 art-sharp-2.24.2-2-x86_64        16,6 KiB   117K/s 00:00 [------------] 100%
 libxml2-2.7.8-2-x86_64          531,5 KiB   188K/s 00:04 [--C o    o  ]  36%

A retenir : pour activer le streaming avec Varnish, il suffit de rajouter l’instruction set beresp.do_stream = true; dans la boucle vcl_fetch de votre configuration. La version actuelle de Varnish ne permet qu’un seul stream à la fois. Il faut utiliser la version “s”, ici 3.0.2s, pour bénéficier de la nouvelle implémentation du streaming… En attendant la prochaine release majeure.