SafeChildren Banner

Havoc Oracle Solaris Experts

miércoles, 14 de abril de 2010

Configurar Virtual Hosts en Tomcat 6 y Contextos de Aplicación - Parte 1

Introducción
En esta serie de artículos, vamos a ver las diferentes formas que hay para definir una estructura de Apache Tomcat para su puesta en producción. Veremos cómo podemos configurar Apache Tomcat con Apache HTTP mediante <mod_jk> y cómo poner <squid-cache> como frontend haciendo de proxy reverso.

Como es una serie y no un unico post iremos viendo cómo montar la infraestructura, desde lo más básico, hasta -en las últimas entregas- cómo ponerlo en producción.

Instalar Apache Tomcat "Stand Alone"
Uno de los problemas con los encontramos al montar Apache Tomcat, es que si no queremos ejecutarlo con root debemos levantarlo en un puerto superior a 1024. Esto hace que tengamos que utilizar ipfilter para realizar un redirect, por ejemplo:
rdr ed0 0.0.0.0/0 port 80 -> 127.0.0.1 port 8080
De esta forma, hacemos que la peticiones a nuestro puerto http(80) sean redireccionadas al puerto 8080 de localhost.

Con este sencillo paso, hemos conseguido publicar nuestro Apache Tomcat en el puerto http(80), sin embargo, esta configuración -así sola- no nos sirve de mucho, ya que principalmente no tenemos alta disponibilidad, balanceo, firewall, virtual hosts. Además, se incluye el problema del contexto.

Contextos de Aplicaciones Web
El contexto de una aplicación Web es -en casos generales- el nombre de la aplicación, por ejemplo, si tenemos la aplicación web llamada HelloWorld.war, su contexto será <HelloWord>, es decir, la URL resultante será:
http://www.sfchildren.com/HelloWord/index.jsp
Así que tendremos tantos contextos como aplicaciones. Sin embargo, si tecleamos la URL
http://www.sfchildren.com/
Nuestro tomcat utilizará una aplicación expecífica llamada ROOT -que en la instalación por defecto será el manager-

# ls -l $TOMCAT_HOME/webapps/ROOT
total 256
-rw-r--r--   1 webrunner www         5866 may 14  2009 asf-logo-wide.gif
-rw-r--r--   1 webrunner www         3376 may 14  2009 build.xml
-rw-r--r--   1 webrunner www        21630 may 14  2009 favicon.ico
-rw-r--r--   1 webrunner www         7777 may 14  2009 index.html
-rw-r--r--   1 webrunner www         8307 may 14  2009 index.jsp
-rw-r--r--   1 webrunner www         7317 may 14  2009 RELEASE-NOTES.txt
-rw-r--r--   1 webrunner www         2324 may 14  2009 tomcat-power.gif
-rw-r--r--   1 webrunner www         1934 may 14  2009 tomcat.gif
-rw-r--r--   1 webrunner www        65643 may 14  2009 tomcat.svg
drwxr-xr-x   2 webrunner www          512 jun 16  2009 WEB-INF
Para evitar que nos muestre la página de administración de Tomcat, tenemos varias soluciones, vamos a ir viendo cada una de ellas.

Crear un <index.html> con un redirect a la página correcta
Por ejemplo, podemos crear una página <index.html> con un pequeño script en JavaScript que nos mande un 301 a la nueva dirección. Siguiendo con el ejemplo,
$ cd $TOMCAT_HOME/webapps/ROOT
$ vi index.html
  <HTML>
  <HEAD>
  <META
     HTTP-EQUIV="Refresh"
     CONTENT="0; URL=http://www.sfchildren.com/HelloWord/index.jsp">
  </HEAD>
  <BODY>
  </BODY>
  </HTML>
:wq

Problemas de esta solución
Bueno, no hace falta decir que si tenemos varias aplicaciones en el mismo tomcat sólo podremos hacer un redirect a una de ellas, además de no tener soporte para VirtualHosts


Sustituir la aplicación <ROOT> por nuestra <HelloWord>
Ya hemos comentado antes que tomcat tiene definida una aplicación por defecto llamada <ROOT> la cual hace referencia a la aplicación sin contexto, podemos por lo tanto, sustituir esta por nuestra aplicación <HelloWord>, veamos un ejemplo -imaginamos que tenemos la aplicación HelloWorld.war en nuestro HOME-
$ cd $TOMCAT_HOME/webapps/ROOT
$ jar xvf $HOME/HelloWorld.war
$ ls -l
total 258
-rw-r--r--   1 webrunner www          148 abr 14 11:35 index.html
drwxr-xr-x   2 webrunner www          512 jun 16  2009 WEB-INF

Qué hemos conseguido
Al hacer este cambio, hemos hecho que el contexto <HelloWord> no sea necesario, y por lo tanto, nuestra URL ahora será la siguiente:
http://www.sfchildren.com/


Problemas de esta solución
Al igual que sucedía antes, seguimos necesitando una instalación de tomcat por aplicación y por lo tanto, muchos sistemas a administrar. Además, no tenemos VirtualHosts ...


Creación de VirtualHosts en Tomcat6
Para solucionar estos problemas tenemos los VirtualHost que -de forma sencilla- hacen una discriminación de la petición en función del valor de la cabecera HOST de cada petición http, por lo tanto, lo que nosotros queremos es que las peticiones a:
  • http://app1.sfchildren.com/ sean enviadas a HelloWorld1
  • http://app2.sfchildren.com/ sean enviadas a HelloWorld2
La activación de los virtualhost en Tomcat6 no requiere de grandes configuraciones, tan sólo es necesario modificar un par de archivos de configuración, es muy importante que el tomcat esté detenido antes de comenzar

Lo primero que debemos hacer es dar de alta los hosts en el archivo de configuración del servidor <$TOMCAT_HOME/conf/server.xml> en el apartado de <Engine>. El formato del tag es:
<Host name="_nombre_vhost_"     appBase="_path_to_base_dir" />

En nuesto ejemplo, crearemos <app1> y <app2> como virtual hosts
$ vi $TOMCAT_HOME/conf/server.xml
    ...
        <Host name="app1.sfchildren.com"      appBase="vdomains/app1.sfchildren.com" />
        <Host name="app2.sfchildren.com"      appBase="vdomains/app2.sfchildren.com" />
   ...

:wq
A continuación crearemos el directorio appBase con sus subdominios
$ mkdir -p $TOMCAT_HOME/vdomains/app1.sfchildren.com
$ mkdir -p $TOMCAT_HOME/vdomains/app2.sfchildren.com
Y creamos las aplicación <ROOT> con nuestra HelloWorld -como hemos visto antes-

$ mkdir $TOMCAT_HOME/vdomains/app1.sfchildren.com/ROOT
$ mkdir $TOMCAT_HOME/vdomains/app2.sfchildren.com/ROOT
$ cd $TOMCAT_HOME/vdomains/app1.sfchildren.com/ROOT
$ jar xvf $HOME/HelloWorld1.war
$ cd ../app2.sfchildren.com/ROOT
$ jar xvf $HOME/HelloWorld2.war
A continuación, levantaremos el tomcat con svc y con un navegador nos pondremos en cada una de las diferentes URL para ver cómo vamos a una u otra.

Un concepto muy importante a tener en cuenta es el uso de resources dentro de los contextos de aplicaciones, es decir, el acceso mediante JNDI a un DataSource. Para que el procedimiento de virtualhost de tomcat no nos amargue la vida con problemas, deberemos pedir que los aplicativos (war) tengan definido su context.xml dentro de la carpeta <WebAPP/META-INF/>, esto nos hará los despliegues en virtualhost mucho más sencillos
$ ls -l $TOMCAT_HOME/vdomains/app1.sfchildren.com/ROOT/META-INF/
total 4
-rw-r--r--   1 webrunner www          536 feb 28 22:51 context.xml
-rw-r--r--   1 webrunner www          102 abr 13 00:02 MANIFEST.MF

Problemas de esta Solución
Claro, no todo puede ser tan bonito. Hemos solucionado el problema del contexto y de tener varias aplicaciones web sobre el mismo servidor Tomcat, sin embargo, tenemos algunas carencias que no podemos cubir: Balanceo de Carga -Alta disponibilidad-, URL Rewrite, Firewall L7

Conclusiones
En esta primera parte de Puesta en producción de Apache Tomcat, hemos visto cómo podemos utilizar los VirtualHosts para solucionar los problemas a múltiples aplicaciones dentro del mismo tomcat. Sin embargo, sólo con apache tomcat no podemos ofrecer las soluciones a todos los problemas que se nos presentan -alta disponibilidad, URL rewrite-.

En la próxima entrega veremos cómo incluir Apache HTTPD con su módulo <mod_jk> el cual nos permitirá  conectarnos a tomcat y realizar tareas de balanceo, standby, ... hasta entonces, esperar


Referencias

10 comentarios:

  1. hola! estudio programacion orientada a objetos, quiza me puedas ayudar, necesito saber si Tomcat es una aplicacion "standalone"?? gracias!

    Saludos desde Mexico!
    Luis.

    ResponderEliminar
  2. Hola Luis,

    Si. Apache Tomcat es un Servidor JSP+Servlet "StandAlone", realmente es como normalmente se monta -llamando a $TOMCAT_HOME/bin/startup- además, es posible configurarlo como parte de un cluster, o como parte de balanceadores.

    Espero haber solucionado tus dudas, sin embargo, no entiendo muy bien la duda.

    Quieres "meterlo" dentro de una aplicación diseñada por tí como servidor "embebido"?

    Si es eso, puedes mirar Jetty, que puede ser "standalone" o "embedded".

    Un Saludo,
    Urko

    ResponderEliminar
  3. Ok! muchas gracias! me sirvio mucho,conozco C++ pero y apenas estoy aprendiendo a programar en Java, como veras ando en pañales en esto de la programacion jejeje.


    Saludos!
    Luis

    ResponderEliminar
  4. Hola Estimado Benito,

    estoy leyendo tu post, para un servidor con mas de 60 aplicaciones recomiendas un virtualhost por cada aplicación?

    saludos!!

    ResponderEliminar
  5. Hola Estimado Benito,

    Tengo un servidor con más de 60 aplicaciones en Linux RedHAt, con tomcat 6 con las configuraciones por defecto, tengo el problema que se le acaban los recursos al Tomcat, ¿Me recomiendas instalar cada uno como VirtualHost?

    de antemano muchas gracias, saludos!!

    ResponderEliminar
  6. Hola Wualfred,

    Bueno, el tema de la configuración multihost y el consumo de recursos es un poco complicado, pero vamos a ver si te puedo "echar una mano":

    1.- Si utilizas mod_rewrite y proxy reversos es mejor que dejes Tomcat sin VirtualHosts

    2.- Si no utilizas rewrite y proxy reversos, entonces puede incluir los VirtualHosts para mejorar en seguridad y simplificar la configuración


    Con el tema de los recursos, te recomiendo que empieces primero con experimentar con las opciones de -Xms -Xmx para asignar memorias mínimas (-Xmx) y máxima (-Xmx) a la máquina virtual (Recuerda que si es una máquina de 32bis no puedes tener más de 3,5Gb de RAM en la apertura -más o menos-)

    Luego, deberías empezar a modificar parámetros de TimeOut y Threads en Tomcat.

    Separarlo por VirtualHost te permitirá crear más instancias de Tomcat y conseguir un mejor uso de la memoria.

    Ya me contarás cómo va,

    Un Saludo,
    Urko

    ResponderEliminar
  7. Buenas,
    He seguido todos los pasos de tu explicación para crear varios virtualhost en Tomcat y no he conseguido hacerlo funcionar.
    No sé qué he hecho mal, pero me aparecen varios errores en los logs y no funcionan los virtualhost.

    Jan 24, 2012 12:54:39 PM org.apache.catalina.startup.Catalina load
    WARNING: Catalina.start using conf/server.xml:
    java.lang.IllegalArgumentException: addChild: Child name 'aplicacion1.com' is not unique
    at org.apache.tomcat.util.digester.Digester.createSAXException(Digester.java:2806)
    at org.apache.tomcat.util.digester.Digester.createSAXException(Digester.java:2832)
    at org.apache.tomcat.util.digester.Digester.endElement(Digester.java:1141)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
    at com.sun.org.apache.xerces.internal.parsers.AbstractXMLDocumentParser.emptyElement(AbstractXMLDocumentParser.java:180)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:1343)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2755)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
    at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
    at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
    at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
    at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
    at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
    at org.apache.tomcat.util.digester.Digester.parse(Digester.java:1642)
    at org.apache.catalina.startup.Catalina.load(Catalina.java:510)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:568)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
    Caused by: java.lang.IllegalArgumentException: addChild: Child name 'aplicacion1.com' is not unique
    at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:781)
    at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
    at org.apache.catalina.core.StandardEngine.addChild(StandardEngine.java:262)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.apache.tomcat.util.IntrospectionUtils.callMethod1(IntrospectionUtils.java:928)
    at org.apache.tomcat.util.digester.SetNextRule.end(SetNextRule.java:193)
    at org.apache.tomcat.util.digester.Rule.end(Rule.java:229)
    at org.apache.tomcat.util.digester.Digester.endElement(Digester.java:1138)
    ... 20 more
    Jan 24, 2012 12:54:39 PM org.apache.catalina.startup.Catalina start
    SEVERE: Cannot start server. Server instance is not configured.

    Si pudieras echarme una mano te estaría muy agradecida.

    Saludos,
    Sandra

    ResponderEliminar
    Respuestas
    1. Hola Sandra,

      Así según los logs que has puesto, parece que el nombre de "application1" lo tienes asignado a más de un VirtualHost, ya que dice:

      Child name 'aplicacion1.com' is not unique

      Verifica que no tengas más de un VHost con ese nombre único,

      Un Saludo,
      Urko

      Eliminar
  8. Hola Benito.

    soy un poco nuevo en esto de configuración de servidores tomcat.
    y no se si me puedes resolver algunas inquietudes. Tengo una aplicación en tomcat 8 y quisiera ocultar el contexto de la aplicación, no se de que forma puedo hacer esto, ya que la aplicación es bastante grande y no veo viable desplegarla en el ROOT porque dentro de la aplicación misma los enlaces hacen uso del contexto

    No se si es posible con un virtualHost o de alguna otra manera

    agradezco la ayuda

    ResponderEliminar
    Respuestas
    1. Hola Duvan,

      La forma más sencilla -y la que actualmente es más recomendada- consiste en utilizar mod_proxy de Apache HTTP y configurar un VirtualHost que apunte al servidor Tomcat.

      Dicho de otra forma, creas un servidor virtual de Apache -en el puerto 80, por ejemplo- y configuras mod_proxy para que "reenvíe" las peticiones al servidor Tomcat,

      Un ejemplo para montar la App "WebApp" en el servidor Tomcat 192.168.1.200, pondríamos lo siguiente dentro del VirtualHost

      # Proxy
      ProxyRequests Off
      ProxyPreserveHost On

      <Proxy *>
      Order deny,allow
      Allow from all
      </Proxy>

      # Rewrite to Tomcat
      ProxyPass /WebApp http://192.168.1.200:8080/WebApp
      ProxyPassReverse /WebApp http://192.168.1.200:8080/WebApp

      Eliminar