Reverse-proxy & cache

Luc Didry, à partir des cours de Sébastien Jean. Licence CC-BY-SA
What's wrong with that?
Le dixième docteur

Télécharger le PDF

1 Définitions

  • reverse-proxy : relaye les requêtes sur un ou plusieurs chemins vers un ou plusieurs autres serveurs définis, le plus souvent internes ;
  • forward-proxy ou serveur proxy : sert d’intermédiaire entre deux hôtes (le plus souvent, entre des hôtes et Internet) pour faciliter (cache par ex.) ou surveiller (logs, proxy filtrant) les échanges ;
  • cache : mécanisme permettant de "sauvegarder" les résultats de requêtes pour, si le cache n’est pas trop ancien, les servir plus rapidement, sans relayer la requête à nouveau.

Si Squid est un des proxy-caches les plus connus et les plus utilisés en forward-proxy, Apache peut être utilisé à l’identique sur des configurations qui restent "simples" mais très fonctionnelles.

2 Reverse-proxy

2.1 Reverse-proxy avec Apache

L’utilisation la plus courante de mod_proxy se fait sur un serveur frontal1 en répartissant les charges dynamiques et statiques sur des "fermes2". Typiquement, on redirige /images/ et /media/ sur une ferme statique avec peut-être un autre serveur Web plus approprié (nginx par exemple).

Apache joue ainsi le rôle de proxy et distribue la charge sur plusieurs serveurs, l’appellation courante est reverse-proxy.

On peut coupler le rôle de proxy avec un mécanisme de cache afin d’améliorer les performances en réduisant le nombre d’appels aux serveurs sous-jacents.

Vous noterez dans la documentation du module3 que sont évoqués plusieurs sous-modules du module de cache, correspondant au différents fournisseurs de stockage pour le cache4 (disque5, à base de cache d’objets partagés6) ainsi que des sous-modules pour les différents protocoles pouvant potentiellement profiter d’un système de cache (ftp7, http8, etc.).

La commande ProxyPass permet d’associer un chemin (ou Location) à un (groupe de) tiers, exemple :

ProxyRequests Off
ProxyPass / http://10.0.0.1/
ProxyPass /images/ http://10.0.0.10/
ProxyPass /medias/ http://10.0.0.20/

La directive ProxyRequests sert à activer les fonctionnalités de forward-proxy d’Apache, ce dont nous n’avons pas besoin ici, ce qui explique sa désactivation en lui passant le paramètre Off;

Les requêtes pour l’URL / seront relayées au serveur possédant l’IP 10.0.0.1, etc.

Ici, en utilisant le sous-module mod_proxy_balancer9, on distribue sur deux fermes de deux machines les parties statiques :

ProxyRequests Off
ProxyPass / http://10.0.0.1/
<Proxy balancer://clusterImages>
    BalancerMember http://10.0.0.10:80
    BalancerMember http://10.0.0.11:80
</Proxy>
<Proxy balancer://clusterMedias>
    BalancerMember http://10.0.0.20:80
    BalancerMember http://10.0.0.21:80
</Proxy>
ProxyPass /images/ balancer://clusterImages/
ProxyPass /medias/ balancer://clusterMedias/

Pour une gestion distante du sous-module mod_proxy_balancer on peut rajouter (attention à la sécurité10 !) :

<Location /balancer-manager>
    SetHandler balancer-manager
    Require ip 10.0.0.1
</Location>

2.2 Reverse-proxy avec Nginx

On se servira ici de la directive proxy_pass.

Dans la configuration de votre virtualhost :

location / {
    include proxy_params;
    proxy_pass http://127.0.0.1;
}

Pour distribuer la charge sur plusieurs machines, on utilise la directive upstream en dehors de la configuration du virtualhost :

upstream loadbalancing {
    server 10.0.0.1;
    server 10.0.0.2;
}

Et on utilisera l’upstream ainsi défini dans le virtualhost :

location / {
    include proxy_params;
    proxy_pass http://loadbalancing;
}

3 Cache

3.1 Apache

On l’a vu plus haut, il existe différents sous-modules de cache. Il vous faudra en activer un pour bénéficier des mécanismes de cache d’Apache.

Prenons l’exemple de mod_cache_disk :

a2enmod cache_disk
systemctl restart apache2
systemctl start apache-htcacheclean

Dans votre virtualhost :

CacheQuickHandler off
<Location />
    CacheEnable disk
</Location>

La directive CacheQuickHandler est généralement à positionner à off. En effet, si elle est à on, sa valeur par défaut, les directives de restrictions d’accès (Require) ne seront pas appliquées.

On peut aussi spécifier l’URL pour laquelle il faut activer le cache directement en argument de la directive CacheEnable :

CacheEnable disk /foo/

3.2 Nginx

NB : Nginx ne peut cacher que sur disque, pas en mémoire. On peut néanmoins tricher en le faisant cacher sur un montage tmpfs11 ou en mamaillant avec memcached.

Définissez un espace de cache en dehors de votre virtualhost :

proxy_cache_path /var/cache/nginx keys_zone=my_zone:10m inactive=7d max_size=700m;

De nombreux arguments12 sont utilisables avec cette directive mais nous n’en verrons que quelques-uns (la documentation du module vous tend les bras).

Notons d’abord les indispensables :

  • /var/cache/nginx : il s’agit du chemin du répertoire de cache (donc bien évidemment modifiable) ;
  • keys_zone=name:size : le nom de la zone de cache. Nginx permet donc de définir différents caches, réutilisables à loisir dans différents endroits de la configuration. La taille de cet argument n’est pas la taille du cache, mais la taille de la mémoire allouée pour conserver les métadonnées des données cachées.

Les arguments optionnels :

  • inactive=time : les données cachées non consultées durant ce délai seront supprimées du cache ;
  • max_size=size : il s’agit de la taille maximale du dossier de cache. Lorsque cette taille est dépassée, les données les plus anciennes sont supprimées pour faire de la place aux nouvelles.

On utilisera ensuite cette zone de cache ainsi dans un virtualhost (ou juste dans le location, voire en dehors du virtualhost) :

proxy_cache my_zone;
location / {
    include proxy_params;
    proxy_pass http://loadbalancing;
}

3.3 En-têtes

Il n’y a pas que le cache côté serveur qui améliore les performances, il y a aussi le cache côté client !

Si certaines applications se chargent de renvoyer toutes seules des en-têtes contenant des instructions pour le cache client, il est parfois necéssaire de les mettre soi-même.

Avec Apache, on pourra utiliser indifféremment le module mod_expires13 ou le module mod_headers14 :

<Directory /var/www/site/css/>
    ExpiresActive On
    ExpiresDefault "access plus 1 month"
</Directory>

<Directory /var/www/site/js>
    Header add Cache-Control "public, max-age=2678400"
</Directory>

Avec Nginx, c’est semblable, on utilisera la directive expires ou add_header :

location /css {
    expires 1M;
}
location /js {
    add_header Cache-Control "public, max-age=2678400";
}

4 Forward-proxy

Vous allez créer un forward-proxy HTTP localement sur votre machine avec Apache. Celui-ci agira comme un proxy d’entreprise, c’est à dire qu’il ne servira pas comme dans la configuration ci-dessus en passant les requêtes reçues par un frontal vers des fermes, mais il ira récupérer le résultat de vos requêtes pour vous le passer. Voici un exemple basique de configuration locale :

ProxyRequests On
<Proxy *>
    Require ip 127.0.0.0/8
</Proxy>

ProxyRequests sert à accepter les requêtes en tant que proxy. À moins de vouloir que votre serveur Apache serve de proxy à la terre entière pour aller voir des sites plus ou moins fréquentables ou participer à des attaques, pensez à bien limiter les accès15.

Définissez un hôte virtuel par IP, écoutant sur localhost sur le port 8080 (ou autre si non disponible) et fournissant un service de forward-proxy. Configurez votre navigateur pour l’utiliser et testez la connexion. Avec vos VMs Vagrant, le plus simple est encore de faire, de l’intérieur de la VM :

export http_proxy=http://localhost:8080/

Et de lancer w3m pour naviguer sur le web.

5 Varnish

Varnish est un reverse-proxy cachant dont les performances sont impressionnantes16 et dont la mise en œuvre est fort simple pour une utilisation basique.

Installez Varnish et configurez-le pour qu’il utilise votre serveur Apache (voir dans le fichier /etc/varnish/default.vcl)

Dans la page servie par votre serveur sur http://localhost/, ajoutez un grand nombre de paragraphes de Lorem Ipsum17 de façon à ce que la page soit relativement lourde (les images ne comptent pas, je souhaite que la page elle-même soit lourde). 500 paragraphes de Lorem Ipsum seront suffisants.

Lancez les commandes suivantes :

siege -c 300 --time=20s http://localhost/
siege -c 300 --time=20s http://localhost:6081/

Vous constaterez aisément que Varnish (qui écoute par défaut sur le port 6081) améliore grandement les performances tout en réduisant l’utilisation des ressources de votre machine.

Pro tips :

NB : Varnish cache par défaut en mémoire, mais il peut aussi cacher sur disque.

6 Miscellanées

  • Ce n’est parce que Nginx est plus véloce dans notre cas, même sans cache, qu’il faut l’employer partout. Le fait de pouvoir étendre les capacités d’Apache de façon simple et dynamique grâce aux modules en font un allié précieux dans bien des cas. « Tout ressemble à un clou pour qui ne possède qu’un marteau ». Vous avez plusieurs outils à votre disposition, faites-en bon usage pour la bonne raison.

    Un montage pertinent et une configuration idoine vous permettront d’encaisser des charges bien plus importantes, voire carrément impressionnantes18 !

  • Varnish déchire pour les performances, c’est un fait. Mais il ne peut pas traiter de requêtes sécurisées (HTTPS). Et ça n’est qu’un reverse-proxy. Il ne fera pas de php, de CGI, etc.
  • Le load balancing ou l’envoi de requêtes sur un serveur plus performant n’est pas la seule raison d’utilisation d’un proxy. Certaines applications, comme celles écrites avec le framework Perl Mojolicious19 comme Lstu20 ou Lutim21 sont servies par un serveur web dédié à ce genre d’application. La façon la plus simple de les utiliser est de les proxyfier.
  • Un proxy n’est pas forcément destiné à servir de serveur frontal. Ainsi, l’application Lutim, sur son instance officielle https://lut.im était, avant le développement d’un système de cache interne, servie de la façon suivante :
    Nginx -> Varnish -> Hypnotoad (le serveur web dédié qui fournit Lutim)
    Ceci afin de mettre en cache en mémoire les images renvoyées par Lutim, mais certaines URLs sont fournies directement par Nginx (css, js, etc).
  • Certaines pages sont tellement dynamiques qu’il ne sert à rien de vouloir les mettre en cache (cours de la bourse, etc.).
  • Nginx est un très bon load balancer
  • Attention aux logs ! Le serveur de la ferme loguera l’IP du frontal si vous n’adaptez pas la configuration. Différents modules ou techniques sont à votre disposition pour régler ce problème :
    • mod_remoteip22 pour Apache 2.4 et mod_rpaf pour Apache 2.223 ;
    • l’utilisation de l’en-tête X-FORWARDED-FOR ;
    • le module ngx_http_realip_module pour Nginx24.

7 Exercices

7.1 Nginx

On considérera que Nginx écoute sur le port 81 et Apache sur le port 80.

Dans /etc/nginx/nginx.conf, ajoutez :

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache:10m inactive=7d max_size=700m;

Dans /etc/nginx/sites-enabled/default :

location / {
    include proxy_params;
    proxy_pass http://127.0.0.1/;
    proxy_cache cache;
    proxy_cache_valid 12h;
    expires 12h;
    proxy_cache_use_stale error timeout invalid_header updating;
}

Bien évidemment, si vous faites écouter apache sur un autre port que le 80, ajoutez le port à l’adresse de la directive proxy_pass.

Lancez les commandes suivantes :

siege -c 300 --time=20s http://localhost/
siege -c 300 --time=20s http://localhost:81/

Comparez les performances.

Supprimez la configuration ajoutée ayant trait au cache (tout ce qui commence par proxy_cache et expires) et redémarrez Nginx.

Recommencez. Que constatez-vous ?

Remettez /etc/nginx/sites-enabled/default dans son état de départ, sauf le port 81 :

location / {
    try_files $uri $uri/ =404;
}

Relancez Nginx.

Relancez le benchmark

Que constatez-vous, que ce soit pour les performances pures (hits, nombre de transactions par secondes, etc.) ou sur l’impact sur la charge de votre machine ?

7.2 Apache

Définissez un hôte virtuel par IP, écoutant sur localhost sur le port 82 (ou autre si non disponible).

Choisissez un module de cache (disque ou mémoire) et activez-le25.

Refaites le benchmark de http://localhost (utilisez les valeurs de l’Apache non configuré comme cache ou comme proxy des exercices précédents pour comparer). Que constatez-vous ?


  1.  sous-entendu dans une architecture n-tiers

  2.  groupes de serveurs applicatifs fournissant le même service de manière équivalente et distribuable

  3.  http://httpd.apache.org/docs/2.4/en/mod/mod_proxy.html

  4.  http://httpd.apache.org/docs/2.4/en/mod/mod_cache.html

  5.  http://httpd.apache.org/docs/2.4/mod/mod_cache_disk.html

  6.  http://httpd.apache.org/docs/2.4/mod/mod_cache_socache.html

  7.  http://httpd.apache.org/docs/2.4/en/mod/mod_proxy_ftp.html

  8.  http://httpd.apache.org/docs/2.4/en/mod/mod_proxy_http.html

  9.  http://httpd.apache.org/docs/2.4/fr/mod/mod_proxy_balancer.html

  10.  ne laissez pas tout le monde accéder à quelque chose qui peut modifier le comportement de votre serveur

  11.  https://fr.wikipedia.org/wiki/Tmpfs

  12.  http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path

  13.  http://httpd.apache.org/docs/current/mod/mod_expires.html

  14.  http://httpd.apache.org/docs/current/mod/mod_headers.html

  15.  oui, je sais que dans la salle, il y a peu de risques, mais soyez sûrs qu’en entreprise, ça peut faire mal

  16.  true story

  17.  vous pouvez générer des paragraphes de Lorem Ipsum sur http://slipsum.com/

  18.  il ne faut pas non plus négliger les possibilités de caching des applications servies. Un bon plugin sur Wordpress peut tout changer

  19.  http://mojolicio.us

  20.  raccourcisseur d’URLs, https://lstu.fr

  21.  hébergement d’images, https://lut.im

  22.  https://httpd.apache.org/docs/2.4/fr/mod/mod_remoteip.html

  23.  https://github.com/gnif/mod_rpaf

  24.  http://nginx.org/en/docs/http/ngx_http_realip_module.html

  25.  et on lit la doc sur http://httpd.apache.org/docs/2.4/en/mod/mod_cache.html siouplaît


https://asrall.fr