Skip to main content

Apache / Varnish / Wordpress

Apache - Varnish - Wordpress

Que es Varnish

VArnish es un acelerador HTTP, el cual almacena en caché los recursos de un servidor web y poder
crear la misma página una y otra vez cuando el usuario lo solicite. Se ejecuta frente a un servidor Web y
sirve las páginas mucho más rápido.

Funcionalidades

  • Equilibrio de Carga
  • Reescritura de URL
  • Comprobación de Backends
  • Manejo elegante de backend muertos
  • Soporte parcial para ESI(Edge Side Includes)

Arquitectura

  • Caché monolítica mapeada a memoria virtual
  • Archivos configuración compilados en C
  • Trata todo el ciclo de vida de una petición
  • Cambios de configuración en caliente
  • Logs escritos en memoria compartida

Comandos

  • varnishtop ->Lista ocurrencias de los log más comunes
  • varnishstat ->Estadísticas en tiempo de real
  • varnishhist ->Hits y misses en tiempo real
  • varnishlog / varnishncsa ->Generan logs tradicionales
  • varnishreplay ->Parsea logs y reduce el tráfico
  • como validar la configuración -> varnishd -C -f /etc/varnish/default.vcl
Funciones Varnish - Rutinas

Cuando una petición HTTP llega a Varnish, éste la procesa llamando a las diferentes rutinas en un orden concreto, y se ejecuta el código que hay dentro de dichas subrutinas. Ese código puede ser el código por defecto de Varnish o bien código personalizado por nosotros.

De estas rutinas las que más vamos a usar son: vcl_recv() y vcl_fetch(), aunque vamos a ver todas las opciones disponibles:*

vcl_recv() >Cuando se recibe una petición HTTP Varnish lanza esta subrutina. Nos permite decidir si la aceptamos o no, cómo hacerlo y que backend usar.
  • vcl_fecth() >Se ejecuta después de haber obtenido del backend HTTP los datos solicitados, después de haberse acepta la petición de vcl_recv().


En todas las funciones podemos realizar diferentes acciones, para ello tenemos la función return() con las diferentes acciones dentro de ella:

  • pass ->Si para la petición en curso devolvemos pass, la peticiÃşn se envia al servidor Backend sin buscarse en la caché y la respuesta del backend http se devuelve al usuario sin cachearse.
  • pipe ->Esta acciÃşn çortocircuita. el cliente HTTP y el Backend HTTP de forma que Varnish se limita a transferir datos de uno a otro. Es similar a pass (no se cachea) y ademÃąs Varnish no se dedica a inspeccionar el trÃąfico HTTP ni rellenar los objetos req/beresp/obj por lo que a veces se utiliza para evitar que objetos muy grandes (vÃŋdeos, etc) sean "procesados"por varnish.
  • lookup ->Fuerza a Varnish a que devuelva el objeto desde la caché incluso si la petición en sí mísma está solicitando contenido no cacheado.
  • deliver ->Le indica a Varnish que queremos devolver el objeto cacheado si es posible.
  • hit_for_pass ->Similar a pass (pero accesible desde vcl_fetch) salvo porque crea un objeto de tipo hitforpass y lo que se hace en este caso es cachear la decisión de no cachear.
  • restart ->Una forma de volver a ejecutar la lógica desde el principio.
  • vcl_hash() >Permite alterar el hash que se utiliza para gestionar el objeto en la cachÃľ. Normalmente es la URL pero podemos alterar dicho hash a nuestra voluntad. Un ejemplo sería cachear la página del perfil (/profile/) de cada usuario, aÃśadiendo concatenando la cookie de usuario a la URL, lo que generaría un objeto distinto en cada para cada usuario.
  • vcl_pipe() >Modo Pipe
  • vcl_pass() >Podemos forzar a que se reinicie la transacción, lo cual incrementa un contador interno de restart"que podemos detectar en otras funciones.
  • vcl_hit() >Llamada cuando lookup en la caché encuentra un objeto válido.
  • vcl_miss() >Es llamada cuando lookup no encuentra un objeto válido.
  • vcl_error() >LLamada cuando se encuentra un error por cualquier motivo.
  • vcl_deliver() >Es llamada antes de que un objeto cacheado sea entregado al cliente HTTP.

Compilación de Varnish 4.1

Para poder compilar sin problemas debemos tener instalados los siguientes paquetes:

pygpgme
yum-utils
epel-release-n

Para instalar epel-release-6 (Servidor de pruebas 6.9)
yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

Vamos al directorio de repositorios y creamos el nuestro para Varnish

/etc/yum.repos.d/

Creamos el archivo varnishcache_varnish41.repo con el siguiente contenido:

[varnishcache_varnish41]

  name=varnishcache_varnish41
  baseurl=https://packagecloud.io/varnishcache/varnish41/el/6/$basearch
  repo_gpgcheck=1
  gpgcheck=0
  enabled=1
  gpgkey=https://packagecloud.io/varnishcache/varnish41/gpgkey
  sslverify=1
  sslcacert=/etc/pki/tls/certs/ca-bundle.crt
  metadata_expire=300

[varnishcache_varnish41-source]

  name=varnishcache_varnish41-source
  baseurl=https://packagecloud.io/varnishcache/varnish41/el/6/SRPMS
  repo_gpgcheck=1
  gpgcheck=0
  enabled=1
  gpgkey=https://packagecloud.io/varnishcache/varnish41/gpgkey
  sslverify=1
  sslcacert=/etc/pki/tls/certs/ca-bundle.crt
  metadata_expire=300

Procedemos a la instalación:

sudo yum -q makecache -y --disablerepo='*' --enablerepo='varnishcache_varnish41'

Configuración Varnish

Parámetros de Arranque

Parámetros de arranque de Varnish, archivos de configuración:
/etc/sysconfig/varnish ->RedHat, CentOS, etc
/etc/default/varnish ->Debian, Ubuntu


# cat /etc/sysconfig/varnish
# Maximum number of open files (for ulimit -n)
NFILES=131072
# Locked shared memory (for ulimit -l)
# Default log size is 82MB + header
MEMLOCK=82000
# Maximum number of threads (for ulimit -u)
NPROCS="unlimited"
# Maximum size of corefile (for ulimit -c). Default in Fedora is 0
# DAEMON_COREFILE_LIMIT="unlimited"
# Init script support to reload/switch vcl without restart.
# To make this work, you need to set the following variables
# explicit: VARNISH_VCL_CONF, VARNISH_ADMIN_LISTEN_ADDRESS,
# VARNISH_ADMIN_LISTEN_PORT, VARNISH_SECRET_FILE.
RELOAD_VCL=1
# Set WARMUP_TIME to force a delay in reload-vcl between vcl.load and vcl.use
# This is useful when backend probe definitions need some time before declaring
# configured backends healthy, to avoid routing traffic to a non-healthy backend.
#WARMUP_TIME=0
# Main configuration file.
VARNISH_VCL_CONF=/etc/varnish/default.vc
# Default address and port to bind to
# Blank address means all IPv4 and IPv6 interfaces, otherwise specify
# a host name, an IPv4 dotted quad, or an IPv6 address in brackets.
VARNISH_LISTEN_PORT=80
# Telnet admin interface listen address and port
VARNISH_ADMIN_LISTEN_ADDRESS=127.0.0.1
VARNISH_ADMIN_LISTEN_PORT=6082
# Shared secret file for admin interface
VARNISH_SECRET_FILE=/etc/varnish/secret
# The minimum number of worker threads to start
VARNISH_MIN_THREADS=50
# The Maximum number of worker threads to start
VARNISH_MAX_THREADS=1000
# Cache file size: in bytes, optionally using k / M / G / T suffix.
VARNISH_STORAGE_SIZE=256M
# Backend storage specification
VARNISH_STORAGE="malloc,${VARNISH_STORAGE_SIZE}"
# Default TTL used when the backend does not specify one
VARNISH_TTL=120
# DAEMON_OPTS is used by the init script.
DAEMON_OPTS="-a ${VARNISH_LISTEN_ADDRESS}:${VARNISH_LISTEN_PORT} \
-f ${VARNISH_VCL_CONF} \
-T ${VARNISH_ADMIN_LISTEN_ADDRESS}:${VARNISH_ADMIN_LISTEN_PORT} \
-p thread_pool_min=${VARNISH_MIN_THREADS} \
-p thread_pool_max=${VARNISH_MAX_THREADS} \
-S ${VARNISH_SECRET_FILE} \
-s ${VARNISH_STORAGE}"
Las opciones más importantes son VARNISH_PORT Y VARNISH_VLC_CONF, sin estas dos parámetros no funcionará.
Configuración Caché

Tenemos el fichero /etc/varnish/default.vlc


Archivo de ejemplo:
# This is a basic VCL configuration file for varnish. See the vcl(7)
# man page for details on VCL syntax and semantics.
#
# Default backend definition. Set this to point to your content
# server.
#
backend default {
.host = "127.0.0.1";
.port = "8080";


sub vcl_recv {
# Happens before we check if we have this in cache already.
# Typically you clean up the request here, removing cookies you don't need,
# rewriting the request, etc.
#Capamos las cookies de wordpress para wp-login y wp-admin
if (!(req.url ~ "wp-(login|admin)")) {
unset req.http.cookie;
}
#Capamos para el de cookies que podrían afectar al administrador
set req.http.cookie = regsuball(req.http.cookie, "wp-settings-\d+=[^;]+(; )?", "");
set req.http.cookie = regsuball(req.http.cookie, "wp-settings-time-\d+=[^;]+(; )?", "");
set req.http.cookie = regsuball(req.http.cookie, "wordpress_test_cookie=[^;]+(; )?", "");
if (req.http.cookie == "") {
unset req.http.cookie;
}
#No se cachea todo lo que acabe con wp-admin o wp-login
if (req.url ~ "wp-admin|wp-login") {
return (pass);
}
}
sub vcl_backend_response {
# Happens after we have read the response headers from the backend.
# Here you clean the response headers, removing silly Set-Cookie headers
# and other mistakes your backend does.
}
sub vcl_deliver {
# Happens when we have all the pieces we need, and are about to send the
# response to the client.
# You can do accounting or modifying the final object here.
}
El lenguaje de configuración de Varnish llamado VCL(Varnish Configuration Language). En esta configuración debemos definir una serie de subrutinas y código dentro de las mismas. Varnish llamará a cada una de esta subrutinas en algún punto de la petición.
Este lenguaje soporta estructuras "tipo if, include, comentarios de como //, /* */ y , salida de funciones con return(), asignaciones con =, comparaciones con ==, negaciÃşn con !, and y or lógico con y ||, matche o contra expresiones regulares con y establecer/eliminar atributos con set y unset. También tenemos funciones como regsub y regsuball (sustituciÃşn por expresiones regulares de una o todas las ocurrencias).


Varnish y Wordpress

Varnish y el contenido dinámico de Wordpress (prácticamente todo) no se llevan muy bien, para ello debemos configurar varnish para que ciertos contenidos los muestre estáticamente.
Ejemplo default.vcl para Wordpress
vcl 4.0;

import std;

# Default backend definition. Set this to point to your content server.

backend default {

.host = "127.0.0.1";

.port = "8080";

}

#Backend net-lz.com

backend netlz {

.host = "127.0.0.1";

.port = "8080";

}

#Backend gamesranking.info

backend gamesranking {

.host = "127.0.0.1";

.port = "8080";

}

#Backend trailersdecine.com

backend trailersdecine {

.host = "127.0.0.1";

.port = "8080";

}

sub vcl_recv {

#Control para ver que backend utilizar

if (req.http.host == "www.net-lz.com" || req.http.host == "net-lz.com"){
set req.backend_hint = netlz;

} elseif (req.http.host == "www.gamesranking.net" || req.http.host == "gamesranking.net"){
set req.backend_hint = gamesranking;

} elseif (req.http.host == "www.trailersdecine.com" || req.http.host == "trailersdecine.com"){
set req.backend_hint = trailersdecine;

}else {
set req.backend_hint = default;

}

#Si la petición es para 443 nos aseguramos que lo marqué en las cabeceras HTML

if (std.port(server.ip) == 443){
set req.http.X-Proto = "https";

}

#Tipos de codificaciones aceptadas

if (req.http.Accept-Encoding) {
if (req.url ~ "\.(gif|jpg|jpeg|swf|flv|mp3|mp4|pdf|ico|png|gz|tgz|bz2)(\?.*|)$") {

# 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;

}

}

#Consultas archivos multimedia

if (req.url ~ "wp-content/themes/" && req.url ~ "\.(gif|jpg|jpeg|swf|css|js|flv|mp3|mp4|pdf|ico|png)(\?.*|)$") {

unset req.http.cookie;

set req.url = regsub(req.url, "\?.*$", "");

}

#Consultas a otro tipos de archivos

if (req.url ~ "\?(utm_(campaign|medium|source|term)|adParams|client|cx|eid|fbid|feed|ref(id|src)?|v(er|iew))=") {

set req.url = regsub(req.url, "\?.*$", "");

}

# no cacheamos las cookies de administrador

# soluciona la redirección que se generaba al querer entrar como administrador

if (req.http.cookie) {

if (req.http.cookie ~ "(wordpress_|wp-settings-)") {

return(pass);

} else {

unset req.http.cookie;

}

}

#Desactivamos la cache para esta url

if (!(req.url ~ "wp-(login|admin)")) {

unset req.http.cookie;

}

#No cacheamos cookies específicas de wordpress

set req.http.cookie = regsuball(req.http.cookie, "wp-settings-\d+=[^;]+(; )?", "");

set req.http.cookie = regsuball(req.http.cookie, "wp-settings-time-\d+=[^;]+(; )?", "");

set req.http.cookie = regsuball(req.http.cookie, "wordpress_test_cookie=[^;]+(; )?", "");

#No cacheamos cookies en general

if (req.http.cookie == "") {

unset req.http.cookie;

}

#Pasamos sin cacheo las url con wp-admin y wp-login

if (req.url ~ "wp-admin|wp-login") {

return (pass);

}

}

#fin vcl_recv


sub vcl_backend_response {

set beresp.ttl = 10s;

set beresp.grace = 1h;

}

#Marcamos que debemos guardar cómo estadísticas

sub vcl_deliver {

if (obj.hits > 0) {

set resp.http.X-Cache = "HIT";

} else {

set resp.http.X-Cache = "MISS";

}

return (deliver);

} 



Podríamos añadir una configuración para permitir la opción purge desde diferentes sitios, este no se ha hecho debido a que tenemos instalado un plugin en la red multisite de worpdress que nos ofrece esta funcionalidad y la configuración Varnish ha sido diseñada con esta objetivo. Ver: https://varnish-cache.org/trac/wiki/VCLExamples

Configuración Varnish especiales

REMOTE ADDRESS PHP

Para que PHP pueda capturar las Ip de los usuarios que se conectan debemos añadir algunos cambios al archivo default.vcl de varnish.


Añadimos la siguiente configuración dentro del la subrutina vcl_recv:

#Control Varnish para que PHP puede devolver las IP de los usuarios que se conectan

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;
  }
}

Ahora nos faltará 2 procesos, añadir unas líneas al Vhost de correspondiente y crear un fichero php que se encargará de asegurarnos que cojamos la ip buena.

Vhost de ejemplo:

<VirtualHost *:8080>
  DocumentRoot "/web/wordpress/static/trailersdecine"
  ServerName trailersdecine.com
  ServerAlias www.trailersdecine.com

  <Directory /web/wordpress/static/trailersdecine>
    #Linea para Varnish  
    php_value auto_prepend_file "/www/conf/sites/varnish_client_ip.php"
    AllowOverride All
    Order deny,allow
    Allow from all
  </Directory>
  
  
  CustomLog logs/common.trailersdecine combined
  ErrorLog logs/error.trailersdecine
  
  #Linea y log para varnish
  LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" varnish
  
</VirtualHost>

Como podéis observar dentro de Directory podemos ver como se hace una llamada al archivo varnish_client_ip.php, vamos a crearlo con el siguiente cotenido:

<?php
if( isset( $_SERVER[ 'HTTP_X_FORWARDED_FOR' ] ) ) {

  $_SERVER[ 'REMOTE_ADDR' ] = $_SERVER[ 'HTTP_X_FORWARDED_FOR' ];

} 

Con tal de no parar el servicio vamos a recargar ambos

  • service varnish reload
  • /www/bin/apachectl stop
  • /www/bin/apachectl start


Ya tenemos nuestro servicio ce cacheo listo

MOD HEADERS

Con tal de controlar las cabeceras que viene de http y https y permitir el trasposo de archivos multimedia entre estos dos protocolos debemos añadir configuración tanto a Varnish como en Apache.

Varnish mod_headers.


sub vcl_recv {
  # Save Origin in a custom header
  set req.http.X-Saved-Origin = req.http.Origin;
  # Remove Origin from the request so that backend
  # doesn't add CORS headers.
  unset req.http.Origin;
  ...
}




sub vcl_deliver {
  if (req.http.X-Saved-Origin == "https://www.trailersdecine.com"
  || req.http.X-Saved-Origin == "http://www.trailersdecine.com"
  || req.http.X-Saved-Origin == "https://trailersdecine.com"
  || req.http.X-Saved-Origin == "http://trailersdecine.com") {
    set resp.http.Access-Control-Allow-Origin =
    req.http.X-Saved-Origin;
  }
  if (resp.http.Vary) {
    set resp.http.Vary = resp.http.Vary + ",Origin";
  } else {
    set resp.http.Vary = "Origin";
  }
  ...
}
Apache mod_headers.
Para compilar el módulo si este no esta instalado:
/www/bin/apxs -i -c ./modules/metadata/mod_headers.c
Activación del módulo en Apache
En httpd.conf añadir al final

LoadModule headers_module /www/modules/mod_headers.so
En los Vhost de los dominios a controlar las cabeceras añadimos las línea:

Header set Access-Control-Allow-Origin "*"
Reiniciamos apache y Varnish y ya esta listo

Configuración Apache para Varnish

Como Varnish está a la escucha en el puerto 80, debemos indicarle a Apache que escuche en otro puerto, en este caso el 8080.

#Puertos de escucha
Listen *:8080
Listen *:443

#Módulos necesarios
LoadModule proxy_modulemodules/mod_proxy.so
LoadModule proxy_balancer_modulemodules/mod_proxy_balancer.so
LoadModule proxy_http_modulemodules/mod_proxy_http.so
LoadModulos mod_ssl

#NameVirtualHost
#!!Este paso no es estrictamente necesario!!
NameVirtualHost *:8080NameVirtualHost 217.13.124.73:443

Virtualhost para sitios sin SSL

<VirtualHost *:8080> 
  DocumentRoot "/web/wordpress/static/trailersdecine" 
  ServerName trailersdecine.com 
  ServerAlias www.trailersdecine.com 
  <Directory /web/wordpress/static/trailersdecine> 
    AllowOverride 
    All Order deny,allow 
    Allow from all 
  </Directory> 
  CustomLog logs/common.trailersdecine combined 
  ErrorLog logs/error.trailersdecine # Other directives here
</VirtualHost>


Es una configuración típica excepto con los puertos de escucha y con el puerto de escucha a la hora de configurar el Vhost: 8080.

Varnish y HTTPS

Varnish no soporta HTTPS, no podemos configurar Varnish para que escuche el puerto 443 simplemente.


Para solucionar este problema debemos configurar el virtualhost de la siguiente manera:

Virtualhost 443
<VirtualHost 217.13.124.73:443>
  ServerName trailersdecine.com
  ServerAlias www.trailersdecine.com
  ErrorLog logs/error_https.trailersdecine.com.log
  CustomLog logs/access_https.trailersdecine.com.log combined
  
  
  
  
  SSLEngine on
  SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
  SSLCertificateFile "/etc/letsencrypt/live/trailersdecine.com/fullchain.pem"
  SSLCertificateKeyFile "/etc/letsencrypt/live/trailersdecine.com/privkey.pem"
  ProxyPreserveHost On
  ProxyPass/ http://127.0.0.1:8080/
  ProxypassReverse/ http://127.0.0.1:8080/
</VirtualHost>


Podemos apreciar varias cosas en este fichero:
  1. No tiene la sentencia DocumentRoot
  2. Creamos la contestación a partir de la dirección interna y el puerto del escucha de Apache2

Una vez creado el VirtualHost para el puerto 443, reiniciamos los servicios y ya tenemos Varnish - Apache - HTTPS funcionando.

En la configuración de apache deberemos añadir:


  • NameVirtualHost 217.13.124.73:443

Enlaces de referencia