viernes, 11 de febrero de 2011

Unir Apache HTTPD y Tomcat mediante un reverse proxy

Apache HTTPDApache Tomcat

Al diseñar el despliegue de una aplicación es habitual hacer que un servidor web devuelva el contenido estático de la página web o de la aplicación. Esto nos puede interesar por varias razones, una de ellas es que quita la carga de trabajo correspondiente al servir el contenido estático al servidor de aplicaciones que dependiendo de la aplicación o página web puede ayudar a aliviar su carga. Pero también puede ser usado para realizar balanceo de carga entre varios servidores de de aplicaciones, cacheo o para acceder a un servidor de nuestra intranet que está detrás de un firewall y no es accesible directamente por los usuarios de internet. El esquema es el siguiente:


Viendo un gráfico se entiende mejor ¿verdad?. En él tenemos el cliente que realiza la petición, el servidor web que se encargará de devolver los recursos estáticos (en la figura denominado reverse proxy) y finalmente el servidor de aplicaciones que se encargará de generar el contenido dinámico de la aplicación (posiblemente accediendo a un servidor de base de datos que en el gráfico no se muestra, en la figura denominado Web1 y Web2). En un primer paso el cliente realiza una petición que llega al servidor web, este determina si el recurso solicitado lo puede servir él o lo tiene que delegar en el servidor de aplicaciones. En el caso de que el servidor web tenga que delegar la petición en el servidor de aplicaciones el servidor web realiza un petición al servidor de aplicaciones solicitando el recurso que no puede servir él. Una vez que el servidor de aplicaciones genera el recurso se lo devuelve al servidor web que a su vez este se lo devuelve al cliente. Para el cliente todo este proceso es transparente, es como si todos los recursos los pudiese servir el servidor web. En este esquema el servidor web actúa de proxy para el servidor de aplicaciones (tambien se llama reverse proxy).

¿Como se configura Apache HTTPD para actuar en modo reverse proxy?

En Apache para delegar las peticiones debemos utilizar las directivas ProxyPass, ProxyPassReverse, ProxyRequests y ProxyPreserveHost. En el primer parámetro de la directiva ProxyPass indicamos las URL que delegamos (/) y en el segundo parámetro indicamos la dirección del servidor de aplicaciones que devolverá realmente el contenido de la petición (ajp://localhost:8009/, ajp es un protocolo específico para comunicarse con Tomcat, también podríamos haber utilizado el protocolo http con http://localhost:8080/, que es lo que haremos si utilizamos otro servidor web que no sea Apache). Para que el servidor web devuelva los contenidos estáticos de la aplicación debemos indicarle a Apache que las peticiones que empiecen por /static/ no las delegue en el servidor de aplicaciones sino que las procese él, esto lo conseguimos con la directiva ProxyPass pero donde pondríamos la dirección del servidor de aplicaciones ponemos un !. En el siguiente ejemplo de configuración para Apache he definido un host virtual basado en nombres.

Descomentamos las siguientes líneas en el archivo httpd.conf:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_ajp_module modules/mod_proxy_http.so

# Virtual hosts
Include conf/extra/httpd-vhosts.conf

Lineas a añadir en el archivo httpd-vhosts.conf:

<virtualhost *:80>
 ServerName www.dominio.com
 ServerAdmin admin@dominio.com
 DocumentRoot "/apache/www"
 ErrorLog "logs/www.dominio-error.log"
 CustomLog "logs/www.dominio-access.log" common

 ProxyRequests Off
 ProxyPreserveHost On

 ProxyPass /static/ !
 ProxyPass / ajp://localhost:8009/
 ProxyPassReverse / ajp://localhost:8009/
 
 <directory />
  Order deny,allow 
  Allow from all 
 </directory>
 
 Alias /static/ "/apache/www/"
</virtualhost>

Ciertas aplicaciones del servidor de aplicaciones pueden necesitar conocer la máquina original que realizó la petición (hay que darse cuenta que para el servidor de aplicaciones todas las peticiones provienen del servidor web) esto se consigue con la directiva ProxyPreserverHost, de esta forma el servidor de aplicaciones podrá conocer cierta información del origen real de la petición.

Según esta configuración la aplicación en el Tomcat esta desplegada en el contexto ROOT y accederíamos a ella con http://www.midominio.com/. Este es un aspecto importante y que en pocos sitios se comenta, el contexto de la aplicación en Apache y en el servidor de aplicaciones ha de coincidir sino las URLs que devuelva el servidor de aplicaciones en el contenido no van a ser utilizables por el cliente. Más especificamente no va a funcionar lo siguiente:

ProxyPass / ajp://localhost:8009/aplicacion
ProxyPassReverse / ajp://localhost:8009/aplicacion

En todo caso deberiamos indicar /aplicacion como URL a delegar y desplegar la aplicación en Tomcat en el contexto /aplicacion:

ProxyPass /aplicacion ajp://localhost:8009/aplicacion
ProxyPassReverse /aplicacion ajp://localhost:8009/aplicacion

Si nuestro servidor web es Apache HTTPD y nuestro servidor de aplicaciones es Tomcat en vez de utilizar un reverse proxy también se puede utilizar el conector mod_jk para conectarlos pero he preferido utilizar esta forma de reverse proxy ya que el conector mod_jk no está disponible para otros servidores web y servidores de aplicaciones, es decir, el este concepto de reverse proxy es más universal y normalmente es implementado por la mayoría de servidores web lo que nos permite poder cambiar de servidor web fácilmente en caso de que quisiéramos. Podríamos cambiar más fácilmente Apache HTTPD por cherokee, lighttpd o nginx que son utilizados hoy día en importantes webs de internet.

Referencia:
Reverse proxy (wikipedia)