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 8080De 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.jspAsí 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/ROOTPara evitar que nos muestre la página de administración de Tomcat, tenemos varias soluciones, vamos a ir viendo cada una de ellas.
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
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
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.xmlA continuación crearemos el directorio appBase con sus subdominios
...
<Host name="app1.sfchildren.com" appBase="vdomains/app1.sfchildren.com" />
<Host name="app2.sfchildren.com" appBase="vdomains/app2.sfchildren.com" />
...
:wq
$ mkdir -p $TOMCAT_HOME/vdomains/app1.sfchildren.comY creamos las aplicación <ROOT> con nuestra HelloWorld -como hemos visto antes-
$ mkdir -p $TOMCAT_HOME/vdomains/app2.sfchildren.com
$ mkdir $TOMCAT_HOME/vdomains/app1.sfchildren.com/ROOTA 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.
$ 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
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
hola! estudio programacion orientada a objetos, quiza me puedas ayudar, necesito saber si Tomcat es una aplicacion "standalone"?? gracias!
ResponderEliminarSaludos desde Mexico!
Luis.
Hola Luis,
ResponderEliminarSi. 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
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.
ResponderEliminarSaludos!
Luis
Hola Estimado Benito,
ResponderEliminarestoy leyendo tu post, para un servidor con mas de 60 aplicaciones recomiendas un virtualhost por cada aplicación?
saludos!!
Hola Estimado Benito,
ResponderEliminarTengo 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!!
Hola Wualfred,
ResponderEliminarBueno, 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
Buenas,
ResponderEliminarHe 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
Hola Sandra,
EliminarAsí 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
Hola Benito.
ResponderEliminarsoy 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
Hola Duvan,
EliminarLa 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