Montando mod_cluster en Docker

By | May 13, 2019

Escribiendo los últimos posts de mod_cluster estuve echando un vistazo a la web del proyecto y acabé dando con la imagen que tienen para montar mod_cluster en un contenedor docker. Básicamente es una imagen con un Apache y mod_cluster embebidos listos para su uso, lo que nos quita de tener que compilar el módulo o bajar los binarios como veníamos haciendo tradicionalmente.

Para probarlo sin más podemos arrancarlo tal cual nos indica el autor de la imagen en el Hub de Docker. Sin embargo para integrarlo con una instancia de Jboss tenemos que personalizar la configuración de la imgen un poco más.

En el artículo, mi instancia de Jboss utiliza la IP 192.168.1.2 y el frontal Apache (que se ejecuta en docker) tiene la IP 192.168.1.154

Configuración de imagen y arranque del contenedor

Me he creado mi propia imagen con un Dockerfile. En el directorio de trabajo tengo los siguientes ficheros:

root@dev:/home/jota/dockerfiles/mod_cluster# ls -l
total 12
-rwxr-xr-x 1 root root 2367 Mar 21 21:05 docker-entrypoint.sh
-rw-r--r-- 1 root root  349 Apr 14 11:44 Dockerfile
-rw-r--r-- 1 root root 1385 Apr 15 21:19 mod_cluster.conf

Donde en Dockerfile tengo:

FROM karm/mod_cluster-master-dockerhub:latest
MAINTAINER juliojosesb@gmail.com

# Your own configuration located in the same directory as your Dockerfile
COPY mod_cluster.conf ${HTTPD_MC_BUILD_DIR}/conf/extra/mod_cluster.conf

# Your own entry point script located in the same directory as your Dockerfile
COPY docker-entrypoint.sh /

El fichero docker-entrypoint.sh contiene:

#!/bin/bash
set -e

if [ "$1" = 'start' ]; then

  #get the current ip addr of the continer
  CONTAINER_IP_ADDR=$(/sbin/ip route|awk '/default/ { print $3 }')
	MODCLUSTER_PORT=${MODCLUSTER_PORT:-6666}
	MODCLUSTER_ADVERTISE=${MODCLUSTER_ADVERTISE:-On}
	MODCLUSTER_ADVERTISE_GROUP=${MODCLUSTER_ADVERTISE_GROUP:-224.0.1.105:23364}
	MODCLUSTER_NET=${MODCLUSTER_NET:-172.}
	MODCLUSTER_MANAGER_NET=${MODCLUSTER_MANAGER_NET:-$MODCLUSTER_NET}

	echo
	echo "Starting httpd with mod_cluster"
	echo "==============================="
	echo "MODCLUSTER_PORT            ${MODCLUSTER_PORT}"
	echo "MODCLUSTER_ADVERTISE       ${MODCLUSTER_ADVERTISE}"
	echo "MODCLUSTER_ADVERTISE_GROUP ${MODCLUSTER_ADVERTISE_GROUP}"
	echo "MODCLUSTER_NET             ${MODCLUSTER_NET}"
	echo "MODCLUSTER_MANAGER_NET     ${MODCLUSTER_MANAGER_NET}"
	echo "mod_cluster server advertise url: http://${CONTAINER_IP_ADDR}:${MODCLUSTER_PORT}"
	echo

	MOD_CLUSTER_CONF_PATH=${HTTPD_MC_BUILD_DIR}/conf/extra/mod_cluster.conf

	if grep -q FACTORY_DEFAULTS ${MOD_CLUSTER_CONF_PATH}; then
		# create mod-cluster.conf from environment variables
		echo "Creating ${MOD_CLUSTER_CONF_PATH} configuration file:"
		echo

		cat >${MOD_CLUSTER_CONF_PATH} <<EOT
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule cluster_slotmem_module modules/mod_cluster_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule advertise_module modules/mod_advertise.so

MemManagerFile ${HTTPD_MC_BUILD_DIR}/cache/mod_cluster

<IfModule manager_module>
Listen ${MODCLUSTER_PORT}
  <VirtualHost ${CONTAINER_IP_ADDR}:${MODCLUSTER_PORT}>
		ErrorLog logs/mod_cluster_error.log
		CustomLog logs/mod_cluster.log common
		LogLevel debug
    <Directory />
      Require ip ${MODCLUSTER_NET}
    </Directory>
		AllowDisplay On
    ServerAdvertise ${MODCLUSTER_ADVERTISE} http://${CONTAINER_IP_ADDR}:${MODCLUSTER_PORT}
    AdvertiseGroup ${MODCLUSTER_ADVERTISE_GROUP}
    EnableMCPMReceive
    <Location /mcm>
      SetHandler mod_cluster-manager
      Require ip ${MODCLUSTER_MANAGER_NET}
   </Location>
  </VirtualHost>
</IfModule>
EOT

		cat ${MOD_CLUSTER_CONF_PATH}
		echo
	fi

	echo "-- Cleaning lock files."
	rm -f /opt/httpd-build/logs/httpd.pid
	rm -f /opt/httpd-build/logs/authdigest_shm.*

	echo "-- ${HTTPD_MC_BUILD_DIR}/bin/apachectl $@"
	${HTTPD_MC_BUILD_DIR}/bin/apachectl "$@"
else
	exec "$@"
fi

Y finalmente el fichero mod_cluster.conf personalizado para mi caso es este:

#
# MOD_CLUSTER MANAGER VHOST
#

LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule cluster_slotmem_module modules/mod_cluster_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule advertise_module modules/mod_advertise.so

MemManagerFile /opt/httpd-build/cache/mod_cluster

Listen *:6666
<VirtualHost *:6666>

    ErrorLog logs/mod_cluster_error.log
    CustomLog logs/mod_cluster_access.log common
    LogLevel info

    <Directory />
      Require ip 192.168.1
    </Directory>

    AllowDisplay On
    ServerAdvertise On
    AdvertiseFrequency 5
    AdvertiseGroup 224.0.1.105:23364
    EnableMCPMReceive On

    <Location /mcm>
      SetHandler mod_cluster-manager
      Require ip 192.168.1
   </Location>

</VirtualHost>


#
# APP VHOST
#

Listen *:80         
<VirtualHost *:80>

    ServerName lb_app1

    <Directory />
        Require all granted
    </Directory>

    # Balancer for this VirtualHost
    ManagerBalancerName lb_app1
    ProxyPass / balancer://lb_app1/ stickysession=JSESSIONID|jsessionid nofailover=On

    ErrorLog          logs/app1_error.log
    CustomLog         logs/app1_access.log common

</VirtualHost>

La configuración es similar a la comentada en el post anterior donde explicaba cómo configurar mod_cluster por UDP o TCP, separando la parte de administración de mod_cluster del LoadBalancer que doy de alta en cada VirtualHost.

Una vez tenemos esto construimos nuestra imagen:

docker build -q --rm --tag=mod_cluster_jota .

Después ejecutamos el contenedor:

docker run -it --rm --net host \
--name mod_cluster-app1 \
-e MODCLUSTER_PORT=6666 \
-e MODCLUSTER_ADVERTISE=On \
-e MODCLUSTER_NET=192.168.1 \
-e MODCLUSTER_MANAGER_NET=192.168.1 \
mod_cluster_jota

Veremos:

Starting httpd with mod_cluster
===============================
MODCLUSTER_PORT            6666
MODCLUSTER_ADVERTISE       On
MODCLUSTER_ADVERTISE_GROUP 224.0.1.105:23364
MODCLUSTER_NET             192.168.1
MODCLUSTER_MANAGER_NET     192.168.1
mod_cluster server advertise url: http://192.168.1.1:6666

-- Cleaning lock files.
-- /opt/httpd-build/bin/apachectl start -DFOREGROUND
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1. Set the 'ServerName' directive globally to suppress this message

Si quisiera entrar dentro del contenedor para revisar logs, ficheros de configuración, etc… podemos hacerlo invocando una shell dentro:

docker exec -it mod_cluster-app1 bash

Configuración y arranque de Jboss

Se trata de una instancia Jboss EAP 7.1. Configuramos en standalone-ha.xml:

  • Subsistema mod_cluster:
    <subsystem xmlns="urn:jboss:domain:modcluster:3.0">
        <mod-cluster-config advertise-socket="modcluster" balancer="${modcluster.balancer}" proxies="apache1" connector="ajp">
            <dynamic-load-provider>
                <load-metric type="cpu"/>
            </dynamic-load-provider>
        </mod-cluster-config>
    </subsystem>
    
  • Outbound-connection para que el proxy apache1 apunte al contenedor:

    <outbound-socket-binding name="apache1">
        <remote-destination host="192.168.1.154" port="6666"/>
    </outbound-socket-binding>
    

Arrancamos Jboss indicando el balanceador (lb_app1) donde se publica el contexto de nuestra aplicación:

./standalone.sh -c standalone-ha.xml \
-Dmodcluster.balancer=lb_app1 \
-Djboss.bind.address.management=192.168.1.2 \
-Djboss.bind.address=192.168.1.2

En un navegador comprobamos el estado de mod_cluster en http://192.168.1.154:6666/mcm

En mi caso la aplicación desplegada tiene el contexto /example como se puede ver en el monitor de mod_cluster. Si entro por el Apache con la dirección http://192.168.1.154/example podré acceder a mi aplicación. Según la aplicación que tengas desplegada en Jboss esto variará. Siempre puedes probar las de ejemplo del repo Jboss EAP Quickstarts.

Tanto la imagen como la instancia de JBoss la podemos configurar según necesidades. He dejado el fork del proyecto original en mi cuenta con los ficheros de configuración ya modificados para construir la imagen y ejecutar el contenedor.