Instalación y configuración de Tomcat en cluster con alta disponibilidad

En este artículo me gustaría explicar cómo instalar y configurar  tomcat en dos servidores distintos llamados master (192.168.1.1) y slave (192.168.1.2) con alta disponibilidad (Clúster) y replicación de sesión. Formare un clúster utilizando las IPs de cada uno de los servidores en lugar de utilizar direcciones de multidifusión como se hace habitualmente, este último método me parece más inseguro.

Para no tener problemas editamos la SELINUX a disabled y actualizar el sistema operativo (opcional):

vi /etcs/elinux/config

# This file controls the state of SELinux on the system.

# SELINUX= can take one of these three values:

#     enforcing - SELinux security policy is enforced.

#     permissive - SELinux prints warnings instead of enforcing.

#     disabled - No SELinux policy is loaded.

SELINUX= disabled

# SELINUXTYPE= can take one of three two values:

#     targeted - Targeted processes are protected,

#     minimum - Modification of targeted policy. Only selected processes are protected.

#     mls - Multi Level Security protection.

SELINUXTYPE=targeted

Actualizamos el sistema: yum update

Antes de comenzar la instalación realizamos en ambos servidores master y slave lo siguiente:

Recomiendo crear un usuario y grupo tomcat, no se recomienda usar el usuario root.

groupadd tomcat
useradd -M -s /bin/nologin -g tomcat -d /opt/tomcat tomcat

Comenzamos con la instalación y configuración de los servidores tomcat master 192.168.1.1 y slave 192.168.1.2, para ellos descargamos la última versión estable de la página oficial:

a

Creamos la ubicación /opt/tomcat para albergar tomcat y descomprimimos el fichero:

mkdir /opt/tomcat
tar xvf apache-tomcat-8.5.14.tar.gz -C /opt/tomcat --strip-components=1

Le asignamos permisos de propietario al usuario tomcat:

cd /opt/tomcat
chown -R tomcat /opt/tomcat

Como hemos descargado el software y no hemos utilizado ‘yum‘ o ‘apt get’ (dependiente de la distribución) para la instalación desde los repositorios, debemos crear un fichero de arranque y  que lo sepa leer systemd en /etc/systemd/system/tomcat.service.

Creamos dicho fichero:

# Systemd unit file for tomcat
[Unit]
Description=Apache Tomcat Web Application Container
After=syslog.target network.target

[Service]
Type=forking
Environment=JAVA_HOME=/usr/lib/jvm/jre
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/bin/kill -15 $MAINPID
User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target

Ahora nos toca indicar a systemd que utilice el nuevo fichero:

systemctl daemon-reload
systemctl start tomcat
#Lo añadimos al arranque
systemctl enable tomcat
#Podemos ver su estado
systemctl status tomcat

Se comprueba su funcionamiento: http://192.168.1.1:8080/ y http://192.168.1.2:8080/

b.PNG

Creamos los usuarios para la administración de tomcat por consola, para ello vamos /opt/tomcat/conf y editamos el fichero “tomcat-users.xml”. En este caso se crea el usuario prueba con contraseña Password01.

…
<user username="prueba" password="Password01" roles="manager-gui,admin-gui,manager-script,manager-jmx,manager-status"/>
…

Por defecto, tomcat tiene deshabilitada la administración web para todas las IPs salvo la propia de la máquina, debemos habilitar la administración web a través de otras IPs, para ello editamos los siguientes ficheros:

vi /opt/tomcat/webapps/host-manager/META-INF/context.xml

vi /opt/tomcat/webapps/host-manager/META-INF/context.xml

…
<Context antiResourceLocking="false" privileged="true" >
<!--  <Valve className="org.apache.catalina.valves.RemoteAddrValve"
         allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" /> -->
<Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>
…

Verificamos el acceso mediante administración: http://192.168.1.1:8080/manager/html y http://192.168.1.2:8080/manager/html

Configuración tomcat

Indicamos al servidor tomcat que utilice la  IP propia del servidor, además se le indica que las aplicaciones se desplieguen dentro de webapps:

Tomcat master

…
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
<Engine name="Catalina" defaultHost="192.168.1.1" jvmRoute="master">
…
…
<Host name="192.168.1.1"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
…

Tomcat slave

   …
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
<Engine name="Catalina" defaultHost="192.168.1.2" jvmRoute="salve">
…
…
<Host name="192.168.1.1"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
…

Clustering en tomcat

Ahora vamos procede a formar el clúster de tomcat, pero antes dentro de /opt/tomcat/conf creamos las carpetas, para el despliegue de las aplicaciones, asignado los permios a tomcat.

  • war-listen
  • war-temp
cd /opt/tomcat/
mkdir war-temp
mkdir war-listen

c.PNG

Se procede a modificar el fichero server.xml en ambos servidores presente en /opt/tomcat/conf:

Servidor Maestro (192.168.1.1)

Es importante que la configuración del cluster se encuentra dentro de <hosts>, se han observado problemas al introducir dicha configuración en otra ubicación del fichero.

watchEnabled=”true o false”:Define al “maestro y a los esclavos”, el maestro se encuentra en true y lo esclavos en false.

…
<Host name="192.168.1.1"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

<!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->
<!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="/var/log/tomcat"
               prefix="localhost_access_log" suffix=".txt"
               pattern="combined"/>

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"  channelSendOptions="6" channelStartOptions="3">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
 expireSessionsOnShutdown="false" notifyListenersOnReplication="true" />
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
 autoBind="0" selectorTimeout="5000" maxThreads="6" address="192.168.1.1"  port="4000" />
<Sender  className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport  className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"
 timeout="60000" keepAliveTime="10" keepAliveCount="0" />
</Sender>
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor"
 staticOnly="true" />
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member className="org.apache.catalina.tribes.membership.StaticMember"
 host="192.168.1.2" port="4000" uniqueId="{1,3,5,7,8,0,0,2,0,0,1,0,0,0,0,9}" />
</Interceptor>
</Channel>

<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  filter="" />
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
 <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/opt/tomcat/war-temp/"
                    deployDir="/opt/tomcat/webapps/"
                    watchDir="/opt/tomcat/war-listen/"
                    watchEnabled="true"/>
 
<ClusterListener  className="org.apache.catalina.ha.session.ClusterSessionListener" />
</Cluster>

</Host>
</Engine>
</Service>
</Server>

Servidor Esclavo (192.168.1.2)

…
<Host name="192.168.1.2"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

<!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
<!--
<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

<!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="/var/log/tomcat"
               prefix="localhost_access_log" suffix=".txt"
               pattern="combined"/>

<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
 channelSendOptions="6" channelStartOptions="3">
<Manager className="org.apache.catalina.ha.session.DeltaManager"  expireSessionsOnShutdown="false" notifyListenersOnReplication="true" />
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"  autoBind="0"
 selectorTimeout="5000" maxThreads="6" address="192.168.1.2" port="4000" />
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport  className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"
 timeout="60000" keepAliveTime="10" keepAliveCount="0" />
</Sender>
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.TcpPingInterceptor"  staticOnly="true" />
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" />
<Interceptor  className="org.apache.catalina.tribes.group.interceptors.StaticMembershipInterceptor">
<Member className="org.apache.catalina.tribes.membership.StaticMember"
 host="192.168.1.1" port="4000" uniqueId="{1,3,5,7,8,0,0,2,0,0,1,0,0,0,0,9}" />
</Interceptor>
</Channel>
<Valve className="org.apache.catalina.ha.tcp.ReplicationValve"  filter="" />
<Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
<Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/opt/tomcat/war-temp/"
                    deployDir="/opt/tomcat/webapps/"
                    watchDir="/opt/tomcat/war-listen/"
                    watchEnabled="false"/>
 <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
 
</Cluster>
</Host>
</Engine>
</Service>
</Server>

Una vez configurados ambos servidores procedemos a iniciarlos:

Service tomcat start

Si todo va bien nos debería aparecer algo similar a lo siguiente en los logs del sistema master:

Master

17-Apr-2018 14:48:22.393 INFORMACIÓN [GroupChannel-Heartbeat[192.168.1.1-Channel]-1] org.apache.catalina.ha.tcp.SimpleTcpCluster.memberAdded Replication member added:[org.apache.catalina.tribes.membership.StaticMember[tcp://192.168.1.2:4000,192.168.1.2,4000, alive=0, securePort=-1, UDP Port=-1, id={1 3 5 7 8 0 0 2 0 0 1 0 0 0 0 9 }, payload={}, command={}, domain={}, ]]
17-Apr-2018 14:48:22.394 INFORMACIÓN [GroupChannel-Heartbeat[192.168.1.1-Channel]-1] org.apache.catalina.tribes.group.interceptors.TcpFailureDetector.performBasicCheck Suspect member, confirmed alive.[org.apache.catalina.tribes.membership.StaticMember[tcp://192.168.1.2:4000,192.168.1.2,4000, alive=0, securePort=-1, UDP Port=-1, id={1 3 5 7 8 0 0 2 0 0 1 0 0 0 0 9 }, payload={}, command={}, domain={}, ]]

Por último, vamos a verificar su funcionamiento con la aplicación clusterjsp, esta aplicación la podemos encontrar en internet sin problemas. Copiamos la aplicación dentro de war-listen del servidor maestro y automáticamente observamos como se despliega en el esclavo:

Log servidor maestro –> 192.168.1.2

INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.deploy.FarmWarDeployer.fileRemoved Removing webapp [/clusterjsp]
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.deploy.FarmWarDeployer.remove Cluster wide remove of web app [/clusterjsp]
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.deploy.FarmWarDeployer.fileModified Installing webapp [/clusterjsp] from [/opt/tomcat/webapps/clusterjsp.war]
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployWAR Despliegue del archivo [/opt/tomcat/webapps/clusterjsp.war] de la aplicación web
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.session.DeltaManager.startInternal Registrar gestor [/clusterjsp] a elemento de clúster [Host] con nombre [192.168.1.1]
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.session.DeltaManager.startInternal Iniciando gestor de clúster a las [/clusterjsp]
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.session.DeltaManager.getAllClusterSessions Gestor [/clusterjsp], requiriendo estado de sesión desde [org.apache.catalina.tribes.membership.StaticMember[tcp://192.168.1.2:4000,192.168.1.2,4000, alive=0, securePort=-1, UDP Port=-1, id={1 3 5 7 8 0 0 2 0 0 1 0 0 0 0 9 }, payload={}, command={}, domain={}, ]]. Esta operación se agotará si no se recibe estado de sesión dentro de [60] segundos.
ADVERTENCIA [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.ha.session.DeltaManager.waitForSendAllSessions Manager [/clusterjsp]: No context manager send at [22/05/18 8:41] received in [111] ms.
INFORMACIÓN [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/opt/tomcat/webapps/clusterjsp.war] has finished in [469] ms

Log servidor esclavo –> 192.168.1.2:

ADVERTENCIA [Tribes-Task-Receiver[192.168.1.2-Channel]-3] org.apache.catalina.ha.session.ClusterSessionListener.messageReceived Context manager doesn't exist:[/clusterjsp]
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.startup.HostConfig.deployWAR Despliegue del archivo [/opt/tomcat/webapps/clusterjsp.war] de la aplicación web
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.ha.session.DeltaManager.startInternal Registrar gestor [/clusterjsp] a elemento de clúster [Host] con nombre [192.168.1.2]
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.ha.session.DeltaManager.startInternal Iniciando gestor de clúster a las [/clusterjsp]
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.ha.session.DeltaManager.getAllClusterSessions Gestor [/clusterjsp], requiriendo estado de sesión desde [org.apache.catalina.tribes.membership.StaticMember[tcp://192.168.1.1:4000,192.168.1.1,4000, alive=0, securePort=-1, UDP Port=-1, id={1 3 5 7 8 0 0 2 0 0 1 0 0 0 0 9 }, payload={}, command={}, domain={}, ]]. Esta operación se agotará si no se recibe estado de sesión dentro de [60] segundos.
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.ha.session.DeltaManager.waitForSendAllSessions Gestor [/clusterjsp]; estado de sesión enviado a las [22/05/18 8:41] recibido en [106] ms.
INFORMACIÓN [Tribes-Task-Receiver[192.168.1.2-Channel]-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/opt/tomcat/webapps/clusterjsp.war] has finished in [450] ms

Como podemos comprobar en el los logs cuando desplegamos en el maestro automáticamente se despliega en el esclavo.

Ahora debemos configurar  en otro servidor apache con mod_jk para que realice el balanceo de cargar y verificar la alta disponibilidad con mantenimiento de sesiones de usuarios. En la siguiente URL encontrareis como configuarlo:

https://loveyourlinux.com/apache/

 

Anuncios