Aller au contenu

Cluster Neo4j


Introduction et architecture

Modes d'un serveur

Les serveurs dans un cluster Neo4j ont deux modes de fonctionnement par Base de données hébergée. Ils peuvent être des serveurs Primary pour certaines BD et/ou Secondary pour d'autres BD.

Dans la figure ci-dessous, les serveurs dans le cercle bleu héberge la BD BAR et ceux dans le cercle rouge la BD FOO. Certains sont en mode Primary et d'autres en mode Secondary.

Le serveur à l'intersection héberge le BD FOO en mode Primary et BAR en mode Secondary.

modes

C'est identique à la notion des noeuds primaires et secondaires dans un Replica Set MongoDB.

Un serveur Primary est utilisé dans les opérations de lecture/écriture. Les primaires de la base de données assurent la haute disponibilité en répliquant toutes les transactions à l'aide du protocole Raft. Ce protocole garantit la pérennité des données en attendant que la majorité des primaires d'une base de données (N/2+1) acquittent une transaction avant de la valider.

Un serveur primaire est élu comme rédacteur primaire qui exécute l'opération d'écriture du client et réplique de manière synchrone les écritures aux autres primaires.

Les secondaires de la base de données répliquent les écritures de manière asynchrone à partir de membres plus à jour du cluster.

La principale responsabilité des serveurs secondaires est de mettre à l'échelle les charges de travail en lecture. En raison de leur nature asynchrone, les serveurs secondaires peuvent ne pas fournir toutes les transactions effectuées sur le(s) serveur(s) primaire(s).

États d'un serveur

Un serveur Neo4j peut avoir l'un des 5 états :

  • Free : Quand un serveur est découvert (discovery service) il est ajouté dans cet état avec un ID généré automatiquement. Il ne fait pas encore partie du cluster et ne peut pas héberger des bases de données.
  • Enabled : Pour pouvoir utiliser un srveur Free, doit passer à l'état Enabled par la commande ENABLE SERVER.
  • Deallocating : État précédant la suppression d'un serveur afin de réallouer les BD dans d'autres serveurs.
  • Cordoned : Un état indiquant que le serveur ne peut être alloué pour héberger de nouvelles BD sans perdre les BD qu'il héberge déjà.e
  • Dropped : Après la commande DROP SERVER, le serveur est supprimé du cluster. Il ne peut plus rejoindre le cluster que s'il obtient un nouveau ID par l'outil neo4j-admin unbind.

states

Déployer un cluseter Neo4j sur Docker

1⃣ Créer les fichiers de configuration neo4j.conf qui seront placés dans les dossiers conf/serverX ou X est {1, 2, 3 ou 4}. Son contenu est le suivant :

# paramètre de mémoire
server.memory.pagecache.size=100M
server.memory.heap.initial_size=100M

# Découverte du cluster basée sur les enregistrement DNS
dbms.cluster.discovery.type=DNS

#Adresse du réseau du cluster pour la découverte automatique
dbms.cluster.discovery.endpoints=neo4j-network:5000

# hostname/IP address du serveur pour la découverte du cluster
server.discovery.advertised_address=$(hostname -i)

# hostname/IP du serveur pour échange des messages Raft dans le cluster
server.cluster.raft.advertised_address=$(hostname)

 # hostname/IP du serveur pour la gestion des transactions
server.cluster.advertised_address=$(hostname)

# Activer server-side routing
dbms.routing.enabled=true

# Utiliser server-side routing pour les connexions avec le protocole neo4j://.
dbms.routing.default_router=SERVER

# hostname/IP du serveur pour le routage intra-cluster.
server.routing.advertised_address=$(hostname)
⚠ modifier les persmissions de ce fichier sudo chmod 640 neo4j.conf

Paramètres pour cluster Neo4j
Paramètre Description
server.default_advertised_address Nom (FQDN) ou adresse du serveur.
server.default_listen_address Adresse de l'interface d'écoute. Utiliser l'adresse 0.0.0.0 pour être à l'écoute sur toutes les cartes réseau.
dbms.cluster.discovery.endpoints L'adress d'au moins un serveur du cluster qui doit être le même pour tous les serveurs membres du cluster. Le comportement de ce paramètre est modifié par le paramètre dbms.cluster.discovery.type.
initial.dbms.default_primaries_count Le nombre intial des serveur en mode primary. Par défaut 1.
initial.dbms.default_secondaries_count Le nombre intial des serveur en mode secondary. Par défaut 0.
Variables d'environment

La configuration du cluster utilise aussi des variables d'environnement.

Variable Description
NEO4J_initial_server_mode__constraint mode PRIMARY ou SECONDARY.
NEO4J_dbms_cluster_discovery_endpoints liste des serveurs à contacter pour la découverte du cluster.
NEO4J_server_discovery_advertised_address hostname/IP à publier pour la découverte des membres.
NEO4J_server.cluster.advertised_address hostname/IP à publier pour la gestion des transactions.
NEO4J_server.cluster.raft.advertised_address hostname/IP à publier pour la communication dans le cluster.

2⃣ Créer le fichier docker-compose.ymlsuivant :

version: '3.8'

# Custom top-level network
networks:
  neo4j-internal:

services:

  server1:
    # Image Docker
    image: ${NEO4J_DOCKER_IMAGE}

    # Hostname
    hostname: server1

    # Réseau du cluster (à utiliser dans neo4j.conf pour la découverte du cluster) 
    networks:
      neo4j-internal:
        aliases:
          - neo4j-network

    # HTTP (7474) et Bolt (7687).
    ports:
      - "7474:7474"
      - "7687:7687"

    volumes:
      #- ./data/server1:/var/lib/neo4j/data
      #- ./logs/server1:/var/lib/neo4j/logs
      - ./conf/server1:/var/lib/neo4j/conf
      #- ./import/server1:/var/lib/neo4j/import
      #- ./metrics/server1:/var/lib/neo4j/metrics
      #- ./licenses/server1:/var/lib/neo4j/licenses
      #- ./ssl/server1:/var/lib/neo4j/ssl

    # Les variables d'environnement à utiliser et qui doivent être préalablement intialisées (voir plus bas)
    # Seuelemt la variable pour le choix du mode est intialisé ici
    environment:
      - NEO4J_ACCEPT_LICENSE_AGREEMENT
      - NEO4J_AUTH
      - EXTENDED_CONF
      - NEO4J_EDITION
      #- NEO4J_initial_server_mode__constraint=PRIMARY

    # Vérifier que le port 7474 est ouvert
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]

    # Utilisateur/Groupe
    #user: ${USER_ID}:${GROUP_ID}

  server2:
    image: ${NEO4J_DOCKER_IMAGE}
    hostname: server2
    networks:
      neo4j-internal:
        aliases:
          - neo4j-network
    ports:
      - "7475:7474"
      - "7688:7687"
    volumes:
      #- ./neo4j.conf:/conf/neo4j.conf
      #- ./data/server2:/var/lib/neo4j/data
      #- ./logs/server2:/var/lib/neo4j/logs
      - ./conf/server2:/var/lib/neo4j/conf
      #- ./import/server2:/var/lib/neo4j/import
      #- ./metrics/server2:/var/lib/neo4j/metrics
      #- ./licenses/server2:/var/lib/neo4j/licenses
      #- ./ssl/server2:/var/lib/neo4j/ssl
    environment:
      - NEO4J_ACCEPT_LICENSE_AGREEMENT
      - NEO4J_AUTH
      - EXTENDED_CONF
      - NEO4J_EDITION
      #- NEO4J_initial_server_mode__constraint=PRIMARY
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
    #user: ${USER_ID}:${GROUP_ID}

  server3:
    image: ${NEO4J_DOCKER_IMAGE}
    hostname: server3
    networks:
      neo4j-internal:
        aliases:
          - neo4j-network
    ports:
      - "7476:7474"
      - "7689:7687"
    volumes:
      #- ./neo4j.conf:/conf/neo4j.conf
      #- ./data/server3:/var/lib/neo4j/data
      #- ./logs/server3:/var/lib/neo4j/logs
      - ./conf/server3:/var/lib/neo4j/conf
      #- ./import/server3:/var/lib/neo4j/import
      #- ./metrics/server3:/var/lib/neo4j/metrics
      #- ./licenses/server3:/var/lib/neo4j/licenses
      #- ./ssl/server3:/var/lib/neo4j/ssl
    environment:
      - NEO4J_ACCEPT_LICENSE_AGREEMENT
      - NEO4J_AUTH
      - EXTENDED_CONF
      - NEO4J_EDITION
      #- NEO4J_initial_server_mode__constraint=PRIMARY
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
    #user: ${USER_ID}:${GROUP_ID}

  server4:
    image: ${NEO4J_DOCKER_IMAGE}
    hostname: server4
    networks:
      neo4j-internal:
        aliases:
          - neo4j-network
    ports:
      - "7477:7474"
      - "7690:7687"
    volumes:
      #- ./neo4j.conf:/conf/neo4j.conf
      #- ./data/server4:/var/lib/neo4j/data
      #- ./logs/server4:/var/lib/neo4j/logs
      - ./conf/server4:/var/lib/neo4j/conf
      #- ./import/server4:/var/lib/neo4j/import
      #- ./metrics/server4:/var/lib/neo4j/metrics
      #- ./licenses/server4:/var/lib/neo4j/licenses
      #- ./ssl/server4:/var/lib/neo4j/ssl
    environment:
      - NEO4J_ACCEPT_LICENSE_AGREEMENT
      - NEO4J_AUTH
      - EXTENDED_CONF
      - NEO4J_EDITION
      #- NEO4J_initial_server_mode__constraint=SECONDARY
    healthcheck:
      test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"]
    #user: ${USER_ID}:${GROUP_ID}

3⃣ Ajouter les variables dans un fichier appelé .env à placer dans le même dossier que le fichier docker-compose.yml :

USER_ID="$(id -u)"
GROUP_ID="$(id -g)"
NEO4J_DOCKER_IMAGE=neo4j:5.2.0-enterprise
NEO4J_EDITION=docker_compose
EXTENDED_CONF=yes
NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
NEO4J_AUTH=neo4j/Passw0rds
4⃣ Déployer le cluster en exécutant docker-compose depuis le dossier contenant le fichier docker-compose.yaml

docker-compose up -d

5⃣ Attendre l'initialisation des différents instances et puis accéder aux serveurs

Les serveurs sont accesible sur :

Serveur URL
server1 http://localhost:7474
server2 http://localhost:7475
server3 http://localhost:7476
server4 http://localhost:7477

6⃣ Vérifier les seveurs

Depuis un serveur se connecter sur Neo4j Browser et exécuter la commande

SHOW SERVERS
Avec docker run

Il est possible de démarrer les containers individuellement avec la commande docker run. Ci-après un exemple pour créer un cluster de 3 serveurs.

docker network create --driver=bridge neo4j-cluster

docker run --name=server1 --detach --network=neo4j-cluster \
    --publish=7474:7474 --publish=7473:7473 --publish=7687:7687 \
    --hostname=server1 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4j_server_bolt_advertised_address=localhost:7687 \
    --env NEO4j_server_http_advertised_address=localhost:7474 \
    --env NEO4J_AUTH=neo4j/Passw0rd \
    neo4j:5.2.0-enterprise

docker run --name=server2 --detach --network=neo4j-cluster \
    --publish=8474:7474 --publish=8473:7473 --publish=8687:7687 \
    --hostname=server2 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4j_server_bolt_advertised_address=localhost:8687 \
    --env NEO4j_server_http_advertised_address=localhost:8474 \
    --env NEO4J_AUTH=neo4j/Passw0rd \
    neo4j:5.2.0-enterprise

docker run --name=server3 --detach --network=neo4j-cluster \
    --publish=9474:7474 --publish=9473:7473 --publish=9687:7687 \
    --hostname=server3 \
    --env NEO4J_initial_server_mode__constraint=PRIMARY \
    --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
    --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
    --env NEO4j_server_bolt_advertised_address=localhost:9687 \
    --env NEO4j_server_http_advertised_address=localhost:9474 \
    --env NEO4J_AUTH=neo4j/Passw0rd \
    neo4j:5.2.0-enterprise

docker run --name=read-server4 --detach --network=neo4j-cluster \
  --publish=10474:7474 --publish=10473:7473 --publish=10687:7687 \
  --hostname=read-server4 \
  --env NEO4J_initial_server_mode__constraint=SECONDARY \
  --env NEO4J_dbms_cluster_discovery_endpoints=server1:5000,server2:5000,server3:5000 \
  --env NEO4J_ACCEPT_LICENSE_AGREEMENT=yes \
  --env NEO4j_server_bolt_advertised_address=localhost:10687 \
  --env NEO4j_server_http_advertised_address=localhost:10474 \
  --env NEO4J_AUTH=neo4j/Passw0rd \
  neo4j:5.3.0-enterprise

Gestion des bases de données du cluster

Créer une base de données

La création d'une base de données sur un cluster consiste à spécifier la topologie désirée : nombre de primary et secondary.

Voir la référence de la commande CREATE DATABASE

1⃣ Créer une BD avec 2 primaires et 1 secondaire

CREATE DATABASE exemple TOPOLOGY 2 PRIMARIES 1 SECONDARY
Attention

La création de la base de données n'est réalisée que si le cluster est en mesure de satisfaire la topologie demandée.

Par exemple, si le cluster n'a pas 3 serveurs cette commande échoue.

2⃣ Afficher l'état de la topologie de la BD

SHOW DATABASES yield name, currentPrimariesCount, currentSecondariesCount, requestedPrimariesCount, requestedSecondariesCount
et aussi

SHOW DATABASE exemple
3⃣ Ajouter des données en important, par exemple, la base Movie Graph

Modifier la topologie une base de données

Voir la commande ALTER DATABASE

1⃣ Changer le topologie de la BD exemple

ALTER DATABASE exemple SET TOPOLOGY 1 PRIMARY 2 SECONDARIES
Attention

La modification de topologie n'est réussie que si le cluster contient les serveurs nécessaires.

Pour une BD avec 1 unique primaire la transition automatique est impossible. Dance ce cas, il faut sauvegarder la BD, la supprimer puis la restaurer avec la nouvelle topologie.

Gestion des serveurs du cluster

Modifier un serveur

1⃣ Changer le nom du serveur 135ad202-5405-4d3c-9822-df39f59b823c (utiliser l'id d'un serveur parmi ceux hébergeant la BD exemple)

RENAME SERVER `135ad202-5405-4d3c-9822-df39f59b823c` TO 'SERVER01';
2⃣ Ajouter une contrainte de mode

ALTER SERVER 'SERVER01' SET OPTIONS {modeConstraint:'SECONDARY'};

💡 Le serveur SERVER01 ne peut être que secondaire.

Supprimer un serveur

La suppression d'un serveur depuis un cluster requiert 2 étapes :

  1. Désaffecter les BD du serveur
  2. Supprimer le serveur

1⃣ Récupérer l'id du serveur

SHOW SERVERS yield name

2⃣ Désaffecter les bases de données de ce serveur

DEALLOCATE DATABASES FROM SERVER 'SERVER01';
DRYRUN

La commande DRYRUN permet d'afficher comment les BD seront déplacées avant d'appliquer l'opération.

DEALLOCATE DATABASES FROM SERVER 'SERVER01';

3⃣ Attendre l'opération de déplacement des BD en vérifiant avec SHOW SERVERS

4⃣ Supprimer le serveur

DROP SERVER 'SERVER01';

Ajouter un serveur

1⃣ Démarrer le serveur et attendre la découverte du cluster.

2⃣ Vérifier avec SHOW SERVERS qu'il apparaît avec l'état FREE

3⃣ Activer le serveur

ENABLE SERVER '25a7efc7-d063-44b8-bdee-f23357f89f01' OPTIONS
    {modeConstraint:'PRIMARY', allowedDatabases:['exemple']};
💡 La partie OPTIONS est facultative et peut être remplacée par une configuration du fichier neo4j.conf.

4⃣ Affectation de BD

Le serveur ne recevra pas automatiquement une base de données à moins que l'un des événements suivants soit déclenché et le cluster aie eu besoin de ce serveur :

  • Création de nouvelle BD,
  • Changement de topologie d'une DB existante,
  • Désaffectation d'un autre serveur,
  • Demande explicite de réallocation des BD pour balancer le cluster avec REALLOCATE DATABASES;