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.
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'outilneo4j-admin unbind
.
Déployer un cluseter Neo4j sur Docker¶
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)
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. |
Créer le fichier
docker-compose.yml
suivant :
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}
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
docker-compose
depuis le dossier contenant le fichier docker-compose.yaml
docker-compose up -d
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 |
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
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.
Afficher l'état de la topologie de la BD
SHOW DATABASES yield name, currentPrimariesCount, currentSecondariesCount, requestedPrimariesCount, requestedSecondariesCount
SHOW DATABASE exemple
Modifier la topologie une base de données¶
Voir la commande ALTER DATABASE
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¶
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';
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 :
- Désaffecter les BD du serveur
- Supprimer le serveur
Récupérer l'id du serveur
SHOW SERVERS yield name
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';
Attendre l'opération de déplacement des BD en vérifiant avec
SHOW SERVERS
Supprimer le serveur
DROP SERVER 'SERVER01';
Ajouter un serveur¶
Démarrer le serveur et attendre la découverte du cluster.
Vérifier avec
SHOW SERVERS
qu'il apparaît avec l'état FREE
Activer le serveur
ENABLE SERVER '25a7efc7-d063-44b8-bdee-f23357f89f01' OPTIONS
{modeConstraint:'PRIMARY', allowedDatabases:['exemple']};
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;