Varnish est un service qui permet de mettre en cache (sur disque ou en mémoire) certains contenus de votre site. Beaucoup de gros sites l’utilisent pour sa puissance et ses options de configuration.
Il est plutôt facile à installer mais ça se corse lorsqu’il faut le configurer car il peut ne rien cacher du tout, surtout avec la config par défaut.
Nous allons voir ici une configuration pour un site sous WordPress avec un serveur Nginx, Varnish étant placé en Front c’est à dire devant Nginx (il y a aussi le mode sandwich Nginx / Varnish / Backend).
Installation de Varnish sous ubuntu:
sudo apt-get install varnish |
Il y a 2 fichiers qu’il faut retenir pour Varnish, le fichier avec les règles et le fichier de config du service.
Configurer le service, On va faire en sorte que Varnish écoute sur le port 80:
vi /etc/default/varnish |
DAEMON_OPTS="-a :80 \ -T localhost:6082 \ -f /etc/varnish/default.vcl \ -S /etc/varnish/secret \ -s malloc,256m" |
Trouvez cette ligne dans le fichier de conf et mettez 80 pour l’option -a.
malloc va sauver le cache en mémoire à hauteur de 256 MB, on peut sauvegarder sur disque si on a un SSD (ex: -s file,/var/lib/varnish/varnish_storage.bin,1G”).
Configurer les règles de cache:
vi /etc/varnish/default.vcl |
# # 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; } # 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 ~ "(sametmax.com)")) { set req.backend = default; } else { return (pass); } # 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]+", ""); # Post requests will not be cached if (req.http.Authorization || 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); } # Define the default grace period to serve cached content set req.grace = 30s; # 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); } # This function is used when a request is sent by our backend (Nginx server) sub vcl_fetch { # Remove some headers we never want to see unset beresp.http.Server; unset beresp.http.X-Powered-By; # For static content strip all backend cookies if (req.url ~ "\.(css|js|png|gif|jp(e?)g)|swf|ico") { unset beresp.http.cookie; } # Only allow cookies to be set if we're in admin area if (beresp.http.Set-Cookie && req.url !~ "^/wp-(login|admin)") { unset beresp.http.Set-Cookie; } # don't cache response to posted requests or those with basic auth if ( req.request == "POST" || req.http.Authorization ) { return (hit_for_pass); } # don't cache search results if ( req.url ~ "\?s=" ){ return (hit_for_pass); } # only cache status ok if ( beresp.status != 200 ) { return (hit_for_pass); } # A TTL of 24h set beresp.ttl = 24h; 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; # Remove some heanders: Varnish unset resp.http.Via; unset resp.http.X-Varnish; return (deliver); } sub vcl_init { return (ok); } sub vcl_fini { return (ok); } |
Avant toute chose voici la doc :) : https://www.varnish-cache.org/docs/3.0/
Ce qu’il faut retenir de ce fichier de règles c’est les parties normalisation, enlever les cookies inutiles (car si cookie alors pas de cache) et ne pas mettre en cache l’admin. Cette config tourne actuellement sur S&M.
Un point intéressant c’est le debug, vous pouvez mettre un set resp.http.X-Cache = “cached”; et vérifier les headers de votre serveur dans le debug de votre navigateur pour savoir si oui ou non la page servie est en cache.
L’admin varnish en ligne de commande:
Varnish possède une admin en ligne de commande où l’on peut par exemple purger le cache d’une ou plusieurs pages.
Tapez varnishadm dans le shell:
varnishadm 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,3.2.0-25-virtual,x86_64,-smalloc,-smalloc,-hcritbit Type 'help' for command list. Type 'quit' to close CLI session. varnish> ban.url /toto/ma-page.php |
Pour purger une page on va utiliser la commande ban.url et y ajouter une partie de l’url à purger. On peut utiliser un ban en appelant également une url de purge avec Curl ou Wget pour les automations, il y a un excellent article sur la purge ici.
Configurer Nginx:
Une fois varnish installé, on va modifier un peu nginx pour que la liaison puisse se faire. Ci-dessous l’exemple de notre conf nginx.
server { listen 8080; server_name sametmax.com ; root /unrep/sametmax/; # absolute path to your WordPress installation index index.php index.html; set_real_ip_from 127.0.0.1; real_ip_header X-Forwarded-For; location ~ \.php$ { include /etc/nginx/fastcgi_params; fastcgi_pass 127.0.0.1:53217; fastcgi_index index.php; fastcgi_buffers 8 16k; fastcgi_buffer_size 32k; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } |
Pour PHP on utilise spawn-fcgi que l’on gère avec supervisor dont voici la conf dans /etc/supervisord.conf
[program:php5-cgi] command=/usr/local/bin/spawn-fcgi -u adolf -n -a 127.0.0.1 -p 53217 -P /tmp/fastcgi-php.pid -F 4 -- /usr/bin/php-cgi redirect_stderr=true ; redirect proc stderr to stdout (default false) user=sametmax autostart=true autorestart=true startsecs=10 startretries=9999999999999 stdout_logfile=/var/log/php5-cgi/php5-cgi.log stdout_logfile_maxbytes=10MB ; max # logfile bytes b4 rotation (default 50MB) |
Pour résumer:
Dans cette configuration Varnish est en front end et va décider quand servir une page prise dans le cache (disque dur ou RAM) et quand interroger le backend pour servir une nouvelle page.
Sur d’autres sites à fort trafic on a mis Nginx en front end car il encaisse beaucoup plus les hits et qu’il fait très bien le boulot pour servir des pages statiques.
Attention!
Même si Varnish n’est pas compliqué à installer il peut être un vrai casse-tête quand il s’agit de cacher des pages. Pensez donc bien à normaliser vos headers (user-agent, gzip, etc), utilisez le debug.
Bien configuré Varnish vous booste un serveur avec une réélle efficacité, il nous a sauvé 2 sites jusqu’à présent, ça sera d’ailleurs un standard sur nos prochains sites.
Rhooo le nom de l’utilisateur pour le process PHP ;-p
Sinon le top avec Varnish c’est quand l’applicatif communique avec le backend pour la gestion des purges et passe les bon headers pour l’expiration ou l’exclusion des caches. Et ça peut aller jusqu’à avoir de la granularité sur les caches de pages avec gestion de bloc et tout le bazard.
Et pas la peine de réinventer la roue quasiment tout les gros framework et cms on des plugin/modules pour ça maintenant ils fournissent même souvent le fichier de conf Varnish adapté.
Ceci dit Varnish ça fait encore un truc en plus a entretenir et susceptible de planter ou d’introduire de multiple problème prise de tête.
Bref quand on a un applicatif et un serveur web qui font déjà bien le boulot avec une gestion du cache efficace je préfère autant le laisser au garage (feignasse inside).
“Varnish configuration language” encore un langage il a l’air simple mais je me méfie
je savais que ça allait en faire tilter un ça :)
ouais j’ai mis un lien pour ça d’ailleurs, le rajouter aurait encore allourdi l’article, mais c’est une usine à gaz ce truc.
Je suis du même avis mais on a testé le cache de DJango tant sur disque qu’en RAM ben c pas terrible, du moins avec le volume qu’on avait à traiter, la seule alternative a été d’utiliser Varnish qui jusqu’à présent a jamais planté.
Gérer PHP par spawn-cgi et supervisor c’est juste gérer le restart en cas de plantage ou il y a un gain de perf par rapport à php-fpm ?
tu peux mettre php-fpm à la place de spawn-fcgi, il parrait que c’est mieux en terme de perfs mais on a très peu de sites sous php (en fait que S&M).
Les 2 sont indépendants de supervisord qui lui ne gère que les services et les monitore (restart, etc), je vois pas comment il pourrait faire gagner en perfs donc là en loccurence on a mis spawn-fcgi car c’est le premier qui nous est tombé sous la main ;)
Niveau perf php-fpm est censé être un poil plus rapide.
Pas décisif mais ajouté a ses autres petits avantages (chroot moins prise de tête, les /status /ping pour surveiller les thread…), je ne vois plus trop l’intérêt d’installer spawn-fcgi sans que pour autant ça vaille le coup de le remplacer sur une config existante.
Tiens ça me fait penser, comme alternative à la configuration Varnish proposé, le fastcgi_cache intégré à Nginx est pas mal non plus.
On peut déjà faire des truc sympa pour les exclusions et purges des caches en bricolant avec les headers et cookies.
L’idée est la même, conserver des versions statique des pages et ne soliciter le backend php que quand c’est nécessaire.