Pourquoi ?

Ce n’est un mystère pour personne, NBS-System migre progressivement ses Reverse Proxies (RPs) sous nginx, le bloc de beton Russe monté sur missile V2. Il est également de notoriété publique que nos systèmes fonctionnent, pour leur écrasante majorité, sous Debian GNU/Linux. Seulement voila, si Debian est belle et stable, Debian met aussi plusieurs millénaires avant d’intégrer des nouveautés dans ses dépôts, et le moins que l’on puisse dire, c’est que dans les dernières versions stables de nginx, des nouveautés, il y en a pour tout le monde.

L’une d’entre elles prend tout son sens lorsque le client -qui rappelons le a toujours raison- demande à corps et à cris que nos RPs obeissent à tout prix aux messages « cache-control » et forcent le rechargement d’éléments particuliers dans le cache à l’aide de la fameuse combinaison « Contrôle-haiffe-cinq » aussi connue sous le nom de « Shift-rilôd ». Mais voila, même les versions 0.7 disponibles dans les backport Debian ne supportent que partiellement ce mécanisme, et notre malheureux client se voit obligé d’attendre d’interminables minutes avant de voir sa nouvelle CSS verte / bleue / orange enfin prise en compte (ne cherchez pas, cet exemple est le fruit de mon imagination).

Pourtant, on peut lire dans le changelog de la version 0.8.46 de nginx la ligne suivante :

Feature: the "proxy_cache_bypass", "fastcgi_cache_bypass",
"uwsgi_cache_bypass", and "scgi_cache_bypass" directives.

Bien que peu explicite, la feature proxy_cache_bypass cache le fonctionnement recherché: l’interrogation directe du serveur HTTP, et la mise en cache de l’élément fraîchement récupéré.

Comment ?

Chez NBS-System, on aime les choses propres. C’est un choix de vie. Aussi, il était parfaitement inimaginable de déposer des binaires compilés sans qu’ils soient soumis aux strict jeu des dépendances; de fait, il était impératif de se fendre d’un package en bonne et due forme, que nous pourrons par la même occasion agrémenter de quelques modules tiers du meilleur effet.

Action.

Afin de reproduire aisemment la création d’un paquet bleeding edge, nous nous fendrons d’un petit script shell.

Précisons en premier lieu quelques variables que nous utiliserons tout au long de notre script :

#!/bin/sh

VERSION="0.8.53"
DEBFULLNAME="foobar"
DEBEMAIL="Mr Foo foo@bar.com"

export VERSION DEBFULLNAME DEBEMAIL

Nous installons ensuite les paquets nécessaires à la construction :

sudo apt-get -t stable install build-essential devscripts fakeroot debhelper autotools-dev libpcre3-dev libssl-dev zlib1g-dev

Puis nous créons un repertoire temporaire afin d’y faire notre petite tambouille :

cd src
wget http://sysoev.ru/nginx/nginx-${VERSION}.tar.gz

apt-get -t lenny-backports source nginx
sudo apt-get -t lenny-backports build-dep nginx
cd $(ls -d nginx*/)

# Mise a jour du paquet source avec la version souhaitee
uupdate --upstream-version ${VERSION} ../nginx-${VERSION}.tar.gz

# Nous nous rendons dans notre nouveau source-tree
cd ../"$(dpkg-parsechangelog | sed -n 's/^Source: //p')-${VERSION}"

# Et remplacons honteusement les informations de maintenance
sed -i '/^Maintainer/s/: .*/: "${DEBEMAIL}"/;/^Uploaders/s/: .*/: "${DEBEMAIL}"/;/XSBC-Original-Maintainer/d' debian/control;

Certains patchs sont inutiles voire inapplicables dans notre version récente de nginx, nous les ignorerons :

sed -i '/nginx-upstream-fair.diff/d' debian/patches/series
rm debian/patches/nginx-upstream-fair.diff
sed -i '/fix_reloading_ipv6.diff/d' debian/patches/series
rm debian/patches/fix_reloading_ipv6.diff

Nous profiterons de la mise à jour pour inclure deux modules tiers lors de la construction du paquet: ngx_http_upstream_fair_module et ngx_cache_purge :

mkdir modules && cd modules
git clone http://github.com/gnosek/nginx-upstream-fair.git
git clone http://github.com/FRiCKLE/ngx_cache_purge.git
cd ..

Puis nous mettons à jour le fichier debian/rules avec nos préférences :

sed -i '/with-debug/,/nginx-upstream-fair/ c\
	\t--with-http_ssl_module \\\
	\t--without-mail_pop3_module \\\
	\t--without-mail_smtp_module \\\
	\t--without-mail_imap_module \\\
	\t--without-http_uwsgi_module \\\
	\t--without-http_scgi_module \\\
	\t--with-ipv6 \\\
	\t--add-module=$(CURDIR)/modules/nginx-upstream-fair \\\
	\t--add-module=$(CURDIR)/modules/ngx_cache_purge \\
	' debian/rules

Enfin, nous mettons à jour les informations sur notre futur paquet :

dch -i

Et lançons sa génération

debuild -i -us -uc

Un dpkg -i plus loin, nous constatons, non sans une certaine satisfaction :

ii  nginx                               0.8.53-2~nbs               small, but very powerful and efficient web server and mail proxy

C’est prêt.

J’peux avoir un autographe ?

Muni de notre nginx-on-steroids, nous pouvons désormais ajouter à notre configuration plusieurs nouveaux super-pouvoirs. En premier lieu, l’un des objectifs principaux de l’opération :

       location / {
                proxy_intercept_errors on;
                error_page      500 502 503 504 /504.html;
                proxy_pass      http://172.16.11.2/;
                proxy_cache     rp-test;
                proxy_cache_key "${scheme}://$host$request_uri";
                proxy_cache_bypass $http_pragma;
        }

Redémarrage de nginx et vérification de la requète :
Nous demandons bien un objet frais. Un rm -rf du cache nginx plus loin, nous chargeons une page simple. Voici le résultat de la commande stat(1) sur le fichier de cache associé :

Access: (0600/-rw-------)  Uid: (   33/www-data)   Gid: (   33/www-data)
Access: 2010-10-19 10:29:49.000000000 +0200
Modify: 2010-10-19 10:28:46.000000000 +0200
Change: 2010-10-19 10:28:46.000000000 +0200

Notre cache est configuré, pour ce test, à 10 minutes de rétention :

proxy_cache_valid 200 302  10m;

Cependant, après une requete de type Cache-Control: no-cache, nous obtenons le résultat suivant :

Access: (0600/-rw-------)  Uid: (   33/www-data)   Gid: (   33/www-data)
Access: 2010-10-19 10:30:44.000000000 +0200
Modify: 2010-10-19 10:30:44.000000000 +0200
Change: 2010-10-19 10:30:44.000000000 +0200

Soit exactement le comportement escompté. [insérer un rire diabolique ici].

Pour finir, nous testons notre nouveau module de purge de cache. Voici la configuration associée :

        location ~ /purge(/.*) {
                allow                   127.0.0.1;
                deny                    all;
                proxy_cache_purge       rp-test "$scheme://$host$1";
        }

Afin de constater le bon fonctionnement de cette nouvelle entrée, rendez-vous sur une page quelconque du site caché, appelons la http://foo.com/test.php, puis sur l’URL suivante: http://foo.com/purge/test.php; vous devriez alors vous emerveiller devant une page de ce type :

 

 

Successful purge

Key : http://foo.com/test.php
Path: /path/to/test/b/18/a2c66c9c4be6dbc805b0c00e06fa718b


nginx/0.8.53

 

Notre nginx réagit désormais tel que nous le souhaitions, toujours avec la même vélocité. C’est l’heure des torture-tests !

Liens et remerciements