Mise en oeuvre d'un site JCMS à haute disponibilité avec le protocole de réplication JSync

1. JCMS et la répartition de charge

La répartition de charge est une solution technique permettant d'assurer la haute disponibilité d'un site Web en distribuant les requêtes reçues sur plusieurs serveurs. Ce type d'architecture offre de nombreux avantages :

  1. Passage à l'échelle : pour améliorer les performances globales du système, il suffit d'ajouter de nouveaux serveurs.
  2. Tolérance aux pannes : si l'un des serveurs tombe, le répartiteur ne le sollicite plus et redistribue la charge sur les serveurs disponibles ;
  3. Flexibilité : Il est possible d'ajouter ou de retirer des serveurs à tout moment et l'effet est immédiat.

JCMS intègre JSync, un moteur de réplication des données et des fichiers. Celui-ci permet, entre autre, de construire des architectures à haute disponibilité basées sur la répartition de charge. La tâche principale de JSync est de synchroniser les données d'un groupe de serveurs JCMS, aussi appelés réplicas. Ainsi, toute création, modification ou suppression de données (catégorie, membre, contenu, …) sur l'un des réplicas sera propagée par le réplica leader aux réplicas membres du groupe. JSync assure ainsi la cohérence globale des données d'un groupe de serveur JCMS.

Avec JSync, chaque serveur JCMS est indépendant pour l'accès aux données. Seules les écritures nécessitent des propagations. Or dans le fonctionnement normal d'un site JCMS, les consultations sont très nettement supérieures aux écritures. Cela signifie que chaque réplica sera capable de traiter la grande majorité des requêtes sans qu'il ait à contacter le reste du groupe. Ainsi, dans le cas où le leader deviendrait indisponible, le réplica acceptera les écritures et ne les diffusera que lorsqu'un leader sera à nouveau disponible. JSync permet donc de construire des architectures robustes, sans goulot d'étranglement et sans SPF (Single Point of Failure).

Enfin, il est important de noter que le répartiteur de charge doit préserver les sessions utilisateur. En effet, JCMS stocke des données dans la session de l'utilisateur (le contenu du caddy, le workspace courant, langue, …). Ces données sont locales au serveur qui gère cette session. Si le client est aiguillé sur un autre serveur, il redémarrera une nouvelle session et perdra donc ces données. Pour cela, les répartiteurs supportent généralement l'affinité de session (sticky session). Ce mécanisme assure qu'un client qui a démarré une session J2EE sur le serveur, continuera à être aiguillé par le répartiteur sur ce serveur. En alternative à l'affinité de session, certains serveurs d'applications (p. ex., Tomcat 5) proposent la réplication des sessions entre les serveurs afin que le client retrouve les données de sa session quelque soit le serveur sur lequel il est aiguillé.

2. Architecture

2.1 La répartition de charge

L'architecture haute disponibilité la plus simple se compose d'un répartiteur de chargeur et de deux réplicas, l'un jouant le rôle de leader. Dans notre exemple, pour des raisons de simplicité et de coût, nous utiliserons Apache 2 comme répartiteur de charge. Néanmoins, pour les sites nécessitant une très haute disponibilité, il est recommandé d'utiliser un véritable répartiteur de charge et une topologie réseau redondante. Ce type d'architecture est décrit en détail dans l'ouvrage Server Load Balancing de Tony Bourke (O'Reilly).

jcb lbt4 archi

Fig. 1. Architecture type

Dans le cadre d'une évaluation simplifiée de cette architecture, il est possible de déployer le répartiteur de charge et le serveur leader (voire les deux serveurs Tomcat) sur une même machine. Dans ce cas, faites attention à modifier les adresses IP utilisées dans les fichiers de paramétrage décrit ci-dessous.

2.2 Gestion des documents

Les publications JCMS peuvent être accompagnées de documents. Il est important que chaque réplica puisse accéder à ces documents. Pour cela, plusieurs solutions sont envisageables :

  1. Répertoire partagé entre les différents réplicas
    Les réplicas accèdent à un répertoire partagé contenant les fichiers uploadés.
  2. Réplication via JSync
    Les fichiers uploadés sont répliqués par JSync sur les différents réplicas.
  3. Réplication via un processus externe
    Les fichiers uploadés sont répliqués par un processus externe (e.g : rsync, unison, ...).

Bien que la seconde solution (Réplication via JSync) assure une indépendance des réplicas, nous recommandons de mettre en place la première solution (Répertoire partagé). D'une part, il existe des solutions simples pour partager le contenu d'un répertoire (NFS, Samba, …) D'autre part, la centralisation des fichiers uploadés réduit la consommation de bande passante entre les réplicas, allège les procédures de sauvegarde et simplifie l'architecture globale.

Néanmoins, pour faciliter la mise en oeuvre de notre exemple, la réplication des fichiers via JSync sera activée.

3. Installation et configuration

Dans cet article nous utilisons le serveur d'applications Tomcat. Consultez le Manuel d'installation et d'exploitation de votre version de JCMS pour connaitre la dernière version de Tomcat à utiliser.

L'article Mise en oeuvre d’un site JCMS à haute disponibilité avec BEA Weblogic décrit la même problématique dans l'environnement BEA WebLogic.

L'installation se déroule en trois étapes :

  1. Installation et configuration des deux serveurs Tomcat ;
  2. Installation de JCMS sur les deux serveurs Tomcat et paramétrage de JSync ;
  3. Installation d'Apache, du mod_jk et configuration de la répartition de charge.

3.1 Installation et configuration des serveurs Tomcat

Les deux serveurs Tomcat sont installés dans les répertoires tomcat1/ et tomcat2/ des serveurs 1 (192.168.0.3) et 2 (192.168.0.4). Pour chacun d'entre eux, éditez les fichiers conf/server.xml et faites les modifications décrites ci-dessous.

3.1.1 Déclaration du connecteur HTTP/1.1

Ce connecteur est utilisé pour les échanges JSync. Il permet aussi d'accéder directement à l'un des réplica sans passer par le répartiteur. Configurez ce connecteur pour qu'il écoute sur le port 9001 pour Tomcat1 et sur le port 9002 pour Tomcat2.

<!-- Define a non-SSL HTTP/1.1 Connector on port 9001 -->
<Connector port="9001" maxHttpHeaderSize="8192"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true"
URIEncoding="UTF-8" />

Consultez la documentation du Connecteur HTTP pour plus d'informations sur les différents attributs possibles et leur valeur.

3.1.2 Déclaration du connecteur AJP 1.3

Ce connecteur est utilisé pour la communication entre Apache/mod_jk et un serveur Tomcat. Configurez ce connecteur pour qu'il écoute sur le port 11009 pour Tomcat1 et sur le port 12009 pour Tomcat2.

<!-- Define an AJP 1.3 Connector on port 11009 -->
<Connector port="11009"
enableLookups="false" redirectPort="8443" protocol="AJP/1.3"
URIEncoding="UTF-8" />

Consultez la documentation du Connecteur AJP pour plus d'informations sur les différents attributs possibles et leur valeur.

3.1.3 Ajout de l'attribut jvmRoute

Modifiez la balise Engine pour y ajouter l'attribut jvmRoute qui sera utilisé par le connecteur mod_jk pour effectuer la répartition de charge. Utilisez la valeur tomcat1 sur Tomcat1 et tomcat2 sur Tomcat2 :

<Engine name="Standalone" defaultHost="localhost" jvmRoute="tomcat1">

3.2 Installation et configuration de JCMS

3.2.1 Installation

Installez une version de JCMS sur chacun des serveurs Tomcat dans le répertoire webapps/jcms/ et démarrez les deux serveurs Tomcat.

Si vous utilisez JCMS 6 ou au delà, l'activation de JSync n'est pas compatible avec la base de données interne (Derby) et nécessite au préalable la configuration d'une base de donnée externe. Pour plus de détails, reportez-vous au Manuel d'installation et d'exploitation de JCMS.

3.2.2 Configuration de JSync sur Tomcat1 (le leader)

Accédez au JCMS de Tomcat1 (http://192.168.0.3:9001/jcms/) et identifiez-vous en administrateur. Dans l'éditeur de propriétés, ouvrez l'onglet Réplication et entrez la configuration suivante :

  • Activé : Oui
  • URID : t1
  • URL de ce réplica : http://192.168.0.3:9001/jcms/
  • Réplication des fichiers : Oui

 

Tomcat1 JSync Properties

Fig. 3. Propriétés de la Réplication sur Tomcat1

Attention

A partir de la version 9 SP3, la nouvelle propriété dbeventlog.explicit-urids doit être renseignée sur le leader ainsi que la "Liste explicite de réplica".

Voir la page "Comment paramétrer la table DBEventLog ?" pour plus de détails.

3.2.3 Configuration de JSync sur Tomcat2

Accédez au JCMS de Tomcat2 (http://192.168.0.4:9002/jcms/) et identifiez-vous en administrateur. Dans l'éditeur de propriétés, ouvrez l'onglet Réplication et entrez la configuration suivante :

  • Activé : Oui
  • URID : t2
  • URL de ce réplica : http://192.168.0.4:9002/jcms/
  • URL de mon leader : http://192.168.0.3:9001/jcms/
  • Réplication des fichiers : Oui
Tomcat2 JSync Properties

Fig. 4. Propriétés de la Réplication sur Tomcat2

3.2.4 Vérification

Pour vérifier le bon fonctionnement de JSync, arrêtez Tomcat1 et Tomcat2. Relancez Tomcat1, puis, une fois qu'il est prêt, relancez Tomcat2. A la fin de son démarrage, Tomcat2 envoie une requête JSync pour se déclarer à son leader (Tomcat1). Pour vérifier que les 2 réplicas sont bien connectés, consultez le Gestionnaire de réplication (Admin > Avancé) sur chacun des réplicas.

Sur Tomcat1, Tomcat2 (t2) doit apparaître dans la liste des réplicas connectés

Tomcat1 JSync Manager

Fig. 5. Gestion de la réplication sur Tomcat1

Sur Tomcat2, Tomcat1 (t1) doit apparaître comme leader de Tomcat2.

Tomcat2 JSync Manager

Fig. 6. Gestion de la réplication sur Tomcat2

3.2.5 Test de la synchronisation

Pour tester que JSync assure bien la synchronisation des données et des fichiers entre les deux réplicas t1 et t2, il suffit de déposer un Document sur l'un et sur l'autre et de vérifier qu'après synchronisation, les 2 Documents sont bien présents avec leur fichier sur les deux réplicas.

Pour cela, créez 2 fichiers textes doc1.txt et doc2.txt. Ouvrez deux navigateurs, l'un sur Tomcat1 (http://192.168.0.3:9001/jcms/) et l'autre sur Tomcat2 (http://192.168.0.4:9002/jcms/). Sur Tomcat1 (resp. Tomcat2) publiez un Document avec doc1.txt (resp. doc2.txt). Au bout de 5 secondes, rechargez la page Tous mes contenus sur les deux navigateurs et vérifiez que les deux Documents sont bien présents sur chacun de deux réplicas. Consultez les fichiers pour tester qu'ils ont bien été répliqués.

Si vous rencontrez un problème de connexion et que vous avez configuré un proxy de sortie, vérifiez que l'adresse IP du noeud à contacter est bien prise en compte par la clause d'exclusion (Sauf pour) de la configuration du proxy de sortie.

3.2.6 Configuration des services externes

Dans une configuration JSync, il faut s’assurer que certains traitements ne seront faits que sur l’un des noeud. Par exemple, les notifications par e-mail ne doivent pas être émises par tous les réplicas.  D’une manière générale, JCMS effectue tous les traitements avec les services externes uniquement sur le leader.

Si les services suivant sont utilisés, ils doivent donc être configurés à minima sur le leader.

  • Serveur SMTP
  • Mail Entrant
  • Import/Export

Si ces services sont aussi configurés sur les réplicas, ils ne seront pas utilisés.

3.3 Installation et configuration d'Apache en répartiteur de charge

Que vous soyez sous Windows ou Unix, assurez vous d'utilisez les dernieres versions stable sur les sites de téléchargement indiqués ci-dessous.

3.3.1 Installation sous Windows

Apache HTTP Server 2.2.x

Téléchargez et executez le programme d'installation (e.g. apache_2.2.11-win32-x86-no_ssl.msi) : http://httpd.apache.org/download.cgi
Suivez les instructions d'installation.

Installation du Connecteur mod_jk 1.2.x

Téléchargez les binaires pour windows (e.g. mod_jk-1.2.27-httpd-2.2.10.so) : http://tomcat.apache.org/download-connectors.cgi.

Renommez le module jk téléchargé en mod_jk.so puis copiez le dans le répertoire modules du serveur Apache (e.g C:\Program Files\Apache Software Foundation\Apache2.2\modules\).

3.3.2 Installation sous Unix

Apache HTTP Server 2.2.x

Si vous n'utilisez pas la version d'Apache fournie avec votre distribution Unix :

Téléchargez et compilez les sources (e.g. httpd-2.2.11.tar.gz) : http://httpd.apache.org/download.cgi

$> tar -zxvf httpd-2.2.11.tar.gz
$> cd httpd-2.2.11/
$> ./configure
$> make
$> make install

Dans ce qui suit, on supposera qu'Apache a été installé dans le répertoire /usr/local/apache/.

Installation du Connecteur mod_jk 1.2.x

Téléchargez le binaire compatible avec votre version d'Apache (e.g mod_jk-1.2.27-httpd-2.2.10.so) http://tomcat.apache.org/download-connectors.cgi.
Renommez le module jk téléchargé en mod_jk.so puis copiez-le dans le répétoire modules du serveur Apache.

3.3.3 Configuration d'Apache

Editez le fichier httpd.conf

A la fin du fichier, ajoutez les lignes suivantes :

# ===================================
# Tomcat/mod_jk configuration

# Load mod_jk
LoadModule jk_module modules/mod_jk.so

# Configure mod_jk
JkWorkersFile conf/workers.properties
JkLogFile logs/mod_jk.log
JkLogLevel info

# Define resources served by Tomcat
JkMount /jcms/* loadbalancer

# Prevent access to WEB-INF directory
<Location "/jcms/WEB-INF/">
deny from all
</Location>

3.3.5 Configuration de la répartition de charge (workers.properties)

Dans le répertoire conf de Apache, créez le fichier workers.properties. Ce fichier indique à Apache les serveurs Tomcat sur lesquels répartir de charge. Il reprend les valeurs spécifiées dans les fichiers de configuration des serveurs Tomcat (cf. section 3.1):

  • Les noms des workers correspondent aux valeurs indiquées dans l'attribut jvmRoute (tomcat1 et tomcat2).
  • Les ports sont ceux définis dans le connecteur AJP/1.3 (11009 et 12009).

Pour plus de détails sur la configuration des workers, consultez la documentation du mod_jk.

# list the workers by name 
worker.list=loadbalancer
# ------------------------
# Tomcat 1
# ------------------------
worker.tomcat1.port=11009
worker.tomcat1.host=192.168.0.3
worker.tomcat1.type=ajp13
worker.tomcat1.lbfactor=1
# ------------------------
# Tomcat 2
# ------------------------
worker.tomcat2.port=12009
worker.tomcat2.host=192.168.0.4
worker.tomcat2.type=ajp13
worker.tomcat2.lbfactor=1
# ------------------------
# Load Balancer worker
# ------------------------
worker.loadbalancer.type=lb
worker.loadbalancer.balance_workers=tomcat1, tomcat2
worker.loadbalancer.sticky_session=1

Démarrez Apache et vérifiez qu'il fonctionne correctement en allant sur l'URL de la page d'accueil, http://192.168.0.2/jcms/

4. Tests

4.1 Test de répartition

Pour vérifier que la répartition de charge fonctionne, ouvrez un navigateur sur deux machines ou deux navigateurs différents sur la même machine (p. ex. Internet Explorer et FireFox). Sur chaque navigateur, interrogez http://192.168.0.2/jcms/admin/status.jsp. Apache va diriger la première requête sur Tomcat1 puis la seconde sur Tomcat2. L'un doit donc afficher la page de statut de JCMS sous Tomcat1 et l'autre celle de JCMS sous Tomcat2. Avec chaque navigateur, consulter la page d'accueil de JCMS. Puis revenez sur la page de statut afin de vérifier que l'affinité de session est bien préservée.

Afin de vous permettre de savoir à tout moment quel réplica à répondu, dans l'espace de travail et dans l'espace d'administration, le nom du réplica est affiché en bas de la page.

jcb lbt4 footer

4.2 Test de tolérance aux pannes

Pour vérifier la tolérance aux pannes, arrêtez le serveur Tomcat2 et rechargez la page http://192.168.0.2/jcms/status.jsp avec le navigateur qui était connecté à Tomcat2. Il devrait maintenant être automatiquement aiguillé sur Tomcat1.

Redémarrez Tomcat2. Arrêtez et relancez le navigateur précédemment utilisé. Accédez à la page http://192.168.0.2/jcms/status.jsp. Il devrait maintenant à nouveau fonctionner avec Tomcat2.

5. Configurations avancées

5.1 Réduire les risques de conflits

Des conflits peuvent survenir durant les phases de divergence des réplicas. JSync résout automatiquement les conflits portant sur un même objet (p. ex. deux mises à jour concurrentes) mais ne résout pas les conflits inter-objets (p. ex. création d'une catégorie sous une catégorie mère et suppression de la catégorie mère). Une solution pour éviter de tels conflits consiste à centraliser les majorités des écritures sur le même réplica. Pour cela, il faut que les contributeurs du site utilisent l'URL d'accès direct à ce réplica (p. ex. : http://192.168.0.3:9001/jcms/ pour accéder à Tomcat1) et non l'adresse publique qui passe par le répartiteur de charge.

5.2 Répartition hétérogène et fail-over

Il est possible de pondérer la répartition de charge entre les réplicas. Ce paramétrage se fait en agissant sur l'attribut lbfactor dans le fichier workers.properties (cf. section 3.3.3). Lorsque le lbfactor est à la même valeur entre les réplicas, la distribution est homogène. Si le lbfactor est à 0, le réplica n'est jamais sollicité sauf si il est le seul à être disponible. Cette particularité permet de monter des architectures en fail-over : un réplica secondaire ne prend la main que lorsque le réplica principal est tombé. Consultez la documentation du mod_jk pour plus d'informations sur les possibilités de configuration de répartition de charge.

5.3 WebDAV

Avant JCMS 6.1, les fichiers édités et sauvegardés depuis un client WebDAV (p. ex. MS Word) ne provoquent pas de mises à jour dans le store de JCMS. Les fichiers sont simplement mis à jour. JSync peut donc fonctionner avec WebDAV du moment que la réplication des fichiers n'est pas activée (cf. section 3.2). Les fichiers doivent être soient partagés soient répliqués par un processus externe à JSync (cf. sections 2.2)

A partir de JCMS 6.1, WebDAV et JSync sont compatibles, il n'est plus nécessaire de configurer la réplication de fichier de manière spécifique.

5.4 DeployManager

Il est possible d'utiliser le DeployManager pour déployer des nouveautés sur les réplicas. Pour cela, installez un DeployManager par serveur et suivez la procédure de déploiement décrite à la section 6.5.

5.5 Sécurité

JSync ouvre une porte d'accès à l'ensemble des données de la webapp en lecture et en écriture. Pour des raisons de sécurité élémentaire, il est important de limiter les machines pouvant faire des requêtes JSync aux différents réplicas. Le principe consiste à définir pour chaque réplica la liste des machines autorisées à dialoguer avec le réplica. Ce paramétrage se fait dans le champ Filtre Adresse IP des propriétés de la réplication en déclarant une expression régulière décrivant les adresses autorisées. Par exemple, l'expression régulière ^192.168.0.\d+$ limite l'accès aux machines du réseau local.

5.6 Ressources statiques

Afin d'améliorer les performances de consultation du site, il est recommandé de faire servir les ressources statiques de JCMS par Apache, pour mettre en place une telle configuration, consultez l'article Configurer Apache, mod_jk et Tomcat pour JCMS.

6. Procédures d'exploitation

6.1 Ajout d'un réplica

Pour faire face à une montée en charge, il faut pouvoir ajouter de nouveaux réplicas. Pour cela, il suffit de d'installer une copie de l'un des serveurs déjà opérationnel. Cette copie doit néanmoins être adaptée. Voici la procédure pour ajouter un serveur Tomcat3 dans notre architecture :

  1. Copier en totalité le répertoire tomcat2/ en tomcat3/ ;
  2. Editer tomcat3/conf/server.xml et modifier les port d'écoute HTTP (p.ex 9003) et AJP (p. ex. 13009) dans server.xml ;
  3. Dans le fichier WEB-INF/data/custom.prop, mettre la propriété jsync.enable à false (afin qu'il ne contact pas le leader avec l'identité de Tomcat2)
  4. Démarrer Tomcat3 et faire le paramétrage JSync (cf. section 3.2.3)
  5. Redémarrer Tomcat3
  6. Déclarer Tomcat3 dans le fichier workers.properties (cf. section 3.3.5) et redémarrer Apache pour que cette nouvelle configuration soit prise en compte.

6.2 Retrait d'un réplica

Pour désactiver un réplica (c'est-à-dire, pour qu'il ne reçoive plus de charge du répartiteur), il suffit de mettre son lbfactor à 0 dans le fichier workers.properties (cf. section 3.3.5) et de redémarrer Apache pour que cette nouvelle configuration soit prise en compte.

6.3 Réintroduction du leader

Si le réplica leader Tomcat1 tombe, les réplicas membres (Tomcat2, Tomcat3, …) ne peuvent plus propager leurs écritures. Néanmoins, chaque réplica membre continuera d'essayer de propager ses nouvelles écritures au leader. Lorsque celui-ci sera à nouveau opérationnel, les réplicas membre pourront à nouveau le contacter mais se verront refuser la synchronisation car le leader ne les connaît pas (dans le journal, code de retour 403 - URID inconnu). Il faut donc procéder à la réintroduction de tous les réplicas membres au près du leader. Pour cela, sur chaque réplica membre, allez dans Gestion de la réplication et cliquez sur le bouton Déconnexion puis sur le bouton Contacter le leader.

A partir de JCMS 5.7.4, le leader peut être configuré pour que, lors de son démarrage, un message JSync soit envoyé forçant chaque réplica à se reconnecter. Pour activer cette fonctionnalité, il suffit de saisir une durée (p. ex. 5 secondes) dans la propriété Reconnexion des réplicas au démarrage.

Par défaut, la liste des réplicas à contacter est automatique alimentée d'après les précédentes connexions. Elle peut aussi être définie explicitement dans l'éditeur de propriétés.

jcms574-jsync

6.4 Manipulation du store

Si le store doit subir des modifications tel qu'un nettoyage, il convient de suivre rigoureusement la procédure suivante.

  1. Sur chaque réplica membre, désactiver les écritures sur tous les réplicas membres (Admin > Propriété > Site > Accès) et propager les dernières écritures au leader (Admin > Gestionnaire de répliation > Propager au leader) ;
  2. Retirer le leader (Tomcat1) de la répartition de charge ;
  3. Faire la manipulation du store sur le leader. Dans le cas d'un nettoyage du store ceci nécessite de désactiver JSync au préalable (Admin > Propriété > Réplication) ;
  4. Une fois la manipulation terminée, recopier le fichier store.xml du leader sur tous les réplicas membres.

6.5 Déploiement par le DeployManager

Le DeployManager peut non seulement modifier le store mais aussi les types, les JSP, les workflows, … Aussi, il faut suivre la procédure suivante pour le déploiement des nouveaux développements sur l'ensemble des réplicas. Cette procédure assure la continuité de service durant toute la phase de déploiement.

  1. Sur chaque réplica membre, désactiver les écritures (Admin > Propriété > Site > Accès) et propager les dernières écritures au leader (Admin > Gestionnaire de réplication > Propager au leader) ;
  2. Désactiver les écritures sur le leader ;
  3. Arrêter le leader ;
  4. Mettre à jour le leader avec le DeployManager ;
  5. Redémarrer le leader ;
  6. Pour chaque réplica membre ;
    1. Arrêter le réplica ;
    2. Mettre à jour le réplica avec le DeployManager ;
    3. Remplacer son store.xml par celui du leader ;
    4. Redémarrer le réplica.
  7. Réactiver les écritures sur les réplicas membres ;
  8. Réactiver les écritures sur le leader.