loading...

PHP + nginx + caché + tmpfs, una combinación ganadora

lito profile image Lito ・3 min read

Supongamos que tenemos un proyecto sencillo sin sistema de usuarios y queremos que suponga una carga muy muy baja para el servidor así como conseguir unos tiempos de respuesta super rápidos.

Una solución podría ser delegar la caché en el backend de PHP a través de código propio o alguna librería externa.

Otra solución más óptima es bajar un nivel y dejar que nginx se encarge de generar las cachés.

Esta opción tiene sus ventajas, ya que es mucho más rápida (no hay petición a backend, es una capa menos) y sus inconvenientes (no es posible gestionar esta caché desde PHP).

Si optamos por esta segunda, la configuración es bastante sencilla.

Lo primero es crear un punto de montaje basado en tmpfs que nos permitirá reducir las peticiones a disco y optimizar su lectura.

$> mkdir /var/cache/nginx
$> chown www-data:www-data /var/cache/nginx
$> echo 'tmpfs /var/cache/nginx tmpfs defaults,size=256M 0 0' >> /etc/fstab
$> mount /var/cache/nginx

Ahora, configuramos nginx:

/etc/nginx/sites-enabled/example.com

# Creamos una zona de caché con un límite de de 256 megas apuntando al directorio tmpfs
# Todo fichero con más de 60 minutos desde el último acceso será descartado
fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=EXAMPLE:256m inactive=60m;
# Indicamos con qué parametros se deben generar las cachés
fastcgi_cache_key "$scheme$request_method$host$request_uri";

server {
    listen 443 ssl http2;

    server_name example.com www.example.com;

    root /var/www/example.com/httpdocs/public;
    index index.php;

    include sites/example.com/base.conf;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

    access_log /var/log/nginx/example.com_access.log combined buffer=32k flush=60;
    error_log  /var/log/nginx/example.com_error.log;
}

/etc/nginx/sites/example.com/base.conf

rewrite ^/(.*)/$ /$1 permanent;

location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location ~ \.php$ {
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

    include fastcgi_params;

    include sites/example.com/cache.conf;
}

/etc/nginx/sites/example.com/cache.conf

# Añadimos una cabecera con el status de cada petición
add_header X-Cache $upstream_cache_status;

# Configuramos la zona de caché a usar
fastcgi_cache EXAMPLE;
# Configuramos que tipo de respuesta es válida para ser cacheada y los tiempos en caché
fastcgi_cache_valid 200 301 302 60m;
# Configuramos con que tipo de errores podemos mostrar una caché obsoleta
fastcgi_cache_use_stale error timeout updating invalid_header http_500 http_503;
# Configuramos los usos mínimos por caché
fastcgi_cache_min_uses 1;
# Si entran varias peticiones simultáneas a una ruta sin caché, esperar
# a que la primera de ellas genere la caché y el resto responda lo mismo
fastcgi_cache_lock on;
# Evitamos las cabeceras de Cache-Control
fastcgi_ignore_headers Cache-Control;

set $no_cache 0;

# Aquí puedes añadir todas tus condiciones para evitar la caché

# No se cachea ninguna petición POST
if ($request_method = POST) {
    set $no_cache 1;
}

fastcgi_cache_bypass $no_cache;
fastcgi_no_cache $no_cache;

Si quieres profundizar en este tema, tienes muchos posts interesantes, como por ejemplo Make your PHP site fly. Even on small VM instance.

Y con esto y un bizcocho tienes un sistema de caché sencillo y muy eficiente para tus sitios semi-estáticos.

Posted on by:

Discussion

markdown guide