Démarrer le labo, c'est allumer un immeuble de trois étages. demarrer appuie sur l'interrupteur général, puis un concierge passe à chaque étage vérifier que la lumière est bien allumée avant de te donner les clés : ce concierge, ce sont les healthchecks de Docker Compose. Kibana n'a même pas le droit de démarrer avant qu'Elasticsearch soit déclaré en bonne santé. Ensuite importer remplit les rayonnages d'Elasticsearch, charger-graphe remplit ceux de Neo4j, et etat te donne le plan de l'immeuble : qui est là, qui répond, combien de documents dans chaque index. Le reste de la leçon est une visite guidée des deux pièces où tu passeras le cours : la console Dev Tools de Kibana et Neo4j Browser.
demarrer enchaîne trois étapes de docker compose : pull (télécharge les images, ~6 Go la première fois, rien ensuite), up -d (crée et lance les conteneurs en arrière-plan), puis une boucle d'attente qui interroge docker inspect toutes les 3 secondes jusqu'à ce que chaque conteneur soit healthy.
Le mot healthy vient du docker-compose.yml. Chaque service y déclare un test que Docker rejoue régulièrement :
| Service | Test exécuté dans le conteneur | Rythme |
|---|---|---|
| elasticsearch | curl -fsS http://localhost:9200/_cluster/health doit contenir "status":"green" ou "yellow" | toutes les 10 s, 30 essais, 30 s de grâce au démarrage |
| kibana | curl -fsS http://localhost:5601/api/status doit contenir "level":"available" | toutes les 10 s, 30 essais, 40 s de grâce |
| neo4j | wget -qO- http://localhost:7474 répond | toutes les 10 s, 30 essais, 30 s de grâce |
Tant que le test échoue, le conteneur est starting ; quand il réussit, healthy ; après 30 échecs, unhealthy. Le service kibana déclare depends_on: elasticsearch: condition: service_healthy : Compose ne le lance qu'une fois Elasticsearch healthy, ce qui évite le fameux « Kibana server is not ready yet » au démarrage.
Deux détails du fichier compose méritent un mot dès maintenant. xpack.security.enabled=false : pas de mot de passe ni de certificat pour Elasticsearch, c'est un labo local, jamais une configuration de production. cluster.routing.allocation.disk.threshold_enabled=false : Elasticsearch ne passera pas tes index en lecture seule si ton disque dépasse 95 % (leçon 04, panne n° 5).
Le kit du labo d'abord. Tout ce qui suit se lance depuis la racine du kit — https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr — récupéré par
git clone https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-frou par le bouton Code → Download ZIP (leçon 02, étape 4). Sidir(ouls) ne montre pasdocker-compose.yml,labo.shetlabo.ps1, tu n'es pas au bon endroit.
Toutes les requêtes de cette leçon : elasticsearch/requetes/01-03-premiere-visite.txt.
Démarrer la stack. Depuis le dossier du kit :
./labo.sh demarrer.\labo.ps1 demarrerLe script affiche trois blocs : == Téléchargement des images == (long la première fois, silencieux ensuite), == Démarrage == (les lignes Container labo-elasticsearch Started de Compose), puis == Attente que chaque service soit prêt == où chaque ligne se remplit de points jusqu'à prêt (…s). Il se termine par :
Le labo est prêt.
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026)
Étape suivante : ./labo.sh importer puis ./labo.sh charger-grapheCe qu'il faut voir : trois prêt (elasticsearch, kibana, neo4j). Compte une à deux minutes après le téléchargement. Si une ligne affiche unhealthy, exited ou délai dépassé, file en leçon 04.
Charger les index Elasticsearch.
./labo.sh importerLa commande crée chaque index avec son mapping (fichiers elasticsearch/mappings/*.json) puis envoie les données à l'API _bulk. Sortie réelle, ici sur un labo où les index existaient déjà (la commande est rejouable sans risque) :
== Import dans elasticsearch ==
— index cours existe déjà — conservé
✔ données cours chargées
— index avis existe déjà — conservé
✔ données avis chargées
— index acces existe déjà — conservé
✔ données acces chargées
index docs.count store.size
acces 12000 1.5mb
avis 609 74.6kb
cours 504 183.6kb
Import terminé. Attendu : cours = 504, avis = 609, acces = 12000.Ce qu'il faut voir : la première fois, les lignes — deviennent ✔ index cours créé avec son mapping. Les trois compteurs doivent être exactement 504, 609 et 12000 ; les documents portent leur propre _id, donc relancer l'import n'en crée jamais de doublon.
Charger le graphe Neo4j.
./labo.sh charger-grapheLe script exécute neo4j/cypher/01-contraintes.cypher (contraintes d'unicité et index) puis 02-charger.cypher (LOAD CSV + MERGE pour chaque fichier de neo4j/import/). Il se termine par le bilan que calcule la dernière requête du script, MATCH (n) RETURN labels(n)[0] AS etiquette, count(*) AS noeuds ORDER BY etiquette :
etiquette, noeuds
"Competence", 22
"Cours", 504
"Etudiant", 300
"Professeur", 30
"Ville", 16
Graphe chargé. Attendu : Competence 22, Cours 504, Etudiant 300, Professeur 30, Ville 16.Ce qu'il faut voir : 872 nœuds au total. Tout est en MERGE, donc rejouer la commande ne crée aucun doublon.
Lire l'état du labo. C'est la commande que tu taperas le plus souvent :
./labo.sh etatSortie réelle (ici avec le profil OpenSearch actif, qui n'apparaîtra pas chez toi avant le module 5) :
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 16 minutes (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up 16 minutes (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 14 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 16 minutes (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 16 minutes 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}Ce qu'il faut voir : (healthy) sur chaque ligne de conteneur, green pour le cluster, les trois compteurs d'index et 872 nœuds. Sans OpenSearch, la dernière ligne dit — OpenSearch non démarré (profil optionnel : ./labo.sh demarrer opensearch).
Ouvrir Kibana Dev Tools. Va sur http://localhost:5601. Kibana est en français (I18N_LOCALE=fr-FR). Clique sur le menu ☰ en haut à gauche ; dans la section Management, choisis Outils de développement (c'est la traduction de « Dev Tools » ; le fil d'Ariane affiche Outils de développement > Console). Tu arrives sur l'onglet Console, sous-onglet Shell : un éditeur à gauche, la réponse à droite. Le bouton Effacer cette entrée vide l'éditeur d'exemple.
Ce qu'il faut voir : quand tu tapes une requête, un bouton ▶ (« Cliquer pour envoyer la requête ») apparaît au bout de la ligne. Raccourci : Ctrl + Entrée (⌘ + Entrée sur Mac) envoie la requête sous le curseur. En bas à droite de la réponse, Kibana affiche le code HTTP et la durée, par exemple 200 - OK 23 ms.
Première requête : la santé du cluster.
GET _cluster/health{
"cluster_name": "labo",
"status": "green",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 53,
"active_shards": 53,
"unassigned_shards": 0,
…
"active_shards_percent_as_number": 100
}Ce qu'il faut voir : "status": "green". Les index du labo sont créés avec number_of_replicas: 0, donc pas de réplica en attente : green sur un seul nœud. Le nombre de shards (53) inclut des index système de Kibana ; il peut varier.
Qui répond, et quels index existent ?
GET /Réponse : "name": "labo-es-1", "cluster_name": "labo", "version": { "number": "9.5.3", …, "lucene_version": "10.5.1" }, "tagline": "You Know, for Search".
GET _cat/indices/cours,avis,acces?v&s=indexhealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.6kb 74.6kb 74.6kb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbCe qu'il faut voir : pri 1, rep 0, et les trois compteurs. ?v ajoute la ligne d'en-tête, s=index trie par nom. Sans le filtre cours,avis,acces, GET _cat/indices?v liste aussi des index internes qui commencent par un point (.internal.alerts-…) : ignore-les.
Le nœud et les comptages.
GET _cat/nodes?vip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.24.0.2 39 63 2 0.43 0.38 0.64 cdfhilmrstw * labo-es-1GET cours/_count{ "count": 504, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 } }Même chose pour GET avis/_count → "count": 609 et GET acces/_count → "count": 12000. Ce qu'il faut voir : un seul nœud, master *, et heap.percent autour de 40 % avec le réglage -Xmx1g du compose.
Un premier document.
GET cours/_search{
"query": { "match_all": {} },
"size": 1
}Réponse (tronquée) :
{
"took": 1,
"hits": {
"total": { "value": 504, "relation": "eq" },
"max_score": 1,
"hits": [
{
"_index": "cours",
"_id": "C0001",
"_score": 1,
"_source": {
"id": "C0001",
"titre": "Docker expliqué simplement",
"categorie": "DevOps", "sujet": "Docker", "niveau": "debutant",
"prix": 129, "note_moyenne": 4.4, "nb_avis": 327,
"professeur": { "id": "P001", "nom": "Karim Caron", "ville": "Gatineau" },
…
}
}
]
}
}Ce qu'il faut voir : hits.total.value = 504 (tous les documents correspondent à match_all) mais un seul est renvoyé grâce à size: 1. Dev Tools envoie un GET avec corps en POST sous le capot ; les deux sont acceptés.
Ouvrir Neo4j Browser et se connecter. Va sur http://localhost:7474. La fenêtre Connect to instance propose déjà Protocol neo4j:// et Connection URL localhost:7687 (le port Bolt, pas 7474). Laisse Database user à neo4j, tape le Password aiopsatlas2026, clique Connect. Ce qu'il faut voir : en haut, un point vert et Instance: neo4j://localhost:7687, Database: neo4j, User: neo4j. Le panneau Database information à gauche affiche Nodes (872) avec les étiquettes Competence, Cours, Etudiant, Professeur, Ville, et Relationships (3,712) avec COUVRE, ENSEIGNE, HABITE, INSCRIT_A, PREREQUIS_DE. La barre latérale propose Database overview, Saved Cypher, History, Cypher reference, Parameters, Settings (l'interface est en anglais).
Première requête Cypher. Dans l'éditeur en haut (neo4j$), tape puis clique Run (ou Ctrl + Entrée) :
MATCH (c:Cours) RETURN c LIMIT 25Ce qu'il faut voir : un cadre de résultat avec trois vues Graph, Table, Raw, et à droite Results overview : Nodes (25) · Cours (25). En vue Graph, 25 bulles orange ; clique sur l'une d'elles pour voir ses propriétés (id, titre, categorie, prix…). En vue Table, les mêmes 25 nœuds en JSON. Le module 6 t'apprend à écrire ces requêtes ; pour l'instant, tu sais où les taper.
Et OpenSearch ? Le labo peut aussi démarrer OpenSearch 3.8.0 et OpenSearch Dashboards sur les ports 9201 et 5602, avec ./labo.sh demarrer opensearch puis ./labo.sh importer opensearch. C'est un profil optionnel de Compose : il n'est pas lancé par défaut, et il réclame 6 Go de mémoire pour Docker. On l'active au module 5, pour rejouer les mêmes requêtes sur les deux moteurs et comparer. Rien à faire aujourd'hui.
Kibana affiche « Kibana server is not ready yet » juste après demarrer → Kibana a démarré mais n'a pas encore fini de se connecter à Elasticsearch et de créer ses index internes. Attends 30 secondes et recharge la page. Si le message persiste au-delà de deux minutes, ./labo.sh journal kibana (leçon 04, panne n° 4).
Dev Tools répond 404 avec "type": "index_not_found_exception", "reason": "no such index [cour]" → Faute de frappe dans le nom de l'index (cour au lieu de cours). Elasticsearch ne devine pas ; GET _cat/indices?v te donne les noms exacts. Variante : 400 avec "no handler found for uri [/cours/_serch] and method [GET]" → c'est le nom de l'API qui est mal orthographié (_serch).
Dev Tools répond 400 avec x_content_parse_exception … was expecting double-quote to start field name → Le JSON est mal formé, le plus souvent une virgule en trop avant l'accolade fermante ("size": 1, }). L'éditeur souligne la ligne fautive en rouge avant même l'envoi.
Neo4j Browser : « Connection to instance failed — The client is unauthorized due to authentication failure. » (détails : Neo.ClientError.Security.Unauthorized) → Mot de passe erroné. C'est aiopsatlas2026 (fixé par NEO4J_AUTH dans le compose), pas le mot de passe par défaut neo4j. Retape-le, sans espace final.
etat affiche Neo4j répond — nœuds : 0 → Le conteneur tourne mais tu as oublié charger-graphe. Lance-le ; il faut une trentaine de secondes.
demarrer, importer, charger-graphe. Les deux derniers sont rejouables sans créer de doublons (identifiants explicites côté Elasticsearch, MERGE côté Neo4j).demarrer attend le statut healthy de chaque conteneur ; ce statut vient des healthchecks du docker-compose.yml, et Kibana ne démarre qu'après Elasticsearch grâce à depends_on … service_healthy.etat en un coup d'œil : (healthy) partout, cluster green, 504 / 609 / 12000 documents, 872 nœuds.GET chemin suivie de son JSON collé dessous ; Ctrl + Entrée pour envoyer.localhost:7687, utilisateur neo4j, mot de passe aiopsatlas2026 ; les résultats se lisent en Graph, Table ou Raw.Le fichier 01-03-premiere-visite.txt est au format exact de Dev Tools : dans la console, le bouton Importer les requêtes (icône en haut à droite de l'éditeur) accepte ce fichier et le colle dans l'éditeur. Tous les fichiers du dossier elasticsearch/requetes/ fonctionnent ainsi ; tu n'auras jamais à retaper une requête du cours.