تشغيل المختبر والقيام بجولة عبر الواجهات

10 دقيقة
الجمهور
مبتدئ ، متطلّبات الدرس 02 كلّها خضراء
المدة
30 دقيقة (بالإضافة إلى أوّل تحميل للصور)
الوحدة
1/7
المهارة المستهدفة
تشغيل الحزمة ، تحميل البيانات ، قراءة etat ، إرسال أوّل استعلام في Kibana Dev Tools وفي Neo4j Browser

في صورة واحدة

تشغيل المختبر يشبه إشعال مبنى من ثلاثة طوابق. 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 تنفيذه بانتظام :

الخدمةالاختبار المُنفَّذ داخل الحاويةالتواتر
elasticsearchcurl -fsS http://localhost:9200/_cluster/health يجب أن يحتوي على "status":"green" أو "yellow"كلّ 10 ثوانٍ ، 30 محاولة ، 30 ثانية سماح عند البدء
kibanacurl -fsS http://localhost:5601/api/status يجب أن يحتوي على "level":"available"كلّ 10 ثوانٍ ، 30 محاولة ، 40 ثانية سماح
neo4jwget -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.

  1. تشغيل الحزمة. من مجلّد الحزمة :

    bash
    ./labo.sh demarrer
    powershell
    .\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). يُنهي بـ :

    text
    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.

  2. تحميل فهارس Elasticsearch.

    bash
    ./labo.sh importer

    يُنشئ الأمر كلّ فهرس بربطه الخاصّ (ملفّات elasticsearch/mappings/*.json) ثم يرسل البيانات إلى واجهة _bulk. مخرجات حقيقيّة ، هنا على مختبر كانت الفهارس فيه موجودة بالفعل (الأمر قابل للتنفيذ من جديد دون خطر) :

    text
    == 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) ، لذا إعادة تنفيذ الاستيراد لا تُنشئ أبدًا نسخة مكرّرة.

  3. تحميل الرسم البيانيّ Neo4j.

    bash
    ./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 :

    text
    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 ، لذا إعادة تنفيذ الأمر لا تُنشئ أيّ نسخة مكرّرة.

  4. قراءة حالة المختبر. هذا هو الأمر الذي ستكتبه أكثر من غيره :

    bash
    ./labo.sh etat

    مخرجات حقيقيّة (هنا مع ملف OpenSearch مفعَّلًا ، الذي لن يظهر عندك قبل الوحدة 5) :

    text
    == 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).

  5. فتح 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.

  6. أوّل استعلام : صحّة المجموعة.

    text
    GET _cluster/health
    json
    {
      "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 ؛ قد يتغيّر.

  7. من يستجيب ، وما هي الفهارس الموجودة؟

    text
    GET /

    الاستجابة : "name": "labo-es-1" ، "cluster_name": "labo" ، "version": { "number": "9.5.3", …, "lucene_version": "10.5.1" } ، "tagline": "You Know, for Search".

    text
    GET _cat/indices/cours,avis,acces?v&s=index
    text
    health 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-…) : تجاهلها.

  8. العقدة والعدّادات.

    text
    GET _cat/nodes?v
    text
    ip         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-1
    text
    GET cours/_count
    json
    { "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.

  9. مستند أوّل.

    text
    GET cours/_search
    json
    {
      "query": { "match_all": {} },
      "size": 1
    }

    الاستجابة (مقتطعة) :

    json
    {
      "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 تحت الغطاء ؛ الطريقتان مقبولتان.

  10. فتح 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 (الواجهة بالإنجليزيّة).

  11. أوّل استعلام Cypher. في المحرّر بالأعلى (neo4j$) ، اكتب ثم اضغط Run (أو Ctrl + Enter) :

    cypher
    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 كتابة هذه الاستعلامات ؛ في الوقت الحاليّ ، أنت تعرف أين تكتبها.

  12. وماذا عن 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 عقدة.
  • Kibana Dev Tools : القائمة ☰ → Management → Outils de développement ؛ الاستعلام = سطر GET مسار يتبعه صيغة JSON ملصقة تحته ؛ Ctrl + Enter للإرسال.
  • Neo4j Browser : http://localhost:7474 ، اتّصال Bolt على localhost:7687 ، المستخدم neo4j ، كلمة السرّ aiopsatlas2026 ؛ تُقرأ النتائج بصيغة Graph أو Table أو Raw.

للمزيد

الملفّ 01-03-premiere-visite.txt بصيغة مطابقة لـ Dev Tools بالضبط : في وحدة التحكّم ، زرّ Importer les requêtes (أيقونة في أعلى يمين المحرّر) يقبل هذا الملفّ ويلصقه في المحرّر. كلّ ملفّات مجلّد elasticsearch/requetes/ تعمل بهذه الطريقة ؛ لن تحتاج أبدًا إلى إعادة كتابة استعلام من الدورة.