Booster votre blog WordPress avec Varnish
Date: 12/10/2010 | Catégories: Open-source,Planet-libre,Web | Tags: apache,planet-libre,varnish,wordpress
Varnish est un accélérateur de sites Web fonctionnant sur le principe d'un reverse proxy. Varnish va prendre en charge les requêtes HTTP venant de vos visiteurs et communiquer avec votre serveur Web en ne demandant la création des pages Web seulement quand cela est nécessaire.
C'est un projet open-source sous licence FreeBSD.
Installation
Sur un Ubuntu Server (ou une distribution Debian "like"), l'installation se résume à la commande suivante. Vous aurez ainsi la version standard maintenu par les dépôts:
sudo aptitude install varnish
Pour disposer d'une version plus récente et seulement sous Ubuntu, vous pouvez utiliser les lignes de commande suivantes:
curl http://repo.varnish-cache.org/debian/GPG-key.txt | sudo apt-key add -
sudo echo "deb http://repo.varnish-cache.org/ubuntu/ $(lsb_release -s -c) varnish-2.1" >> /etc/apt/sources.list
sudo aptitude update
sudo aptitude install varnish
Nous allons donc dans un premier temps tester Varnish sur la configuration initiale de notre serveur Web (qui comprend dans mon exemple deux sites virtuels sous Apache). Cette configuration de test permettra de tester les performances de Varnish sans aucun impact sur vos sites (les visiteurs continueront à passer directement par votre serveur Web Apache). Enfin, si les tests sont concluants, nous modifierons la configuration finale pour que vos visiteurs transitent de manière transparente par Varnish.
Configuration de test
Varnish appelle backend les serveurs Web qu'il doit accélérer. Il est possible de définir un ou plusieurs backends par serveur Varnish.
Ainsi, si l'on souhaite accélérer le site http://blog.nicolargo.com qui est hébérgé sur un serveur, il faut saisir la configuration suivante dans le fichier /etc/varnish/default.vcl (configuration librement inspiré de la documentation officielle sur comment optimiser son blog WordPress avec Varnish) avec une gestion du cache sur les requête POST, pas de cache pour la zone admin, gestion des cookies de Google Analytics.
Vous pouvez télécharger la configuration Varnish VCL complète pour WordPress ici.
# # Varnish 3 configuration for WordPress # # On Debian OS: /etc/varnish/default.vcl # # Nicolas Hennion (aka) Nicolargo # # Set the default backend (Nginx server for me) backend default { # My Nginx server listen on IP address 127.0.0.1 and TCP port 8080 .host = "127.0.0.1"; .port = "8080"; # Increase guru timeout # http://vincentfretin.ecreall.com/articles/varnish-guru-meditation-on-timeout .first_byte_timeout = 300s; } # Forbidden IP ACL acl forbidden { "41.194.61.2"/32; "192.54.144.229"/32; } # Purge ACL acl purge { # Only localhost can purge my cache "127.0.0.1"; "localhost"; } # This function is used when a request is send by a HTTP client (Browser) sub vcl_recv { # Block the forbidden IP addresse if (client.ip ~ forbidden) { error 403 "Forbidden"; } # Only cache the following sites #if ((req.http.host ~ "(blog.nicolargo.com)") || (req.http.host ~ "(blogtest.nicolargo.com)")) { if ((req.http.host ~ "(blog.nicolargo.com)")) { set req.backend = default; } else { return (pass); } # Compatibility with Apache format log if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } # Normalize the header, remove the port (in case you're testing this on various TCP ports) set req.http.Host = regsub(req.http.Host, ":[0-9]+", ""); # Allow purging from ACL if (req.request == "PURGE") { # If not allowed then a error 405 is returned if (!client.ip ~ purge) { error 405 "This IP is not allowed to send PURGE requests."; } # If allowed, do a cache_lookup -> vlc_hit() or vlc_miss() return (lookup); } # Post requests will not be cached if (req.request == "POST") { return (pass); } # --- WordPress specific configuration # Did not cache the RSS feed if (req.url ~ "/feed") { return (pass); } # Blitz hack if (req.url ~ "/mu-.*") { return (pass); } # Did not cache the admin and login pages if (req.url ~ "/wp-(login|admin)") { return (pass); } # Remove the "has_js" cookie set req.http.Cookie = regsuball(req.http.Cookie, "has_js=[^;]+(; )?", ""); # Remove any Google Analytics based cookies set req.http.Cookie = regsuball(req.http.Cookie, "__utm.=[^;]+(; )?", ""); # Remove the Quant Capital cookies (added by some plugin, all __qca) set req.http.Cookie = regsuball(req.http.Cookie, "__qc.=[^;]+(; )?", ""); # Remove the wp-settings-1 cookie set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-1=[^;]+(; )?", ""); # Remove the wp-settings-time-1 cookie set req.http.Cookie = regsuball(req.http.Cookie, "wp-settings-time-1=[^;]+(; )?", ""); # Remove the wp test cookie set req.http.Cookie = regsuball(req.http.Cookie, "wordpress_test_cookie=[^;]+(; )?", ""); # Are there cookies left with only spaces or that are empty? if (req.http.cookie ~ "^ *$") { unset req.http.cookie; } # Cache the following files extensions if (req.url ~ "\.(css|js|png|gif|jp(e)?g|swf|ico)") { unset req.http.cookie; } # Normalize Accept-Encoding header and compression # https://www.varnish-cache.org/docs/3.0/tutorial/vary.html if (req.http.Accept-Encoding) { # Do no compress compressed files... if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg)$") { remove req.http.Accept-Encoding; } elsif (req.http.Accept-Encoding ~ "gzip") { set req.http.Accept-Encoding = "gzip"; } elsif (req.http.Accept-Encoding ~ "deflate") { set req.http.Accept-Encoding = "deflate"; } else { remove req.http.Accept-Encoding; } } # Check the cookies for wordpress-specific items if (req.http.Cookie ~ "wordpress_" || req.http.Cookie ~ "comment_") { return (pass); } if (!req.http.cookie) { unset req.http.cookie; } # --- End of WordPress specific configuration # Did not cache HTTP authentication and HTTP Cookie if (req.http.Authorization || req.http.Cookie) { # Not cacheable by default return (pass); } # Cache all others requests return (lookup); } sub vcl_pipe { return (pipe); } sub vcl_pass { return (pass); } # The data on which the hashing will take place sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } # If the client supports compression, keep that in a different cache if (req.http.Accept-Encoding) { hash_data(req.http.Accept-Encoding); } return (hash); } sub vcl_hit { # Allow purges if (req.request == "PURGE") { purge; error 200 "Purged."; } return (deliver); } sub vcl_miss { # Allow purges if (req.request == "PURGE") { purge; error 200 "Purged."; } return (fetch); } # This function is used when a request is sent by our backend (Nginx server) sub vcl_fetch { # For static content strip all backend cookies if (req.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico") { unset beresp.http.cookie; } # A TTL of 30 minutes set beresp.ttl = 1800s; return (deliver); } # The routine when we deliver the HTTP request to the user # Last chance to modify headers that are sent to the client sub vcl_deliver { if (obj.hits > 0) { set resp.http.X-Cache = "cached"; } else { set resp.http.x-Cache = "uncached"; } # Remove some headers: PHP version unset resp.http.X-Powered-By; # Remove some headers: Apache version & OS unset resp.http.Server; return (deliver); } sub vcl_init { return (ok); } sub vcl_fini { return (ok); }
Le port d'écoute par défaut de Varnish est TCP/6081 (défini dans le fichier /etc/default/varnish).
Si vous avez un Firewall (iptables) sur votre serveur, il faut penser à ajouter la règle suivante:
iptables -A OUTPUT -p tcp --dport 6081 -j ACCEPT
On relance Varnish pour prendre en compte la configuration:
sudo service varnish restart
Si tout marche bien, vos sites doivent s'afficher avec les URL: http://www.nicolargo.com:6081 et http://blog.nicolargo.com:6081 (à remplacer par vos sites hein, faite pas les boulets...).
Tests sur le site statique
Test local sur le site statique (avec uniquement des éléments statiques page HTML & images). La configuration donnée ci-dessous ne montre pas l'optimisation pour ce site.
Test sur une page statique (HTML simple) sans Varnish:
ab -t 30 -c 5 http://www.nicolargo.com/
Requests per second: 651 [#/sec] (mean)
Time per request: 7.6 [ms] (mean)
Test sur une page statique (HTML simple) avec Varnish:
ab -t 30 -c 5 http://www.nicolargo.com:6081/
Requests per second: 652 [#/sec] (mean)
Time per request: 7.6 [ms] (mean)
Sur notre site Web statique, on n'a donc pas de gain au niveau de la capacité maximale de montée en charge du serveur Apache (Requests per second). Ce qui est normal vu qu'Apache n'a pas grand chose à faire pour servir ce genre de page. Par contre on observe une occupation mémoire moindre pendant le test avec Varnish (environ 10% de moins).
Tests sur le site dynamique (WordPress)
Test en local sur le site Web dynamique (WordPress, déjà optimisé en suivant ce tutoriel).
Test sur la page index d'un blog WordPress sans Varnish:
ab -t 30 -c 5 http://blog.nicolargo.com/
Requests per second: 588 [#/sec] (mean)
Time per request: 8.5 [ms] (mean)
Test sur la page index d'un blog WordPress avec Varnish:
ab -t 30 -c 5 http://blog.nicolargo.com:6081/
Requests per second: 3460 [#/sec] (mean)
Time per request: 1.5 [ms] (mean)
Sur notre site dynamique, la valeur ajoutée de Varnish est importante car on a ici un gain de plus de 488% en terme de nombre de requêtes simultanées que votre serveur peut fournir. Sur une page dynamique un peu plus lourde (par exemple un billet avec beaucoup de commentaires), le gain peut alors monter jusqu'à 650%.
Attention, cela ne veut pas dire que votre blog va s'afficher 488% plus vite avec Varnish mais "seulement" que votre serveur pourra accepter 488% de requêtes simultanées maximales en plus.
Configuration finale
Une fois votre site accéléré validé, il faut passer Varnish en production. Les actions suivantes sont à effectuer.
Configurer le serveur Apache hébergeant votre blog pour écouter sur un port différent du port TCP/80 (par exemple TCP/8080). Sous Ubuntu Server, on commence par modifier le fichier /etc/apache2/ports.conf pour lui demander d'écouter les requêtes HTTP sur le port 8080 en lieu et place du port 80:
NameVirtualHost *:8080
Listen 8080
On modifie ensuite le fichier de configuration de nos sites /etc/apache2/site-enabled/virtualhosts: (notez bien la modification du port :8080 et l'utilisation du format de log varnishcombined)
# WWW
<VirtualHost *:8080>
DocumentRoot /var/www/www
ServerName nicolargo.com
ServerAlias www.nicolargo.com
ServerAdmin contact@pasdespam.com
CustomLog /var/log/apache2/www-access.log varnishcombined
ErrorLog /var/log/apache2/www-error.log
</VirtualHost>
# BLOG
<VirtualHost *:8080>
DocumentRoot /var/www/blog
ServerName blog.nicolargo.com
ServerAdmin contact@pasdespam.com
CustomLog /var/log/apache2/blog-access.log varnishcombined
ErrorLog /var/log/apache2/blog-error.log
</VirtualHost>
On configurer le serveur Apache pour loguer les adresses sources et non pas l'adresse du serveur Varnish. Il suffit pour cela d'ajouter la ligne suivante dans le fichier /etc/apache2/apache2.conf:
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnishcombined
La configuration d'Apache est maintenant fini, il faut alors configurer Varnish pour écouter sur le port TCP/80 et changer la configuration du "backend" en éditant la section suivante du fichier /etc/default/varnish:
DAEMON_OPTS="-a :80 \
-T localhost:6082 \
-f /etc/varnish/default.vcl \
-S /etc/varnish/secret \
-p thread_pool_add_delay=2 \
-p thread_pools=4 \
-p thread_pool_min=200 \
-p thread_pool_max=4000 \
-p cli_timeout=25 \
-p session_linger=100 \
-s file,/var/lib/varnish/$INSTANCE/varnish_storage.bin,1G"
On change ensuite la configuration de Varnish pour pointer les backends vers le port 8080:
backend blog {
.host = "blog.nicolargo.com";
.port = "8080";
}
La dernière chose à faire si votre serveur est protégé par un Firewall IpTables est de modifier la règle ajoutée dans le premier chapitre de ce billet et d'y ajouter une nouvelle règle pour une éventuelle administration locale de Varnish à l'aide de l'utilitaire varnishadm:
iptables -A OUTPUT -p tcp --dport 8080 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 6082 -s 127.0.0.1 -j ACCEPT
On prend en compte la configuration finale en relancant les deux processus (Apache et Varnish):
sudo service apache2 restart
sudo service varnish restart
A partir de ce moment là, toutes les requêtes HTTP de vos visiteurs seront d'abord prise en compte par Varnish (sur le port TCP/80) puis ensuite, si nécessaire, par votre serveur Apache (TCP/8080).
Ecrire les logs sur le disque
Update du 22/02/2012
Par défaut, Varnish n'écrit pas les logs dans un fichier. Pour forcer Varnish à générer des fichiers de logs au format NSCA dans le sous répertoire /var/log/varnishnsca, nous allons utiliser le daemon VarnishNSCA (installé en même temps que Varnish). Il faut commencer par vérifier que script de lancement du daemon VarnishNSCA (/etc/init.d/varnishnsca) ne contient pas un bug, comme c'est le cas dans ma version 3.0.2:
Remplacer la ligne suivante dans le fichier /etc/init.d/varnishnsca:
DAEMON_OPTS="-a -w ${LOGFILE} -D -P $PIDFILE}"
par
DAEMON_OPTS="-a -w ${LOGFILE} -D -P ${PIDFILE}"
Puis forcer le lancement du daemon en modifiant le fichier /etc/default/varnishnsca:
VARNISHNCSA_ENABLED=1
On peut ensuite lancer le daemon:
sudo service varnishnsca start
Il est alors possible d'analyser ce fichier avec vos outils de stats favoris. Par exemple en ligne de commande avec GoAccess:
goaccess -f /var/log/varnish/varnishncsa.log
Quelques commandes utiles
- varnishlog: Affichage du log du daemon Varnish.
- varnishstat: Affichage des statistiques d'utilisation de Varnish.
- varnishhist: Affiche un historique sous forme de graphe des requêtes faites à votre serveur Varnish.
- varnishadm: une interface d'administration locale de Varnish
Vous pouvez également consulter la documentation en ligne à l'adresse suivante.