Cluster de Liferay y Terracotta

Voy a montar un entorno de pruebas en una sola máquina con un cluster de Liferay + un cluster de Terracotta con Web sessions. He usado CentOS 6 x86_64 y las versiones open source de estos dos productos.

Le he dado 2 IPs adicionales a la máquina para usar para el cluster, y añado una entrada en /etc/hosts para resolver por nombre:

10.0.3.15       nodo01
10.0.3.16       nodo02

Primero instalo los paquetes necesarios. Voy a usar como base de datos Mysql

# yum install mysql-server
# chkconfig --list mysqld
mysqld             0:off    1:off    2:off    3:off    4:off    5:off    6:off
# chkconfig mysqld on
# service mysqld start

Descargo la versión de Liferay con Tomcat incluido y la versión open source de Terracotta.

En mi caso he bajado el instalador gráfico de Terracotta, lo ejecuto de la siguiente manera:

# java -jar terracotta-3.5.2_2-installer.jar

Creo los directorios para guardar la información y un usuario para correr el servicio:

# mkdir -p /var/lib/terracotta/{nodo01,nodo02}/{data,statistics}
# mkdir -p /var/log/terracotta/{nodo01,nodo02}
# groupadd -r terracotta
# useradd -r -g terracotta -d /var/lib/terracotta -s /bin/bash terracotta
# chown -R terracotta:terracotta /var/lib/terracotta
# chown -R terracotta:terracotta /var/log/terracotta

Creo el archivo principal de configuración de Terracotta y lo guardo com /usr/local/terracotta/terracotta-3.5.2_2/tc-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!--

All content copyright Terracotta, Inc., unless otherwise indicated. All rights reserved.

-->
<!--
  All the documentation can be found in:
  tc-config-reference.xml
-->
<tc:tc-config xmlns:tc="http://www.terracotta.org/config"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.terracotta.org/schema/terracotta-6.xsd">

  <tc-properties>
    <property name="l2.nha.dirtydb.autoDelete" value="true"/>
    <property name="l1.cachemanager.enabled" value="true"/>
    <!-- <property name="logging.maxLogFileSize" value="1024"/> -->

      <!-- Network Active/Passive for failover from Active to Passive  -->
      <!-- The Nodes keeps tabs on each other to failover from Active to Passive -->
      <!-- http://www.terracotta.org/documentation/ga/product-documentation-16.html#50462727_19590 -->
      <!-- L2 - L2 -->
      <!-- Max Time = (ping.idletime) + socketConnectCount * [(ping.interval * ping.probes) + (socketConnectTimeout * ping.interval)] -->
      <!-- 5000 + 5[ (3 * 1000) + (2 * 1000) ] = 5000 + 25000 = 30000 ms -->
      <!-- The health checker declares the node dead in 25 seconds -->
      <property name="l2.healthcheck.l2.ping.enabled"         value="true" />
      <property name="l2.healthcheck.l2.ping.idletime"        value="5000"  />
      <property name="l2.healthcheck.l2.ping.interval"        value="1000"  />
      <property name="l2.healthcheck.l2.ping.probes"          value="3"     />
      <property name="l2.healthcheck.l2.socketConnect"        value="true"  />
      <property name="l2.healthcheck.l2.socketConnectTimeout" value="2"     />
      <property name="l2.healthcheck.l2.socketConnectCount"   value="5"     />

      <!-- Terracotta-Server and Client-JVMs correspondences -->
      <!-- L2 - L1  L2 Terracotta Server L1 Client -->
      <!-- 5000 + 5[ (3 * 1000) + (2 * 1000) ] =5000 + 25000 = 30000 ms -->
      <!-- Terracotta server keeping tabs on client i.e. apache/tomcat shibboleth -->
      <!-- The health checker declares the client dead in 25 seconds -->
      <!-- Max Time = (ping.idletime) + socketConnectCount * [(ping.interval * ping.probes) + (socketConnectTimeout * ping.interval)] -->
      <property name="l2.healthcheck.l1.ping.enabled"         value="true"  />
      <property name="l2.healthcheck.l1.ping.idletime"        value="5000"  />
      <property name="l2.healthcheck.l1.ping.interval"        value="1000"  />
      <property name="l2.healthcheck.l1.ping.probes"          value="3"     />
      <property name="l2.healthcheck.l1.socketConnect"        value="true " />
      <property name="l2.healthcheck.l1.socketConnectTimeout" value="2"     />
      <property name="l2.healthcheck.l1.socketConnectCount"   value="5"     />

      <!-- Client-JVMs and Terracotta Server correspondences -->
      <!-- L1 - L2  L2 Terracotta Server L1 Client -->
      <!-- 5000 + 5[ (3 * 1000) + (2 * 1000) ] =5000 + 25000 = 30000 ms -->
      <!-- Client server keeping tabs on Terracotta Servers -->
      <!-- The health checker declares the client dead in 25 seconds -->
      <!-- Max Time = (ping.idletime) + socketConnectCount * [(ping.interval * ping.probes) + (socketConnectTimeout * ping.interval)] -->
      <property name="l1.healthcheck.l2.ping.enabled"         value="true"  />
      <property name="l1.healthcheck.l2.ping.idletime"        value="5000"  />
      <property name="l1.healthcheck.l2.ping.interval"        value="1000"  />
      <property name="l1.healthcheck.l2.ping.probes"          value="3"     />
      <property name="l1.healthcheck.l2.socketConnect"        value="true"  />
      <property name="l1.healthcheck.l2.socketConnectTimeout" value="2"     />
      <property name="l1.healthcheck.l2.socketConnectCount"   value="5"     />

      <property name="l2.nha.tcgroupcomm.reconnect.enabled" value="true" />
      <property name="l2.nha.tcgroupcomm.reconnect.timeout" value="15000" />
      <property name="l2.l1reconnect.enabled" value="true" />
      <property name="l2.l1reconnect.timeout.millis" value="15000" />
  </tc-properties>

  <system>
    <configuration-model>production</configuration-model>
  </system>

  <servers>

    <server host="nodo01" name="nodo01" bind="10.0.3.15">
      <data>/var/lib/terracotta/nodo01/data</data>
      <logs>/var/log/terracotta/nodo01</logs>
      <statistics>/var/lib/terracotta/nodo01/statistics</statistics>

      <dso>
        <client-reconnect-window>120</client-reconnect-window>
        <persistence>
          <mode>permanent-store</mode>
          <offheap>
              <!-- <enabled>true</enabled> -->
              <enabled>false</enabled>
              <maxDataSize>5g</maxDataSize>
          </offheap>
        </persistence>
        <garbage-collection>
          <enabled>true</enabled>
          <verbose>false</verbose>
          <interval>3600</interval>
        </garbage-collection>

      </dso>
    </server>

    <server host="nodo02" name="nodo02" bind="10.0.3.16">
      <data>/var/lib/terracotta/nodo02/data</data>
      <logs>/var/log/terracotta/nodo02</logs>
      <statistics>/var/lib/terracotta/nodo02/statistics</statistics>

      <dso>
       <persistence>
          <mode>permanent-store</mode>
       </persistence>
        <garbage-collection>
          <enabled>true</enabled>
          <verbose>true</verbose>
          <interval>3600</interval>
        </garbage-collection>
      </dso>
    </server>

    <mirror-groups>
      <mirror-group group-name="grupo01">
        <members>
          <member>nodo01</member>
          <member>nodo02</member>
        </members>
        <ha>
          <mode>networked-active-passive</mode>
          <networked-active-passive>
            <election-time>5</election-time>
          </networked-active-passive>
        </ha>
       </mirror-group>
    </mirror-groups>     

    <ha>
      <mode>networked-active-passive</mode>
      <networked-active-passive>
        <election-time>5</election-time>
      </networked-active-passive>
    </ha>

  </servers>

  <!-- This section contains settings that affect all clients that connect to the
     system.

     Note that while these settings are applied uniformly across all clients,
     this does not prevent you from applying different settings to various
     clients. There are two ways of doing this:

      - Certain parameters ('logs', below) undergo parameter expansion
        before being used by the client. This allows you to use various predefined
        substitutions (like '%h' for host), or a general one
        (%(myprop) to use the value of Java system property 'myprop'), for
        these values; expansions are carried out in each client's JVM
        independently.  Parameter expansion is not done for values inside the
        <modules> element.
  -->
  <clients>

    <logs>/var/log/terracotta/client-logs-%h-%i</logs>

       <!-- Tomcat module is dependent on tomcat version
             Spring webflow and security also necessary -->
<!--
        <modules>
            <module name="tim-vector" version="2.6.3" group-id="org.terracotta.modules"/>
            <module name="tim-tomcat-5.5"/>
            <module name="tim-spring-webflow-2.0"/>
            <module name="tim-spring-security-2.0"/>
        </modules>
-->
    <dso>
      <fault-count>500</fault-count>

      <!-- This section controls output from the DSO client that can help you
         debug your program. -->
      <debugging>

        <instrumentation-logging>
          <class>false</class>
          <locks>false</locks>
          <transient-root>true</transient-root>
          <roots>false</roots>
          <distributed-methods>false</distributed-methods>
        </instrumentation-logging>

        <runtime-logging>
          <non-portable-dump>true</non-portable-dump>
          <lock-debug>false</lock-debug>
          <wait-notify-debug>false</wait-notify-debug>
          <distributed-method-debug>false</distributed-method-debug>
          <new-object-debug>false</new-object-debug>
          <named-loader-debug>false</named-loader-debug>
        </runtime-logging>

        <runtime-output-options>
          <auto-lock-details>false</auto-lock-details>
          <caller>false</caller>
          <full-stack>false</full-stack>
        </runtime-output-options>
      </debugging>
    </dso>
  </clients>

  <application>

    <!-- This section controls how Terracotta DSO behaves in your application. -->
    <dso>

      <instrumented-classes>
    <!-- FROM http://www.liferay.com/es/community/forums/-/message_boards/message/2766518 -->
    <include>
        <class-expression>org.apache.tomcat.util.log.SystemLogHandler</class-expression>
    </include>
    <include>
        <class-expression>*..*</class-expression>
    </include>
    <!-- Exclude tomcat internals to improve performance of webapp loading -->
    <exclude>org.apache.coyote..*</exclude>
    <exclude>org.apache.catalina..*</exclude>
    <exclude>org.apache.jasper..*</exclude>
    <exclude>org.apache.tomcat..*</exclude>

      </instrumented-classes>

    <additional-boot-jar-classes>
        <include>java.io.Reader</include>
        <include>java.io.BufferedReader</include>
        <include>java.io.PrintStream</include>
        <include>java.io.FilterOutputStream</include>
        <include>java.io.OutputStream</include>
    </additional-boot-jar-classes>

    </dso>
  </application>
</tc:tc-config>

Creo unos scripts para arrancar y parar Terracotta (lo ideal sería crearlos usando de plantilla algún servicio de /etc/init.d yo he creado esto para hacer unas pruebas rápidas):

/usr/local/bin/start-terracotta.sh :

#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/jre

su terracotta -c "/usr/local/terracotta/terracotta-3.5.2_2/bin/start-tc-server.sh -n nodo01 -f /usr/local/terracotta/terracotta-3.5.2_2/tc-config.xml" >/dev/null 2>&1 &
su terracotta -c "/usr/local/terracotta/terracotta-3.5.2_2/bin/start-tc-server.sh -n nodo02 -f /usr/local/terracotta/terracotta-3.5.2_2/tc-config.xml" >/dev/null 2>&1 &

/usr/local/bin/stop-terracotta.sh :

#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/jre

su terracotta -c "/usr/local/terracotta/terracotta-3.5.2_2/bin/stop-tc-server.sh -n nodo01 -f /usr/local/terracotta/terracotta-3.5.2_2/tc-config.xml" &
su terracotta -c "/usr/local/terracotta/terracotta-3.5.2_2/bin/stop-tc-server.sh -n nodo02 -f /usr/local/terracotta/terracotta-3.5.2_2/tc-config.xml" &

Ahora pasamos a configurar Liferay. Lo primero que haremos será crear una base de datos vacía en mysql.

# mysqladmin -u root password 'mi password de root'
# mysql -u root -p
> create database liferay;
> grant all privileges on liferay.* to 'lr_user'@'localhost' identified by 'password';
> flush privileges;
> exit

Descomprimimos el paquete que nos hemos bajado de la página web y hacemos dos copias en /usr/local/liferay1 y /usr/local/liferay2 para los dos nodos del cluster. Creamos también un usuario para el servicio.

# mkdir /var/lib/liferay
# groupadd -r liferay
# useradd -r -g liferay -d /var/lib/liferay -s /bin/bash liferay
# chown liferay:liferay /var/lib/liferay
# chown -R liferay:liferay /user/local/liferay?

En la configuración de cada Tomcat, hacemos que escuchen solamente en una IP (excepto el puerto SHUTDOWN, que le pondremos puertos distintos), además añadiremos un puerto SSL.

Para liferay1:

<Server port="8005" shutdown="SHUTDOWN">

<Connector port="8080" address="10.0.3.15" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />

<Connector port="8443" address="10.0.3.15" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="${user.home}/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS" />

<Connector port="8009" address="10.0.3.15" protocol="AJP/1.3" redirectPort="8443"            URIEncoding="UTF-8" />

Para liferay2:

<Server port="8006" shutdown="SHUTDOWN">

<Connector port="8080" address="10.0.3.16" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8" />

<Connector port="8443" address="10.0.3.16" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
keystoreFile="${user.home}/.keystore" keystorePass="changeit"
clientAuth="false" sslProtocol="TLS" />

<Connector port="8009" address="10.0.3.16" protocol="AJP/1.3" redirectPort="8443"            URIEncoding="UTF-8" />

He definido un almacén de certificados en el conector SSL, voy a crearlo en el home de Liferay:

# su - liferay
$ export JAVA_HOME=/usr/lib/jvm/jre
$ $JAVA_HOME/bin/keytool -genkey -alias tomcat -keyalg RSA
<creamos el certificado y ponemos la contraseña que queramos>

Configuro en los dos Liferay la base de datos mysql:

# vi /usr/local/liferay1/tomcat-6.0.29/webapps/ROOT/WEB-INF/classes/portal-ext.properties

#
# MySQL
#
jdbc.default.driverClassName=com.mysql.jdbc.Driver
jdbc.default.url=jdbc:mysql://localhost/liferay?useUnicode=true&characterEncoding=UTF-8&useFastDateParsing=false
jdbc.default.username=lr_user
jdbc.default.password=password

Creo los scripts de arranque similares a los de Terracotta:

/usr/local/bin/start-liferay.sh

#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/jre

su liferay -c '/usr/local/liferay1/tomcat-6.0.29/bin/startup.sh' >/dev/null 2>&1 &
su liferay -c '/usr/local/liferay2/tomcat-6.0.29/bin/startup.sh' >/dev/null 2>&1 &

/usr/local/bin/stop-liferay.sh

#!/bin/bash
export JAVA_HOME=/usr/lib/jvm/jre

su liferay -c '/usr/local/liferay1/tomcat-6.0.29/bin/shutdown.sh' >/dev/null 2>&1 &
su liferay -c '/usr/local/liferay2/tomcat-6.0.29/bin/shutdown.sh' >/dev/null 2>&1 &

Arranco uno de los tomcats a mano para que cree la base de datos, podemos ver el progreso en el log /usr/local/liferay1/tomcat-6.0.29/logs/catalina.out

# su - liferay
$ export JAVA_HOME=/usr/lib/jvm/jre
$ /usr/local/liferay1/tomcat-6.0.29/bin/startup.sh

Configuramos Liferay para que integre Web Sessions de Terracotta, primero copiamos las librerias necesarias y posteriormente hacemos que coja la configuración por red.

# cp /usr/local/terracotta/terracotta-3.5.2_2/sessions/terracotta-session-1.1.2.jar /usr/local/liferay1/tomcat-6.0.29/lib/
# cp /usr/local/terracotta/terracotta-3.5.2_2/sessions/terracotta-session-1.1.2.jar /usr/local/liferay2/tomcat-6.0.29/lib/
# cp /usr/local/terracotta/terracotta-3.5.2_2/common/terracotta-toolkit-1.3-runtime-3.2.0.jar /usr/local/liferay1/tomcat-6.0.29/lib/
# cp /usr/local/terracotta/terracotta-3.5.2_2/common/terracotta-toolkit-1.3-runtime-3.2.0.jar /usr/local/liferay2/tomcat-6.0.29/lib/

Añado en los dos tomcat-6.0.29/conf/context.xml

<Valve className="org.terracotta.session.TerracottaTomcat60xSessionValve" tcConfigUrl="nodo01:9510,nodo02:9510"/>

Creo las reglas de firewall necesarias:

# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 9520 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 9530 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 9510 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 8005 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 8006 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 8080 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 8443 -j ACCEPT
# iptables -I INPUT 5 -m state --state NEW -m tcp -p tcp --dport 8009 -j ACCEPT
# service iptables save

Arranco el cluster de Terracotta, y luego los dos Liferay.

# start-terracotta.sh
# start-liferay.sh

Por último, existe una herramienta gráfica que nos permite monitorizar Terracotta y ver si se forma el cluster correctamente y se conectan los clientes, además nos da un acceso muy cómodo a todas las configuraciones y objetos. Existen más funcionalidades de la versión open source que se podrían utilizar, espero que habiéndoos explicado esto os resulte más sencillo. Podéis hacer prueba a iniciar sesión en un Tomcat e iros al otro para comprobar que seguís logueados, etc. El usuario y contraseña por defecto en Liferay es: test@liferay.com / test

$ /usr/local/terracotta/terracotta-3.5.2_2/bin/dev-console.sh

Terracotta dev console

Leave a comment