تشغيل المختبر يشبه إشعال مبنى من ثلاثة طوابق. demarrer يضغط على المفتاح العامّ ، ثم يمرّ حارس عبر كلّ طابق للتحقّق من أنّ النور مُشعل قبل أن يعطيك المفاتيح : هذا الحارس هو الفحوصات الصحّيّة (healthchecks) لـ Docker Compose. لا يملك Kibana حتّى الحقّ في البدء قبل أن يُعلن Elasticsearch سليمًا. ثم يملأ importer أرفف Elasticsearch ، ويملأ charger-graphe أرفف Neo4j ، ويعطيك etat خريطة المبنى : من هنا ، ومن يستجيب ، وكم مستندًا في كلّ فهرس. بقيّة الدرس زيارة موجّهة إلى الغرفتين اللتين ستقضي فيهما الدورة : وحدة تحكّم Dev Tools في Kibana و Neo4j Browser.
يسلسل demarrer ثلاث خطوات من docker compose : pull (يحمّل الصور ، حوالي 6 جيجابايت في المرّة الأولى ، لا شيء بعد ذلك) ، 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 فهارسك إلى قراءة فقط إذا تجاوز قرصك 95% (الدرس 04 ، العطل رقم 5).
حزمة المختبر أوّلًا. كلّ ما يلي يُشغَّل من جذر الحزمة — 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.
تشغيل الحزمة. من مجلّد الحزمة :
./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.
تحميل فهارس Elasticsearch.
./labo.sh importerيُنشئ الأمر كلّ فهرس بربطه الخاصّ (ملفّات elasticsearch/mappings/*.json) ثم يرسل البيانات إلى واجهة _bulk. مخرجات حقيقيّة ، هنا على مختبر كانت الفهارس فيه موجودة بالفعل (الأمر قابل للتنفيذ من جديد دون خطر) :
== 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 (تقيّدات التفرّد والفهارس) ثم 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) ، عدّادات الفهارس الثلاثة و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 » ؛ يعرض مسار التصفّح Outils de développement > Console). تصل إلى تبويب Console ، التبويب الفرعيّ Shell : محرّر إلى اليسار ، والاستجابة إلى اليمين. زرّ Effacer cette entrée يُفرغ المحرّر من المثال.
ما يجب أن تراه : عندما تكتب استعلامًا ، يظهر زرّ ▶ (« اضغط لإرسال الاستعلام ») في نهاية السطر. اختصار : Ctrl + Enter (⌘ + Enter على Mac) يرسل الاستعلام تحت المؤشّر. في أسفل يمين الاستجابة ، تعرض Kibana رمز HTTP والمدّة الزمنيّة ، مثلًا 200 - OK 23 ms.
أوّل استعلام : صحّة المجموعة.
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". فهارس المختبر أُنشئت بـ number_of_replicas: 0 ، لذا لا نسخة احتياطيّة (replica) في انتظار : green على عقدة واحدة. عدد الشظايا (53) يشمل فهارس نظاميّة تابعة لـ Kibana ؛ قد يتغيّر.
من يستجيب ، وما هي الفهارس الموجودة؟
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 أيضًا فهارس داخليّة تبدأ بنقطة (.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 بمتن (body) عبر 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 ، نفس العقد الخمس والعشرين بصيغة JSON. تعلّمك الوحدة 6 كتابة هذه الاستعلامات ؛ في الوقت الحاليّ ، أنت تعرف أين تكتبها.
وماذا عن OpenSearch؟ يمكن للمختبر أيضًا تشغيل OpenSearch 3.8.0 و OpenSearch Dashboards على المنفذين 9201 و 5602 ، بـ ./labo.sh demarrer opensearch ثم ./labo.sh importer opensearch. هذا ملف اختياريّ (profil optionnel) لـ Compose : لا يُشغَّل تلقائيًّا ، ويطلب 6 جيجابايت من الذاكرة لـ Docker. سنفعّله في الوحدة 5 ، لإعادة تنفيذ نفس الاستعلامات على المحرّكين والمقارنة بينهما. لا شيء تفعله اليوم.
يعرض Kibana « Kibana server is not ready yet » مباشرة بعد demarrer → بدأ Kibana لكنّه لم ينتهِ بعد من الاتّصال بـ Elasticsearch وإنشاء فهارسه الداخليّة. انتظر 30 ثانية وأعد تحميل الصفحة. إذا استمرّت الرسالة أكثر من دقيقتين ، ./labo.sh journal kibana (الدرس 04 ، العطل رقم 4).
يجيب Dev Tools بـ 404 مع "type": "index_not_found_exception", "reason": "no such index [cour]" → خطأ إملائيّ في اسم الفهرس (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 لكلّ حاوية ؛ تأتي هذه الحالة من الفحوصات الصحّيّة في docker-compose.yml ، ولا يبدأ Kibana إلّا بعد Elasticsearch بفضل depends_on … service_healthy.etat بنظرة واحدة : (healthy) في كلّ مكان ، مجموعة 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/ تعمل بهذه الطريقة ؛ لن تحتاج أبدًا إلى إعادة كتابة استعلام من الدورة.