Uso de Varnish Cache con Nginx y Wordpress

  Varnish Cache, o Varnish a secas, es un acelerador de aplicaciones web, también conocido como caché de proxy HTTP inversa. Se instala delante de cualquier servidor de red y se configura para almacenar en el caché del servidor una copia del recurso solicitado. Está ideado para aumentar el rendimiento de aplicaciones web con contenidos pesados y APIs altamente consumidas.
  Es una alternativa a otras opciones existentes que plantean caché de cliente o servidores de origen. Además, este programa está orientado exclusivamente a HTTP.
  Cuando Varnish recibe las peticiones HTTP, podrá decidir entre:
  •   Aceptarlas o denegarlas en base a determinados criterios.
  •   Pasarlas directamente al backend HTTP (parte que procesa la entrada desde la parte del software que interactúa con el usuario) para que las atienda y devuelva a Varnish la salida de la petición.
  •   Cachear (según determinados criterios impuestos por el usuario) la salida devuelta por el backend HTTP a una determinada petición bien a memoria o a disco.
  •   Servir la respuesta desde la caché (según determinados criterios impuestos por el usuario) en lugar de pasarla al backend.

  En todo este proceso, este programa podrá, además, manipular las cabeceras HTTP de las respuestas, imponiendo "TTLs", tiempos de expiración, eliminando cabeceras, cookies, etc, según las reglas de configuración que se le hayan indicado.
  El resultado es que Varnish podrá servir contenidos estáticos y dinámicos sin que estas peticiones lleguen realmente al backend HTTP, lo que puede producir un increíble incremento de rendimiento en una plataforma web.
  Una de las características clave, además de su rendimiento, es la flexibilidad de su configuración del lenguaje VCL (Varnish Configuration Language). VCL es un lenguaje específico de dominio DSL utilizado para implementar los ganchos ("hooks", en inglés) que son llamados en momentos críticos del manejo de cada solicitud. Cuando un guión VCL es cargado, traducido al lenguaje C, compilado a un objeto compartido por el compilador de sistema y cargado directamente en el acelerador, permitiendo reconfiguraciones sin necesidad de reinicios.
  VCL permite determinar las políticas a tomar sobre las peticiones de entrada. En esta política se puede decidir qué contenido desea servir, desde donde se desea obtener el contenido y la forma en que la solicitud o la respuesta debe ser alterada. Esto hace a Varnish Cache mucho más configurable y flexible que otros aceleradores HTTP.
  Hay una cantidad de parámetros de ejecución para controlar el máximo y mínimo de hilos, timeouts, etc. Existe una interfaz de línea de comando que permite modificar estos parámetros, compilar nuevos guiones VCL, cargarlos y activarlos, sin necesidad de reinicio.
  Para reducir la cantidad de llamadas de sistema, la información de registros es almacenada en la memoria compartida, mientras que las tareas de monitoreo, filtrado, formateo y escritura de los registros a disco es delegada una aplicación diferente.
  Básicamente, si hay un solo servidor, se debe ubicar el servidor Nginx con WordPress en el puerto 81, y levantar Varnish en el puertto 80 para que atienda las peticiones HTTP de los usuarios clientes HTTP.


INSTALACIÓN DE VARNISH

  Para instalar este programa (cuya versión en los ejemplos es la 5.0) en Linux (concretamente en Debian 9, que es el sistema operativo que servirá para ilustrar los ejemplos de la presente explicación), se deben seguir los pasos de instalación de paquetes básicos:
  •   Actualización de lista de paquetes [comando apt update (anteponiendo sudo si no se está como administrador)].
  •   Actualización de paquetes instalados [comando apt upgrade (anteponiendo sudo si no se está como administrador)].
  •   Instalación del paquete [comando apt install varnish (anteponiendo sudo si no se está como administrador)].
  

CONFIGURACIÓN DE ARRANQUE DEL VARNISH 

  Para que Varnish Cache arranque correctamente, se debe configurar, en primer lugar, el archivo "varnish" (alojado en la ruta "/etc/default"), donde sólo se deben configurar las líneas descomentadas por defecto (salvo que se quiera hacer algo más complicado). Para ello se debe editar dicho archivo como administrador y configurar las siguientes líneas:
DAEMON_OPTS="-a <dirección IP donde escuchar:puerto>  \
             -T <dirección IP donde escuchar:puerto> \
             -f <ruta del fichero de configuración> \
             -S /etc/varnish/secret \
             -s <modo, lugar y cantidad de memoria de cacheo>"

  En el parámetro "-s" se utilizará el valor "malloc" para el modo de cacheo en memoria, y el valor "file" para el modo de cacheo en disco (las cantidades en este caso pueden ser absolutas con un valor numérico, o relativas con un valor de porcentaje). La configuración final debe ser parecida a la de la siguiente imagen (en este ejemplo la dirección IP y el puerto de escucha son 192.168.0.15:80", los mismos valores para "-T"  son "localhost:6082", el archivo de configuración es el predeterminado, y el almacenamiento es de 256 mb en memoria).
   En segundo lugar, hay que editar el archivo "varnish.service" (situado en "lib/systemd/system/") y añadir las siguientes líneas:
ExecStartPre=/usr/sbin/varnishd -C -f <ruta del fichero de configuración>
ExecStart=/usr/sbin/varnishd -j unix,user=vcache -F -a <mismo valor que en archivo anterior> -T <mismo valor que en archivo anterior> -f <mismo valor que en archivo anterior> -S /etc/varnish/secret -s <mismos valores que en el archivo anterior>
ExecReload=/usr/share/varnish/reload-vcl

  El archivo debe quedar de un modo similar al de la siguiente imagen.

  Aunque no es imprescindible, es una buena práctica copiar este archivo en la ruta "/etc/systemd/system/" [comando cp /lib/systemd/system/varnish.service /etc/systemd/system/varnish.service (anteponiendo sudo si no se está como administrador)].

CONFIGURACIÓN DE VARNISH PARA WORDPRESS

  El siguiente paso es configurar el Varnish Cache editando el archivo "default.vcl" (puede tener otro nombre, mejor si es representativo, pero debe tener siempre la extensión ".vcl"), situado en la ruta "/etc/varnish". En dicho archivo, primeramente se debe añadir el servidor que hará de backend HTTP (en este ejemplo se trata del servidor de red Nginx 1.14), para lo que hay que cambiar las siguientes líneas según los parámetros de la red donde se apliquen:
    .host = "<dirección IP donde escuchar>";
    .port = "<puerto donde escuchar>";
  Además, pueden añadirse las siguientes líneas el mismo bloque:
    .connect_timeout = <tiempo de espera de la conexión>;
    .first_byte_timeout = <tiempo de espera del primer byte>;
    .between_bytes_timeout = <tiempo de espera entre bytes>;
    .max_connections = <nº máximo de conexiones>;
  El bloque correspondiente debe quedar de un modo similar al siguiente.
  A continuación, se hace un bloque de limpieza ACL que sólo limpie la caché del equipo local y del sitio de WordPress. Dicho bloque estár constituído por direcciones IP o DNS del siguiente modo:
acl <nombre> {
    "<dirección IP>";
    "<DNS>";
    "<dirección IP"/<CIDR>;
                   ...
}

  Este bloque puede quedar de una manera parecida a esta:
  Seguidamente viene el bloque "vcl_recv", que es una subrutina del programa que se activa cuando se recibe una petición HTTP y este la ha procesado y parseado. Dentro de esta subrutina el usuario puede configurar si acepta o no la petición, cómo hacerlo y usando qué backend HTTP.
 En este bloque deberán ir las siguientes líneas exactamente igual que como se presentan a continuación, excepto en lo referente a la parte del WordPress (si no, el Varnish dará errores al reiniciarlo).
 # 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.method == "PURGE") {
                # If not allowed then a error 405 is returned
                if (!client.ip ~ purge) {
                        return(synth(405, "This IP is not allowed to send PURGE requests."));
                }
                # If allowed, do a cache_lookup -> vlc_hit() or vlc_miss()
                return (purge);
        }

        # Post requests will not be cached
        if (req.http.Authorization || req.method == "POST") {
                return (pass);
        }
# Aquí deberá ir la configuración específica de WordPress.
# Do 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 (hash);
}

  En el lugar indicado de las líneas anteriores, deberá ir la parte específica para WordPress. Se trata de las líneas siguientes (que también deben ir tal cual aparecen aquí):
# 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)$") {
                                unset 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 {
                        unset 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;
        }

  Después, se añaden los bloques "vcl_pipe" ("cortocircuita" el cliente HTTP y el backend HTTP de forma que Varnish se limita a transferir datos de uno a otro) y "vcl_pass" (si para la petición HTTP en curso se devuelve "pass", la petición se envía al servidor backend HTTP sin buscarse en la caché y la respuesta del backend HTTP se devuelve al cliente sin cachearse) mediante las líneas siguientes:
sub vcl_pipe {
        return (pipe);
}

sub vcl_pass {
        return (fetch);
}

  El siguiente bloque que se debe añadir es el de "vcl_hash", que permite alterar el hash que se utiliza para gestionar el objeto en la caché. Tiene las siguientes líneas: 
# 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 (lookup);
}

  El bloque que viene a continuación, "vcl_backend_response", gestiona las respuestas del backend HTTP.  Sus líneas son las siguientes:
# This function is used when a request is sent by our backend (Nginx server)
sub vcl_backend_response {
        # 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 (bereq.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 && bereq.url !~ "^/wp-(login|admin)") {
                unset beresp.http.Set-Cookie;
        }

        # don't cache response to posted requests or those with basic auth
        if ( bereq.method == "POST" || bereq.http.Authorization ) {
                set beresp.uncacheable = true;
                set beresp.ttl = 24h;
                set beresp.grace = 1h;
                return (deliver);
        }

        # don't cache search results
        if ( bereq.url ~ "\?s=" ){
                set beresp.uncacheable = true;
                set beresp.ttl = 120s;
                return (deliver);
        }

        # only cache status ok
        if ( beresp.status != 200 ) {
                set beresp.uncacheable = true;
                set beresp.ttl = 120s;
                return (deliver);
        }

        # A TTL of 24h
        set beresp.ttl = 24h;
        # Define the default grace period to serve cached content
        set beresp.grace = 30s;

        return (deliver);
}

  El siguiente bloque a agregar es "vcl_deliver", cuya función es llamada antes de que un objeto cacheado sea entregado al cliente HTTP. Posee las siguientes líneas:
# 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);
}

  Por último, se añadirań los bloques de las directivas "vcl_ init" y "vcl_fini", con las siguientes líneas del archivo:
sub vcl_init {
        return (ok);
}

sub vcl_fini {
        return (ok);
}

  Tras guardar los cambios y cerrar el archivo, debe validarse la configuración utilizando el comando varnishd -C -f <ruta al archivo de configuración>. Después, se reiniciará el Varnish Cache [comando systemctl restart varnish (anteponiendo sudo si no se está como administrador)].

CONFIGURACIÓN DE NGINX

  Suponiendo que tanto el servidor de red, como el gestor de contenidos, como el sistema de gestión de bases de datos asociado a este último estan bien configurados de manera básica (si no es así, consultar el siguiente enlace), se editará el archivo específico de la ruta "/etc/nginx/sites-available" (en este ejemplo es "svelb2.net") y se realizará el cambio pertinente en la línea "listen" para que su puerto sea el 81, quedando el archivo de modo similar al de la siguiente imagen.

  Tras guardar y cerrar el archivo, se reinicia Nginx [comando systemctl restart nginx (anteponiendo sudo si no se está como administrador)].

  Si todo está correctamente, debe verse la página de WordPress al escribir el nombre del dominio en la barra de direcciones del navegador, y no sólo eso, sino que debe cargarse más rápidamente de lo que lo hacía anteriormente.
  Puede comprobarse la mejora de la velocidad de transferencia mediante el programa Apache Bench, por ejemplo. Una vez instalado en el equipo cliente [comando apt install apache2-utils (anteponiendo sudo si no se está como administrador)], se utilizará el comando ab -c <nº de conexiones concurrentes> -n <nº de peticiones>
Prueba antes de Varnish


Prueba después de Varnish

 Espero que la presente entrada haya sido del gusto del lector; de ser así, aguardo que la comente y/o la comparta, por favor.

No hay comentarios:

Publicar un comentario

Deje aquí su comentario, si no puede comentar, pruebe a hacerlo desde otro navegador de red u otro equipo.