Mise en place d'un cluster OpenIDM

OpenIDM est prévu pour s'installer et fonctionner en mode cluster, de manière assez facile.

Chaque instance partage le même repository, ce qui leur permet de disposer des mêmes données, la configuration étant alors partagée. Seul le repository est utilisé pour le cluster, sans autre mécanisme (pas de test de lien réseau par exemple).

Cet article explique comment démarrer deux instances OpenIDM qui vont partager le même repository et utiliser une ressource LDAP commune (OpenDJ dans notre exemple).

Nous avons monté 5 containers Linux (lxc) :

  • 10.0.3.41 (mysql) : Repository MySQL
  • 10.0.3.42 (opendj) : Annuaire LDAP OpenDJ
  • 10.0.3.43 (openidm1) : Première instance OpenIDM
  • 10.0.3.44 (openidm2) : Deuxième instance OpenIDM
  • 10.0.3.144 (openidm) : Reverse Proxy nginx

Image

# lxc-ls -f
NAME         STATE    IPV4       IPV6  AUTOSTART  
------------------------------------------------
mysql        RUNNING  10.0.3.41  -     NO         
opendj       RUNNING  10.0.3.42  -     NO         
openidm1     RUNNING  10.0.3.43  -     NO         
openidm2     RUNNING  10.0.3.44  -     NO 
openidm      RUNNING  10.0.3.144 -     NO 

Paramétrage MySQL

La base de données MySQL doit pouvoir être accédée en remote. Il faut donc autoriser des connexions à distance.
Ceci s'effectue dans le fichier /etc/mysql/my.cnf : Modifier la ligne

#bind-address           = 127.0.0.1

en

bind-address            = 10.0.3.41

De cette manière, la machine MySQL sera en mesure d'écouter (et de répondre) sur cette adresse IP. Il faut cependant que les utilisateurs soient autorisés à se connecter à distance. Ceci est géré lors de la création d'un utilisateur, avec une instruction GRANT par exemple :

grant all on openidm.* to usropenidm@'*' identified by 'password';

Ceci va donner les droits sur le schéma openidm pour l'utilisateur usropenidm, quelle que soit la machine (*).
On pourrait restreindre les accès à certaines adresses IP bien particulières, par exemple :

grant all on openidm.* to usropenidm@'10.0.3.43' identified by 'passw0rd';
grant all on openidm.* to usropenidm@'10.0.3.44' identified by 'passw0rd';

Paramétrage sur la première machine du cluster OpenIDM

Activation du mode cluster et position

Le paramétrage s'effectue dans le fichier conf/boot/boot.properties du projet.

S'agissant du premier noeud du cluster, on va utiliser le paramétrage suivant :

# node id if clustered; each node in a cluster must have a unique node id
openidm.node.id=node1

# valid instance types for node include standalone, clustered-first, and clustered-additional
#openidm.instance.type=standalone
openidm.instance.type=clustered-first

 

Paramétrage du repository

Puisque nous utilisons une base MySQL, nous allons supprimer le fichier repo.orientdb.json, et utiliser le fichier repo.jdbc.json.
Si on ne désire pas modifier le schéma standard OpenIDM, il faut uniquement se concentrer sur la partie connection

{
    "connection" : {
        "dbType" : "MYSQL",
        "jndiName" : "",
        "driverClass" : "com.mysql.jdbc.Driver",
        "jdbcUrl" : "jdbc:mysql://10.0.3.41:3306/openidm?allowMultiQueries=true&characterEncoding=utf8",
        "username" : "usropenidm",
        "password" : "passw0rd",
        "defaultCatalog" : "openidm",
        "maxBatchSize" : 100,
        "maxTxRetry" : 5,
        "enableConnectionPool" : true,
        "connectionTimeoutInMs" : 30000
    },

Paramétrage sur la deuxième machine du cluster OpenIDM

Pour effectuer la mise en route de la deuxième machine, on peut soit cloner (s'il s'agit d'une VM quelconque), soit simplement copier le contenu du répertoire projet.
Lorsque l'on configure un cluster, il faut vérifier que les fichiers de configuration soit les mêmes sur chaque instance, à l'exception du fichier boot/boot.properties.

Activation du mode cluster et position

Le paramétrage s'effectue dans le fichier conf/boot/boot.properties du projet.

S'agissant du second noeud du cluster, on va utiliser le paramétrage suivant :

# node id if clustered; each node in a cluster must have a unique node id
openidm.node.id=node2

# valid instance types for node include standalone, clustered-first, and clustered-additional
#openidm.instance.type=standalone
openidm.instance.type=clustered-additional

 

Paramétrage du repository

Le paramétrage du repository doit être identique au premier serveur, puisqu'il s'agit du composant qui sera partagé entre les instances.

Configuration minimale

Si on désactive le parsing des fichiers de configuration json, il est possible de démarrer un noeud "esclave" avec un ensemble de fichiers de configuration minimal :

cd /path/to/openidm/project
ls -R conf
conf:
boot  config.properties  logging.properties  repo.jdbc.json  system.properties

conf/boot:
boot.properties

Dans le fichier conf/system.properties, on va désactiver le parsing des fichiers :

# To disable the JSON file monitoring you need to uncomment this line
openidm.fileinstall.enabled=false

Démarrage des instances

On peut ensuite démarrer les instances sur chaque machine :

cd /path/to/openidm
./startup.sh -p myproject

Attention : seule la configuration est stockée dans le repository.

Par contre les autres éléments du projet, par exemple des scripts JS ou Groovy qui sont en dehors du répertoire conf, ne sont pas répliqués.
De même ,les fichiers *.properties ne sont pas répliqués. Ils sont propres à l'instance. On peut donc imaginer avoir des instances travaillant sur des ports réseau différents par exemple.
Ceci peut être considéré comme un avantage ou un inconvénient :

  • en cas de mise à jour, il faut répliquer les modifications sur l'autre serveur. Pour cela, on pourra utiliser un mécanisme comme rsync ou encore utiliser un gestionnaire de sources, type git ou svn.
  • ceci permet cependant d'avoir des scripts et donc des comportements différents sur les deux serveurs. On pourrait donc avoir un serveur plus spécialisé dans les traitements de réconciliation, et un autre qui dispose de endpoints spécifiques qui seront utilisés par une application.

Tout dépend donc des besoins

 

Vérification de l'état des membres du cluster

Il existe un endpoint permettant de récupérer l'état des noeuds du cluster OpenIDM : openidm/cluster. Par exemple :

curl -s --insecure --header 'X-OpenIDM-Username: openidm-admin' --header 'X-OpenIDM-Password: openidm-admin' \
--header 'Content-Type: application/json' --request GET http://localhost:8080/openidm/cluster
{
    "results": [
        {
            "instanceId": "node1",
            "shutdown": "2015-10-13T15:59:18.478Z",
            "startup": "2015-10-13T15:14:06.908Z",
            "state": "down"
        },
        {
            "instanceId": "node2",
            "shutdown": "",
            "startup": "2015-10-13T15:07:19.319Z",
            "state": "running"
        }
    ]
}

On constate ici que le node1 est arrêté (down). On a également une indication de la date de l'arrêt (attribut shutdown). Il est donc possible de monitorer via une sonde l'état des différents noeuds du cluster, à condition bien évidemment qu'au moins l'un des noeuds réponde aux requêtes REST !

Mise en place d'un frontal reverse proxy

Si on veut amener de la haute-disponibilité, il est nécessaire d'avoir une adresse "virtuelle" ou une URL qui ne bouge pas, et qui va jouer le rôle de reverse proxy vers les serveurs OpenIDM.

Dans le cadre de notre maquette j'ai ajouté un container LXC avec un serveur nginx qui va jouer ce rôle.

Le paramétrage du proxy est très simple :

cat /etc/nginx/sites-avaiable/openidm_proxy 
## Proxy requests to OpenIDM servers

upstream app {
  server 10.0.3.43:8080 fail_timeout=5s max_fails=3 ; 
  server 10.0.3.44:8080 backup ;
}


server {
  listen 80 ;

  server_name openidm.lxc.local;

# We proxy everything
  location / {
    proxy_pass http://app ;
  }
}
  

On déclare un upstream que l'on appelle app, et qui est constitué des 2 serveurs openidm.

  • server 10.0.3.43:8080 fail_timeout=5s max_fails=3 : c'est le serveur principal, qui va recevoir toutes les requêtes.
  • server 10.0.3.44:8080 backup : c'est le serveur secondaire, en mode failover, qui recevra les requêtes si le serveur principal ne répond pas au bout de

L'explication des paramètres fail_timeout et max_fails est la suivante : le serveur est considéré indisponible s'il y a le nombre de max_fails tentatives de connexion infructueuses pendant la durée correspondant au fail_timeout. Dans notre exemple, s'il y a 3 rejets en 5 secondes, le serveur 1 sera considéré comme indisponible, et les requêtes seront routées vers le serveur 2 (backup).
Par défaut, si on ne précise pas de backup, tous les serveurs sont considérés comme primaires et nginx envoie les requêtes à l'un puis à l'autre en mode round-robin (chacun son tour).

On peut maintenant router les requêtes sur http://openidm.lxc.local, qui va gérer la répartition entre les instances OpenIDM.

Catégorie