Ja fa un parell d’anys que faig servir el varnish cache i és un molt bon aliat de qualsevol plataforma web per descarregar als servidors http (apache, nginx, lighthttpd, etc… ) i al backend, ja sigui per aplicacions amb poc tràfic com per aplicacions/plataformes d’alt rendiment, on pot ser un CLAU aliat.
Per això, en aquest post enumenarem les raons principals d’implantar el varnish, citarem els processos i posarem 4 exemples de configuracions amb diferents proposits.
Bueno, començaré enumerant les 3 funcionalitats principals:
– Accelerador de continguts web, minimitzant considerablement els temps mitjans de resposta dels continguts dinamics d’un site.
– Alliberar de càrrega el backend, des dels servidors web i sobretot a les bases de dades, sql i nosql.
– Permetre servir continguts “caducats” en cas que la plataforma tingui problemes de rendiment.
Altres funcionalitats més avançades que podem fer amb Varnish es,
– Si treballeu amb CDNs pot fer el paper d’amortidor en moments de canvis bruscos de tràfic, per exemple, un campanya amb molt èxit, una noticia important, etc…
– Securitzar el website, per segons quines parts de la web podem exigir que les peticions haguin de complir uns determinats requeriments. Això també ho pot fer el apache, però aixi el podem alliberar d’aquestes tasques.
– En plataformes d’alt rendiment, si treballeu amb CDNs, per bloquejar l’accés a determinades parts de les webs que no es puguin processar per averia en el sistema.
Segur que podeu trobar altres funcionalitats, aquestes són les que he utilitzat.
La filosofia del Varnish es emmagatzemar a memòria volàtil tots els cantinguts “cachejables” i estalviar de cicles de proces i cicles d’espera d’entrada i sortida, es a dir, l’equiliri IO-CPU-RAM es veurà afectat així: disminuim IO, disminuim us de CPU, increment ús de memoria, on és molt important dimensionar bé la memòria que utilitzarem.
Un dels principals inconvenients dels Varnish és el HTTPS, encara que té el sentit. Varnish és un accelerador de continguts HTTP que vol disminuir el temps de resposta, el httpS és el contrari, requereix de temps de proces per a tal d’assegurar la seguretat. Conclusió, Varnish no es capaç i no ho serà mai de servir contignut per HTTPS. Només el podrem fer servir per aquelles parts del website que no siguin segures. També ho podeu utilitzar en parts de la web segures si poseu un nginx per davant, en aquest altre post teniu explicat millor aquesta solució.
D’altra banda, la corba d’aprenentatge del varnish es considerable, de fet, un graph em va ajudar a entendre totes les transicions possibles, es a dir, és el tipic aliat que si el compliquem molt es pot tornar en enemic. De fet, el varnish són 2 procesos un pare i un fill (fork), on el pare fa el paper de gestió i compila les configuracion escrites amb llenguatge VCL i el fill és qui realment fa realment les funcions descrites en la configuracio VCL. En aquest link ho explica detalladament.
Es a dir, la clau del varnish, és escriure un VCL amb allò que realment necessitem que faci. Aqui us deixo un graph que vaig decidir fer un cop vaig llegir la documentació oficial del varnish d’aquest link.
Seguidament posarem 3 exemples de fitxers basics de VCL i comentarem la seva finalitat:
Exemple 1
backend default { .host = "127.0.0.1"; .port = "8080"; .max_connections = 40; .first_byte_timeout = 180s; .between_bytes_timeout = 180s; } sub vcl_recv { return(pass); }
Aquesta configuració l’únic que voliem és limitar la quantitat de connexions(max_connections) que li passarem al un apache en local, en aquest cap no cachejem res.
Exemple 2
backend default { .host = "127.0.0.1"; .port = "8080"; .max_connections = 200; .connect_timeout = 180s; .first_byte_timeout = 180s; .between_bytes_timeout = 180s; .probe = { .url = "/healthcheck.php"; .timeout = 100 ms; .interval = 1s; .window = 10; .threshold = 8; } } sub vcl_recv { if (req.http.Host == "setup.domain.com") { set req.grace = 30s; return(lookup); } else { return(pass); } } sub vcl_fetch { set beresp.grace = 1h; }
En aquest exemple només cachejem les peticions del domini “setup.domain.com”, domini intern de la plataforma que serveix les configuracions de l’aplicació. Ademés, volem que si el backend no respon, seguiran sen vàlids durant 1 hora. Això ho conseguim en les tres últimes linies de codi. El meu company Tomas té un article on exemplifica molt bé aquest comportament. Una de les coses més interessants del article de Tomàs es diferenciar les dues situacions en les que aquest parametre es important.
– No hi ha cap backend disponible, per tant servim contingut antic.
– Hi ha backends disponibles, i un fil ja ha demanat nou contingut. Mentre aquest nou contingut arriba des del backend, el varnish continua servint l’antic a la resta de fils.
Exemple 3
sub vcl_recv { … if (req.url ~ ".*remindmethepassword.*" && !req.http.User-Agent) { error 403 "Forbidden"; } … return(pass); }
En el exemple anterior, podem aferir-hi regles que augmentent la seguretat, per exemple, en les peticions que tinguin remindmethepassword en la URL necessitaran tenir el User-Agent, sino retornem un 403.
Exemple 4
backend default { .host = "127.0.0.1"; .port = "8080"; .max_connections = 200; .connect_timeout = 180s; .first_byte_timeout = 180s; .between_bytes_timeout = 180s; .probe = { .url = "/healthcheck.php"; .timeout = 10 ms; .interval = 5s; .window = 5; .threshold = 3; } } sub vcl_error { } sub vcl_recv { 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; } } if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } if (req.request != "GET" && req.request != "HEAD" && req.request != "POST" ) { /* We only deal with GET and HEAD by default */ return (pass); } set req.grace = 30s; return(lookup); } sub vcl_fetch { if (beresp.ttl <= 0s || beresp.http.Vary == "*") { return (hit_for_pass); } if(beresp.ttl > 0s){ unset beresp.http.Set-Cookie;} set beresp.grace = 1h; return(deliver); }
En aquest exemple ja comença a ser més complert:
– limitat a 200 connexions, a més peticions, retornem 503.
– accepted petitions GET, HEAD, PUT, POST, TRACE, OPTIONS, DELETE.
– cachejem GET, POST, HEAD segons les capceleres HTTP que emet el backend
– en el moment en que anem a buscar un objecte en la cache, eliminem les capçeleres Set-Cookie
– tindrem 1 hora els elements en la cache, en cas que el backend estigui caigut o no respongui
Finalment, el ideal seria tenir ben segmentada la plana web amb les diferents seccions i tenir identificades les url’s de cadascuna d’elles facilitant així:
– La propia aplicació generi les capçeleres HTTP adecuades especificant quant de temps serà vàlid el contingut, segons quines seccións o accions tindrem uns temps de caducitat diferents
– La propia aplicació permite caducar/invalidar el contingut en el varnish en les situacions que ho necessitin.
– Securitzar les parts de la web que ho requereixin jugant amb les capçeleres