Es la moda :), y con este comentario no quiero decir que no sea una buena idea.
La explicación es sencilla. Solemos utilizar Apache para servir cualquier tipo de contenido a través de una petición HTTP, pero en Apache cargamos habitualmente muchos módulos necesarios para que nuestras aplicaciones funcionen correctamente, empezando por el mismísimo PHP, pero que no necesitamos para servir contenidos estáticos (imágenes, archivos css o javascript, archivos comprimidos…). Si pudiésemos separar de una manera sencilla las peticiones de estáticos de las de dinámicos podríamos redirigirlas a distintos servicios y conseguiríamos que las estáticas consumiesen muchos menos recursos ya que las podríamos hacer con un servidor mucho más ligero que Apache. ¡Podemos!
Este sería el escenario tradicional de un servidor web:
Las solicitudes HTTP llegan al servidor web desde Internet y éste lee los archivos en disco necesarios para servirla. Nada nuevo.
Este es el escenario al que queremos migrar:
Todas las peticiones HTTP son recibidas en el puerto 80 por un servidor mucho más ligero que Apache (nginx, lighttpd) que se encarga de servir directamente los contenidos estáticos desde el disco y de redirigir las solicitudes dinámicas al Apache de siempre que ahora escucha en el puerto 8080, es decir, el servidor ligero actúa como proxy para las peticiones de contenidos dinámicos.
Para el caso que nos ocupa, el servidor a migrar tiene varios virtual hosts definidos de distintos sites y se pretendía dejar los que tienen poco tráfico tal y como están ahora, es decir, que Apache lo sirva todo, y cambiar sólo aquellos donde el tráfico es elevado para que Nginx sirva los estáticos.
El primer paso es, por tanto, instalar y configurar Nginx para que actúe como proxy completo. Deberemos modificar también Apache para que deje de escuchar en el puerto 80. Empecemos por esto último.
Tendremos que cambiar el fichero de configuración httpd.conf para que escuche en el nuevo puerto, en mi caso el 8080. Cambiaremos la línea correspondiente para que quede así:
Listen 8080
Pero esto no es todo. Tenemos que modificar también los virtual hosts definidos para que escuchen en el nuevo puerto. Al final del mismo archivo cambiaremos la línea correspondiente por esta (la tuya será similar, quizás en vez del asterisco tenga la ip de tu máquina):
NameVirtualHost *:8080
En mi Centos5 la configuración de los virtual hosts se guarda en /etc/httpd/httpd.d. Desde ahí lanzamos este comando que nos los actualizará todos de un tirón:
for i in `dir .`; do sed -i s/*:80/*:8080/g $i; echo $i; done
Menos mal, sino habría que cambiarlos a mano uno por uno :P.
Ahora instalamos nginx:
yum install nginx
y hacemos la primera configuración que nos permitirá, de momento, que sea proxy completo de Apache, es decir, que lo redirija todo a Apache.
Creamos el archivo /etc/nginx/proxy.conf
resolver 127.0.0.1; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 128m; client_body_buffer_size 256k; proxy_connect_timeout 60; proxy_send_timeout 60; proxy_read_timeout 60; proxy_buffer_size 32k; proxy_buffers 32 256k; proxy_busy_buffers_size 512k; proxy_temp_file_write_size 512k;
La primera línea será la ip del servidor DNS.
Ahora añadiremos a la configuración de nginx lo siguiente dentro del “server” por defecto.
/etc/nginx/nginx.conf
server{ listen 80; location / { proxy_pass http://$host:8080; include /etc/nginx/proxy.conf; }
El primer paso debería estar terminado. Levantamos los servicios de Apache y nginx y probamos a navegar por los virtual hosts del servidor. Si todo ha ido bien debería funcionar correctamente, pero si analizamos las cabeceras que se reciben en la respuesta, por ejemplo con el plugin livehttpheaders de Firefox, veremos como añade la siguiente línea a todas las solicitudes:
Server: nginx/0.8.53
Prueba 1 superada. Vamos ahora a configurar determinados virtual hosts para que el contenido estático lo sirva nginx.
/etc/nginx/conf.d/virtual.conf
server { listen 80; server_name www.tudominio.com; location ~* ^.+.(jpg|js|jpeg|png|ico|gif|txt|js|css|swf|zip|rar|avi|exe|mpg|mp3|wav|mpeg|asf|wmv|flv)$ { root /var/httpd/www.tudominio.com; expires 30d; } location / { proxy_pass http://www.tudominio.com:8080; include /etc/nginx/proxy.conf; } }
Con esto le decimos que todos los archivos estáticos los sirva nginx directamente y que las demás peticiones las redirija a Apache. Debemos crear un “server” por cada virtual host que queramos configurar de esta manera. Básicamente lo que hacemos es configurar la misma ruta en disco para los archivos estáticos que la que teníamos en Apache, de esta manera no tendremos que cambiar absolutamente nada. En muchos sitios proponen crear un host static.tudominio.com, pero esto implicaría modificar físicamente todas tus aplicaciones web, y eso no es lo que se pretende :P.
Sólo nos queda reiniciar nginx. ¿Cómo sabemos si funciona? Sencillo, revisando los logs de Apache y nginx. Si lo hacemos veremos cómo este último sirve los archivos estáticos y Apache todos los demás :).
Finalmente tendremos un pequeño problema. Desde Apache se verán todas las solicitudes HTTP como si vinieran de la propia máquina ya que, en efecto, vienen de nosotros mismos a través del proxy. Esto puede ser un problema para analizadores de tráfico pero también si tienes algún sistema que controla las ip’s de los usuarios. Para solucionarlo tenemos mod_rpaf para Apache (reverse proxy add forward module) que nos reemplazará las cabeceras adecuadamente de manera que la IP remota que veamos sea la de nuestro cliente. Lo descargamos, lo instalamos y lo configuramos:
/etc/httpd/conf.d/rpaf.conf
LoadModule rpaf_module modules/mod_rpaf-2.0.so RPAFenable On RPAFsethostname On RPAFproxy_ips 127.0.0.1 RPAFheader X-Forwarded-For
Con esto conseguiremos que se reemplace la IP del cliente que se recibe en Apache con la que viene en la cabecera “X-Forwarded-For” que es donde nginx nos deja la original.
Eso es todo. Si reiniciamos el servidor Apache veremos que en los logs comenzará a verse la IP real del usuario y no la de nuestra máquina local.
Como siempre, un último detalle. Integremos nginx con Cacti para hacer un seguimiento :).
Las plantillas están aquí, pero a mi no me funcionaron bien, las cambié un poco por estas otras.
Añadimos en la configuración inicial de nginx las siguientes líneas:
location /nginx_status { stub_status on; access_log off; allow IP_DE_TU_SERVIDOR_CACTI; deny all; }
Con esto podrás acceder al estado del servidor nginx desde http://IP_DE_TU_SERVIDOR/nginx_status.
Dentro del zip hay cuatro archivos, dos scripts perl que debes copiar en el directorio scripts de tu instalación de cacti y dos xml que debes importar desde la propia herramienta. Ya está todo preparado. Desde la configuración de cacti del servidor donde has instalado nginx añades las nueves fuentes de datos y los gráficos. Al configurarlo te pedirá la url de acceso que veíamos hace un momento.
Y hasta aquí hemos llegado. En muy poco tiempo hemos conseguido modificar toda la estructura de nuestro sistema web sin pérdida de disponibilidad y, lo mejor de todo, hemos creado un sistema mucho más eficiente y robusto. En condiciones de tráfico medio no notarás mucha diferencia de rendimiento, pero tendrás un sistema mucho más preparado para hacer frente a picos y efectos “barrapunto” :).
Un comentario en “Modificando la infraestructura web de un servidor con Nginx para servir contenido estático y como proxy de Apache”