22 de febrero de 2011

Instalación de un Servidor de Desarrollo - Parte 4: Conexión Apache + Tomcat

En el artículo anterior de la serie de la instalación de un servidor de desarrollo vimos cómo instalar y configurar el Servidor HTTP Apache versión 2.2 y el contenedor de Servlets y JSPs Apache Tomcat versión 7.0.

Ahora veremos cómo configurarlos para que el servidor Apache sirva como punto de entrada de las peticiones que se realicen al Tomcat. De esta forma podremos tener sitios estáticos y sitios dinámicos y no necesitaremos entrar por un puerto especial para unos y por otro para otros. Además posteriormente veremos cómo esto nos puede servir para que Apache sirva todos los recursos estáticos, como imágenes, archivos javascript, archivos css, etc., y que Tomcat sirva los recursos dinámicos.

Además también nos permitirá hacer algo más importante, conocido como "balanceo de carga", que nos permite distribuir nuestra aplicación en N servidores y el usuario no sabrá quién atiende la petición. O si uno de nuestros servidores se cae otros podrán atender la petición.

Comencemos pues con el tema del artículo.

Teniendo nuestros servidores configurados y corriendo debemos usar un conector para realizar el puenteo entre ambos. Existen varios conectores disponibles, pero el más conocido (y fácil de configurar) es el conector "mod_jk".

Lo primero que haremos es instalar algunas herramientas y módulos de Linux que necesitaremos para el proceso, el primero es instalar "libtool", "automake"


apt-get install libtool
apt-get install automake


Ahora descargaremos la versión más actual (en este momento la 1.2.32) del código fuente del conector, usando el comando "wget" (recuenden hacer esto en el directorio "/usr/src"):


cd /usr/src
wget http://www.apache.org/dist/tomcat/tomcat-connectors/jk/source/jk-1.2.31/tomcat-connectors-1.2.31-src.tar.gz


Una vez realizada la descarga descomprimimos el archivo "tomcat-connectors-1.2.31-src.tar.gz"


tar -xzf tomcat-connectors-1.2.31-src.tar.gz


Cuando el archivo se termine de descomprimir entramos el directorio que se creó "tomcat-connectors-1.2.31-src", y después al directorio "native":


cd tomcat-connectors-1.2.31-src/native


Una vez en este directorio realizamos la configuración del conector, ejecutando el script "buildconf.sh":


./buildconf.sh




Configuraremos nuestro conector con el comando "configure". Pasaremos como parámetro del comando el directorio en el que tenemos instalado nuestro servidor Apache (que en mi caso es "/usr/local/apache2"):


./configure --with-apxs=/usr/local/apache2/bin/apxs 


Una vez terminado el proceso de configuración volveremos a ver la línea de comandos. Ahora usaremos el comando "make" para compilar el código fuente:


make


Finalmente realizaremos la instalación de nuestro conector usando el comando "make install":


make install clean


Cuando se termine la instalación deberemos ver un archivo llamado "mod_jk.so" en el directorio "/usr/local/apache2/modules":



Ahora nos dirigimos al directorio "/usr/local/apache2/conf" y creamos un archivo llamado "workers.properties", usando el comando "touch":


cd /usr/local/apache2/conf
touch workers.properties


El archivo "workers.properties" es el archivo de configuración de los workers del Tomcat. Un worker del Tomcat es una instancia del Tomcat que está esperando para ejecutar Servlets sobre algún servidor web. En este caso tendremos al servidor Apache enviando las peticiones que nuestros usuarios hagan a algún Servlet hacia el proceso del Tomcat (el worker). De hecho podríamos configurar múltiples workers de Tomcat para servir los servlets detrás de cierto servidor web. Este archivo está conformado por un conjunto de directivas que indican la forma en la que cada worker debe trabajar.

Lo primero que debemos hacer es indicar la ruta donte esta instalado el Tomcat que responderá a las peticiones, usando la directiva siguiente:


workers.tomcat_home


En nuestro caso el Tomcat se encuentra en el directorio "/usr/local/tomcat". La otra directiva importante es:


workers.java_home


Que indica la ruta en el que se encuentra nuestro jdk (en nuestro caso es "/usr/java/jdk").

Hacemos referencia a cada uno de nuestros workers, usando un nombre (que nosotros mismos les asignamos). Cada directiva de configuración consiste de tres palabras separadas por un punto:


worker.<nombre_del_worker>.<directiva> = <valor>


También podemos definer variables usando la sintaxis:


<nombre_de_la_variable> = <valor>


A la que posteriormente podemos hacer referencia de esta manera:


$(nombre_de_la_variable)


Lo primero que haremos es indicar la lista de workers que tendrá nuestro archivo. Esto lo hacemos con la siguiente directiva:


worker.list=default


En donde "default" es el nombre del único worker que configuraré en el archivo. Si tuviéramos más, debemos separar cada uno de los nombres de nuestros workers usando comas.

Ahora debemos indicar de qué tipo será nuestro worker, el tipo de worker que usemos definirá las directivas que podemos configurar en este. En realidad solo podemos elegir uno de 4 tipos:
  • ajp13
  • ajp14
  • lb
  • status

El worker tipo "status" es un worker especial, el cual no envía las peticiones al Tomcat, sino que nos permite el recuperar información de configuración y de estatus en tiempo de ejecución, y también modificar algunos de los elementos de configuración de forma dinámica. Hablaremos más de este worker cuando veamos balanceo de carga en unos cuantos tutoriales.

El worker "lb" es usado para balanceo de carga (load balancing) y este no se comunica con los workers del Tomcat, sino que más bien se encarga de administrarlos. También hablaremos más de este worker cuando hablamos sobre balanceo de carga.

El tipo de worker preferido para este tipo de configuración es el "ajp13" ("Apache JServ Protocol 1.3"), el cual, como su nombre lo indica, es un protocolo de comunicación que está orientado a paquetes. Aunque existe (en teoría) un "ajp14", este parece no estar en uso, además de que no hay ningún tipo de documentación para este, por lo que evitaremos su uso:


worker.default.type=ajp13


Lo siguiente que debemos hacer es indicar el factor de balanceo de carga, o cuanto trabajo esperamos que realice este worker (en términos más claros es la prioridad de este workers sobre los demás que tengamos configurados). Como no hay otro worker el factor de trabajo de "default" será de 1:


worker.default.lbfactor=1


Finalmente debemos indicar el host y el puerto por el que nos comunicaremos al Tomcat que responderá a las peticiones que se hagan a este worker (tengan cuidado, porque el puerto por el que responde el worker no es el mismo por el que hacemos una petición para una aplicación). Por default (si no modificamos la configuración de nuestro Tomcat) el puerto para los workers de tipo "ajp13" es el "8009". Nuestra configuración final debe quedar de esta forma:


workers.tomcat_home=/usr/local/tomcat
workers.java_home=/usr/java/jdk

worker.list=default

worker.default.type=ajp13
worker.default.lbfactor=1
worker.default.port=8009
worker.default.host=localhost




Si quieren ver algunas otras opciones de configuración pueden hacerlo en la página oficial del conector.

Ahora editaremos el archivo de configuración del servidor Apache. Algunas cosas las colocaremos en este archivo y otras en un archivo de configuración externo (podríamos poner todo en este archivo, pero trataremos de tener un poco de orden y separaremos las cosas especificas de otros módulos en archivos separados).

Editamos el archivo "httpd.conf" del directorio "/usr/local/apache2/conf". Buscamos la línea que dice:


# LoadModule foo_module modules/mod_foo.so


Y debajo la última línea que diga "LoadModule" (si es que tienen alguna) colocamos lo siguiente:


LoadModule jk_module modules/mod_jk.so


Debajo de esta línea indicaremos que usaremos un archivo de configuración externo, usando la directiva "Incluide":


Include conf/tomcat.conf




Finalmente buscamos la línea que dice:


DirectoryIndex index.html


Y la modificamos para que quede asi:


DirectoryIndex index.html index.jsp


Guardamos y cerramos este archivo. Creamos, en el directorio "/usr/local/apache2/conf", un archivo llamado "tomcat.conf":


touch tomcat.conf


Este archivo nos permitirá configurar el módulo "mod_jk", el cual es el conector del Tomcat. Editamos este archivo y colocamos las directivas que le indicarán al servidor Apache cómo usar este módulo.

No entraré en detalle de las opciones que colocaremos, ya que muchas son bastante intuitivas; solo diré que la directiva "jkWorkersFile" nos indica la ubicación del archivo donde están definidos los workers del Tomcat (a partir de la raíz del Apache), y la directiva "jkLogFile" indica el lugar en el que se crean los archivos de log relacionadas con el conecto (también a partir de la raíz del Apache). Si quieren ver qué otras directivas existen para la configuración, pueden revisar la guía de referencia de configuración para el servidor Apache.

Al final el archivo "tomcat.conf" debe tener el siguiente contenido:


JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"


Con esto ya tenemos configurado el conector. Ahora lo que nos falta hacer es decirle al Apache cuáles peticiones deben ser enviadas al Tomcat. Para esto usamos la directiva "JkMount", e indicamos las peticiones a las que debe responder el Tomcat. Por ejemplo si indicamos:


JkMount /examples/* default


Le decimos al Apache que todas las peticiones que lleguen para el contexto "examples" deberá ser servido por el worker "default". Con esto la petición será enviada directo al Tomcat. Este atenderá la petición y nos dará la respuesta que será mostrada en el navegador. Cada vez que queramos agregar una nueva aplicación para que sea servida por nuestro servidor Apache, esta tendrá que ser agregada a este archivo de configuración. Por ejemplo, si tenemos una aplicación que está en el contexto "registro" deberemos agregar la siguiente línea:


JkMount /registro/* default


Por lo que cada vez que queramos agregar una aplicación al Tomcat y que esta sea atendida por el Apache deberemos agregar una nueva directiva "jkMount". Nosotros colocaremos la siguiente en el archivo de configuración para comprobar que todo funciona correctamente:

JkMount /examples/* default


Ya que el Tomcat por default trae una aplicación "examples" instalada. Probamos que efectivamente esto esté funcionando como debe. Guardamos y cerramos el archivo.

Ahora asignamos los dos archivos de configuración que acabamos de agregar ("workers.properties" y "tomcat.conf") al usuario "apache2", y damos permisos completos al dueño, de lectura y ejecución para los usuarios del mismo grupo, y nada para otros:


chown apache2 tomcat.conf
chown apache2 workers.properties

chmod 750 tomcat.conf
chmod 750 workers.properties




Reiniciamos el servidor Apache:


/etc/init.d/apache2 restart


Abrimos un navegador y escribimos en la barra de direcciones la IP de nuestro servidor y accederemos, desde el Tomcat, a la aplicación "examples".


http://miniServer:8080/examples


Y debemos ver más o menos el siguiente contenido:



Como podemos ver, el Tomcat está respondiendo correctamente, hasta aquí nada nuevo; pero como lo que queremos probar es que sea Apache quien nos devuelva la página eliminamos el ":8080" de la dirección en nuestro navegador:


http://miniServer/examples


Con lo que veremos:



Como podemos ver, estamos recibiendo la misma página, con la diferencia de que en nuestro puerto 80 (el puerto default) quien atiende es el servidor Apache, y no el Tomcat.

Con esto podemos comprobar que la configuración ha funcionado correctamente. Sin embargo, aún podemos realizar otra configuración extra para que sea el servidor Apache el que sirva los recursos estáticos, como imágenes, hojas de estilo, html estáticos, y que sea el Tomcat quien sirva los recursos dinámicos. Para entender cómo funciona esto, vayamos por pasos.

¿Recuerdan que en el archivo de configuración "tomcat.conf" indicamos las aplicaciones que queremos "montar" para que sea Apache quien las sirva? Esto lo hacemos con la directiva "JkMount". Por ejemplo, cuando montamos la aplicación "examples" lo hicimos de esta forma:


JkMount /examples/* default


Pues también existe una directiva para indicar que no queremos montar ciertos archivos o directorios, esta instrucción es "JkUnMount" y le indicamos qué archivos queremos que no sean servidos por un worker particular. Por ejemplo, si queremos excluir todas las imágenes ".gif", de todas las aplicaciones, para que estas no sean regresadas por Tomcat, la instrucción quedaría de la siguiente forma:

JkUnMount /*.gif default


Si agregamos la línea anterior al archivo de configuración "tomcat.conf" y reiniciamos nuestro servidor Apache. Cuando entremos nuevamente a la aplicación "examples" (a alguna de las paginas internas, ya que la principal no tiene ninguna imagen) veremos que ya no se ve ninguna de las imágenes que tiene:



Mientras que si nos conectamos por el puerto 8080 las seguiremos viendo:



Pero ¿cómo hacemos ahora para que Apache devuelva las imágenes? Pues bien, para esto usamos una directiva llamada "Alias". Esta instrucción nos permite crear un directorio virtual el cual, como si nombre lo indica, es un directorio que se encuentra en un directorio distinto de nuestro sistema de archivos. Esto nos permite que el servidor Apache entregue archivos que no se encuentran en su directorio raíz (el directorio "htdocs"). En este caso haremos que nuestro directorio virtual para la aplicación "examples" sea la ruta en la que se encuentra la aplicación dentro del Tomcat "/usr/local/tomcat/webapps/examples", aunque podría estar en cualquier directorio de nuestra computadora donde el usuario Apache tenga permisos de lectura.

Alias /examples "/usr/local/tomcat/webapps/examples"


Para cada "Alias" se debe definir una sección "Directory" que cubre los directorios destino de los alias, permitiendo el acceso a dichos directorios. En ellos definimos la ruta absoluta del directorio que contiene los recursos estáticos, en este caso "/usr/local/tomcat/webapps/examples". Estas secciones definen algunos parámetros, no entraré en detalles, pero básicamente le estamos dando permisos al Apache para que regrese todos los archivos del directorio "examples" del Tomcat:


<Directory "/usr/local/Tomcat/webapps/examples">
    Options FollowSymLinks
    AllowOverride None
    Allow from all
</Directory>


Ahora, en realidad no queremos que el Apache regrese "todos" los archivos de esta aplicación (si, ya lo sé, parece que no sabemos tomar decisiones) ya que el directorio "WEB-INF" debe ser privado porque contiene nuestros archivos de configuración, clases, librerías, etc. Así que excluimos este directorio, usando otra sección "Directory":


<Directory "/usr/local/Tomcat/webapps/examples/WEB-INF">
    AllowOverride None
    deny from all
</Directory>


Al final, nuestro archivo de configuración "tomcat.conf" queda de la siguiente forma:

JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel debug
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat ForwardDirectories
JkRequestLogFormat "%w %V %T"

JkMount /examples/* default
JkUnMount /*.gif default

Alias /examples "/usr/local/Tomcat/webapps/examples"
<Directory "/usr/local/Tomcat/webapps/examples">
    Options FollowSymLinks
    AllowOverride None
    Allow from all
</Directory>
<Directory "/usr/local/Tomcat/webapps/examples/WEB-INF">
    AllowOverride None
    deny from all
</Directory>


Ahora volvemos a reiniciar nuestro servidor Apache:


service apache2 restart


Y cuando actualicemos nuestra página ahora deberemos ver las imágenes, pero ahora serán regresadas por Apache y no por el Tomcat (y obviamente no nos daremos cuenta de quién lo está regresando, que es exactamente lo que queremos ^_^):



Bien, con esto concluimos este pequeño tutorial, de la serie de configuración de nuestro servidor de desarrollo, en el que vimos cómo integrar dos servidores (Apache 2.2 y Tomcat 7) para que uno sirva el contenido estático y otro el contenido dinámico, y de esta forma reducir la carga de ambos servidores.

No olviden dejar sus dudas, comentarios, correcciones, y sugerencias.

Saludos.

Entradas Relacionadas: