Η εκκίνηση του εργαστηρίου είναι σαν να ανάβεις ένα κτίριο τριών ορόφων. Το demarrer πατά τον γενικό διακόπτη, μετά ένας θυρωρός περνά από κάθε όροφο για να επαληθεύσει ότι το φως είναι πράγματι αναμμένο πριν σου δώσει τα κλειδιά: αυτός ο θυρωρός είναι τα healthchecks του Docker Compose. Το Kibana δεν έχει καν το δικαίωμα να ξεκινήσει πριν το Elasticsearch δηλωθεί υγιές. Στη συνέχεια το importer γεμίζει τα ράφια του Elasticsearch, το charger-graphe γεμίζει αυτά του Neo4j, και το etat σου δίνει τον χάρτη του κτιρίου: ποιος είναι εκεί, ποιος απαντά, πόσα έγγραφα σε κάθε index. Το υπόλοιπο του μαθήματος είναι μια ξενάγηση στα δύο δωμάτια όπου θα περάσεις το μάθημα: την κονσόλα Dev Tools του Kibana και το Neo4j Browser.
Το demarrer αλυσιδώνει τρία βήματα του docker compose: pull (κατεβάζει τις εικόνες, ~6 GB την πρώτη φορά, τίποτα μετά), up -d (δημιουργεί και εκκινεί τα κοντέινερ στο παρασκήνιο), μετά ένας βρόχος αναμονής που ρωτά το docker inspect κάθε 3 δευτερόλεπτα μέχρι κάθε κοντέινερ να γίνει healthy.
Η λέξη healthy προέρχεται από το docker-compose.yml. Κάθε υπηρεσία δηλώνει εκεί ένα τεστ που το Docker επαναλαμβάνει τακτικά:
| Υπηρεσία | Τεστ που εκτελείται στο κοντέινερ | Ρυθμός |
|---|---|---|
| elasticsearch | το curl -fsS http://localhost:9200/_cluster/health πρέπει να περιέχει "status":"green" ή "yellow" | κάθε 10 δ., 30 προσπάθειες, 30 δ. χάριτος στην εκκίνηση |
| kibana | το curl -fsS http://localhost:5601/api/status πρέπει να περιέχει "level":"available" | κάθε 10 δ., 30 προσπάθειες, 40 δ. χάριτος |
| neo4j | το wget -qO- http://localhost:7474 απαντά | κάθε 10 δ., 30 προσπάθειες, 30 δ. χάριτος |
Όσο το τεστ αποτυγχάνει, το κοντέινερ είναι starting· όταν πετυχαίνει, healthy· μετά από 30 αποτυχίες, unhealthy. Η υπηρεσία kibana δηλώνει depends_on: elasticsearch: condition: service_healthy: το Compose την εκκινεί μόνο αφού το Elasticsearch γίνει healthy, κάτι που αποφεύγει το περίφημο «Kibana server is not ready yet» κατά την εκκίνηση.
Δύο λεπτομέρειες του αρχείου compose αξίζει να ειπωθούν από τώρα. xpack.security.enabled=false: κανένας κωδικός πρόσβασης ούτε πιστοποιητικό για το Elasticsearch, είναι ένα τοπικό εργαστήριο, ποτέ διαμόρφωση παραγωγής. cluster.routing.allocation.disk.threshold_enabled=false: το Elasticsearch δεν θα θέσει τα index σου σε λειτουργία μόνο ανάγνωσης αν ο δίσκος σου υπερβεί το 95% (μάθημα 04, βλάβη αρ. 5).
Πρώτα το kit του εργαστηρίου. Όλα τα παρακάτω εκτελούνται από τη ρίζα του kit — https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr — που αποκτήθηκε με
git clone https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-frή με το κουμπί Code → Download ZIP (μάθημα 02, βήμα 4). Αν τοdir(ή τοls) δεν δείχνει ταdocker-compose.yml,labo.shκαιlabo.ps1, δεν βρίσκεσαι στο σωστό μέρος.
Όλα τα ερωτήματα αυτού του μαθήματος: elasticsearch/requetes/01-03-premiere-visite.txt.
Εκκίνηση του stack. Από τον φάκελο του kit:
./labo.sh demarrer.\labo.ps1 demarrerΤο σενάριο εμφανίζει τρία μπλοκ: == Téléchargement des images == (μεγάλο την πρώτη φορά, σιωπηλό μετά), == Démarrage == (οι γραμμές Container labo-elasticsearch Started του Compose), μετά == Attente que chaque service soit prêt == όπου κάθε γραμμή γεμίζει με τελείες μέχρι prêt (…s). Τελειώνει με:
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-grapheΑυτό που πρέπει να δεις: τρία prêt (έτοιμο) (elasticsearch, kibana, neo4j). Υπολόγισε ένα με δύο λεπτά μετά το κατέβασμα. Αν μια γραμμή εμφανίζει unhealthy, exited ή délai dépassé (υπέρβαση προθεσμίας), πήγαινε στο μάθημα 04.
Φόρτωση των index Elasticsearch.
./labo.sh importerΗ εντολή δημιουργεί κάθε index με το mapping του (αρχεία elasticsearch/mappings/*.json) μετά στέλνει τα δεδομένα στο API _bulk. Πραγματική έξοδος, εδώ σε ένα εργαστήριο όπου τα index υπήρχαν ήδη (η εντολή επαναλαμβάνεται χωρίς κίνδυνο):
== 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.Αυτό που πρέπει να δεις: την πρώτη φορά, οι γραμμές — γίνονται ✔ index cours créé avec son mapping. Οι τρεις μετρητές πρέπει να είναι ακριβώς 504, 609 και 12000· τα έγγραφα φέρουν το δικό τους _id, οπότε η επανεκτέλεση της εισαγωγής δεν δημιουργεί ποτέ διπλότυπο.
Φόρτωση του γράφου Neo4j.
./labo.sh charger-grapheΤο σενάριο εκτελεί το neo4j/cypher/01-contraintes.cypher (περιορισμοί μοναδικότητας και index) μετά το 02-charger.cypher (LOAD CSV + MERGE για κάθε αρχείο του neo4j/import/). Τελειώνει με τον απολογισμό που υπολογίζει το τελευταίο ερώτημα του σεναρίου, 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.Αυτό που πρέπει να δεις: 872 κόμβοι συνολικά. Όλα γίνονται με MERGE, οπότε η επανεκτέλεση της εντολής δεν δημιουργεί κανένα διπλότυπο.
Ανάγνωση της κατάστασης του εργαστηρίου. Είναι η εντολή που θα πληκτρολογείς πιο συχνά:
./labo.sh etatΠραγματική έξοδος (εδώ με το ενεργό προφίλ OpenSearch, που δεν θα εμφανιστεί σε σένα πριν την ενότητα 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}Αυτό που πρέπει να δεις: (healthy) σε κάθε γραμμή κοντέινερ, green για το cluster, τους τρεις μετρητές index και 872 κόμβους. Χωρίς OpenSearch, η τελευταία γραμμή λέει — OpenSearch non démarré (profil optionnel : ./labo.sh demarrer opensearch).
Άνοιγμα του Kibana Dev Tools. Πήγαινε στο http://localhost:5601. Το Kibana είναι στα γαλλικά (I18N_LOCALE=fr-FR). Κάνε κλικ στο μενού ☰ πάνω αριστερά· στην ενότητα Management, επίλεξε Outils de développement (είναι η μετάφραση του «Dev Tools»· το breadcrumb εμφανίζει Outils de développement > Console). Φτάνεις στην καρτέλα Console, υποκαρτέλα Shell: έναν επεξεργαστή αριστερά, την απάντηση δεξιά. Το κουμπί Effacer cette entrée αδειάζει τον επεξεργαστή από το παράδειγμα.
Αυτό που πρέπει να δεις: όταν πληκτρολογείς ένα ερώτημα, εμφανίζεται ένα κουμπί ▶ («Cliquer pour envoyer la requête») στο τέλος της γραμμής. Συντόμευση: Ctrl + Enter (⌘ + Enter σε Mac) στέλνει το ερώτημα κάτω από τον δρομέα. Κάτω δεξιά της απάντησης, το Kibana εμφανίζει τον κωδικό HTTP και τη διάρκεια, για παράδειγμα 200 - OK 23 ms.
Πρώτο ερώτημα: η υγεία του 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
}Αυτό που πρέπει να δεις: "status": "green". Τα index του εργαστηρίου δημιουργούνται με number_of_replicas: 0, άρα κανένα replica σε αναμονή: green σε έναν μόνο κόμβο. Ο αριθμός των shards (53) περιλαμβάνει index συστήματος του Kibana· μπορεί να διαφέρει.
Ποιος απαντά, και ποια index υπάρχουν;
GET /Απάντηση: "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.6kbΑυτό που πρέπει να δεις: pri 1, rep 0, και τους τρεις μετρητές. Το ?v προσθέτει τη γραμμή κεφαλίδας, το s=index ταξινομεί κατά όνομα. Χωρίς το φίλτρο cours,avis,acces, το GET _cat/indices?v απαριθμεί επίσης εσωτερικά index που ξεκινούν με τελεία (.internal.alerts-…): αγνόησέ τα.
Ο κόμβος και οι μετρήσεις.
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 } }Το ίδιο για GET avis/_count → "count": 609 και GET acces/_count → "count": 12000. Αυτό που πρέπει να δεις: ένας μόνο κόμβος, master *, και heap.percent γύρω στο 40% με τη ρύθμιση -Xmx1g του compose.
Ένα πρώτο έγγραφο.
GET cours/_search{
"query": { "match_all": {} },
"size": 1
}Απάντηση (περικομμένη):
{
"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" },
…
}
}
]
}
}Αυτό που πρέπει να δεις: το hits.total.value = 504 (όλα τα έγγραφα ταιριάζουν με το match_all) αλλά μόνο ένα επιστρέφεται χάρη στο size: 1. Το Dev Tools στέλνει ένα GET με σώμα ως POST από κάτω· και τα δύο γίνονται δεκτά.
Άνοιγμα του Neo4j Browser και σύνδεση. Πήγαινε στο http://localhost:7474. Το παράθυρο Connect to instance προτείνει ήδη Protocol neo4j:// και Connection URL localhost:7687 (η θύρα Bolt, όχι η 7474). Άφησε το Database user στο neo4j, πληκτρολόγησε το Password aiopsatlas2026, κάνε κλικ Connect. Αυτό που πρέπει να δεις: πάνω, μια πράσινη κουκκίδα και Instance: neo4j://localhost:7687, Database: neo4j, User: neo4j. Το πλαίσιο Database information αριστερά εμφανίζει Nodes (872) με τις ετικέτες Competence, Cours, Etudiant, Professeur, Ville, και Relationships (3,712) με COUVRE, ENSEIGNE, HABITE, INSCRIT_A, PREREQUIS_DE. Η πλαϊνή μπάρα προτείνει Database overview, Saved Cypher, History, Cypher reference, Parameters, Settings (η διεπαφή είναι στα αγγλικά).
Πρώτο ερώτημα Cypher. Στον επεξεργαστή πάνω (neo4j$), πληκτρολόγησε μετά κάνε κλικ Run (ή Ctrl + Enter):
MATCH (c:Cours) RETURN c LIMIT 25Αυτό που πρέπει να δεις: ένα πλαίσιο αποτελεσμάτων με τρεις προβολές Graph, Table, Raw, και δεξιά Results overview : Nodes (25) · Cours (25). Στην προβολή Graph, 25 πορτοκαλί φυσαλίδες· κάνε κλικ σε μία για να δεις τις ιδιότητές της (id, titre, categorie, prix…). Στην προβολή Table, οι ίδιοι 25 κόμβοι σε JSON. Η ενότητα 6 σε μαθαίνει να γράφεις αυτά τα ερωτήματα· προς το παρόν, ξέρεις πού να τα πληκτρολογείς.
Και το OpenSearch; Το εργαστήριο μπορεί επίσης να ξεκινήσει το OpenSearch 3.8.0 και το OpenSearch Dashboards στις θύρες 9201 και 5602, με ./labo.sh demarrer opensearch μετά ./labo.sh importer opensearch. Είναι ένα προαιρετικό προφίλ του Compose: δεν εκκινείται εξ ορισμού, και απαιτεί 6 GB μνήμης για το Docker. Το ενεργοποιούμε στην ενότητα 5, για να ξαναπαίξουμε τα ίδια ερωτήματα και στους δύο κινητήρες και να συγκρίνουμε. Τίποτα να κάνεις σήμερα.
Το Kibana εμφανίζει «Kibana server is not ready yet» αμέσως μετά το demarrer → Το Kibana ξεκίνησε αλλά δεν έχει ολοκληρώσει ακόμα τη σύνδεσή του με το Elasticsearch και τη δημιουργία των εσωτερικών του index. Περίμενε 30 δευτερόλεπτα και ανανέωσε τη σελίδα. Αν το μήνυμα επιμένει πέρα από δύο λεπτά, ./labo.sh journal kibana (μάθημα 04, βλάβη αρ. 4).
Το Dev Tools απαντά 404 με "type": "index_not_found_exception", "reason": "no such index [cour]" → Ορθογραφικό λάθος στο όνομα του index (cour αντί cours). Το Elasticsearch δεν μαντεύει· το GET _cat/indices?v σου δίνει τα ακριβή ονόματα. Παραλλαγή: 400 με "no handler found for uri [/cours/_serch] and method [GET]" → είναι το όνομα του API που είναι λάθος γραμμένο (_serch).
Το Dev Tools απαντά 400 με x_content_parse_exception … was expecting double-quote to start field name → Το JSON είναι κακοσχηματισμένο, συνηθέστερα ένα κόμμα παραπάνω πριν το κλείσιμο αγκύλης ("size": 1, }). Ο επεξεργαστής υπογραμμίζει τη λανθασμένη γραμμή με κόκκινο πριν καν την αποστολή.
Neo4j Browser: «Connection to instance failed — The client is unauthorized due to authentication failure.» (λεπτομέρειες: Neo.ClientError.Security.Unauthorized) → Λανθασμένος κωδικός πρόσβασης. Είναι aiopsatlas2026 (καθορισμένος από το NEO4J_AUTH στο compose), όχι ο προεπιλεγμένος κωδικός neo4j. Ξαναπληκτρολόγησέ τον, χωρίς τελικό κενό.
Το etat εμφανίζει Neo4j répond — nœuds : 0 → Το κοντέινερ τρέχει αλλά ξέχασες το charger-graphe. Εκτέλεσέ το· χρειάζεται περίπου τριάντα δευτερόλεπτα.
demarrer, importer, charger-graphe. Οι δύο τελευταίες επαναλαμβάνονται χωρίς να δημιουργούν διπλότυπα (ρητά αναγνωριστικά στο Elasticsearch, MERGE στο Neo4j).demarrer περιμένει την κατάσταση healthy κάθε κοντέινερ· αυτή η κατάσταση προέρχεται από τα healthchecks του docker-compose.yml, και το Kibana ξεκινά μόνο μετά το Elasticsearch χάρη στο depends_on … service_healthy.etat με μια ματιά: (healthy) παντού, cluster green, 504 / 609 / 12000 έγγραφα, 872 κόμβοι.GET διαδρομή ακολουθούμενη από το JSON της κολλημένο από κάτω· Ctrl + Enter για αποστολή.localhost:7687, χρήστης neo4j, κωδικός aiopsatlas2026· τα αποτελέσματα διαβάζονται σε Graph, Table ή Raw.Το αρχείο 01-03-premiere-visite.txt είναι στην ακριβή μορφή του Dev Tools: στην κονσόλα, το κουμπί Importer les requêtes (εικονίδιο πάνω δεξιά του επεξεργαστή) δέχεται αυτό το αρχείο και το επικολλά στον επεξεργαστή. Όλα τα αρχεία του φακέλου elasticsearch/requetes/ λειτουργούν έτσι· δεν θα χρειαστεί ποτέ να ξαναπληκτρολογήσεις ένα ερώτημα του μαθήματος.