يا جماعة الخير، السلام عليكم ورحمة الله وبركاته. معكم أخوكم أبو عمر.
خلوني أحكي لكم قصة صارت معي قبل كم سنة، قصة علّمتني درس ما بنساه للموت. كانت الساعة حوالي 2 بعد نص الليل، وأنا قاعد بشتغل على مشروع ضخم لعميل مهم. فجأة، بلّشت توصلني تنبيهات على الموبايل زي المطر: “فشل في عملية الدفع”، “بوابة الدفع لا تستجيب”. قلبي نزل عند ركبي. هاي كارثة بتعني خسارة فلوس للعميل، وخسارة سمعة إلنا.
جمّعت الفريق بسرعة على اتصال طارئ. التطبيق تبعنا كان مبني على نظام الخدمات المصغرة (Microservices)، يعني عندنا حوالي 7 أو 8 خدمات مختلفة بتتكلم مع بعض عشان تكمل عملية دفع واحدة. خدمة للمستخدمين، خدمة للمنتجات، خدمة للسلة، خدمة للطلبات، وخدمة بتتكلم مع بوابة الدفع نفسها. المشكلة وين؟ الله أعلم.
وبدأ الجحيم. كل واحد فينا صار يعمل SSH على سيرفر مختلف. أنا على سيرفر الطلبات، وزميلي على سيرفر بوابة الدفع، والثالث على سيرفر المستخدمين. كل واحد فاتح الطرفية (Terminal) وعم بكتب tail -f /var/log/app.log | grep ERROR. عيوننا صارت حمرا وإحنا بنحاول نلاقي أي إشي يدل على المشكلة. بنشوف خطأ في خدمة، بس ما بنعرف شو اللي سببه في الخدمة اللي قبلها. السجلات متناثرة، غير متزامنة، وكل واحد بصيغة. بعد ساعتين من التخبيص والضياع، اكتشفنا المشكلة: تحديث بسيط في مكتبة خارجية غيّر طريقة تعاملها مع الـ Timezone، فكانت الطلبات بتوصل لبوابة الدفع بتوقيت غلط فبترفضها. ساعتين ضاعوا من عمرنا، وكثير من أعصابنا، عشان نلاقي مشكلة كان ممكن نلاقيها بدقايق.
في هداك اليوم، بعد ما حلينا المصيبة، عملت اجتماع وقلت للفريق: “يا جماعة، هالحكي ما بنفع. شغلنا هيك زي اللي بدور على إبرة بكومة قش… وكل كومة بسيرفر مختلف. لازم نلاقي حل يجمع كل هالسجلات بمكان واحد”. ومن هنا بدأت رحلتنا مع التجميع المركزي للسجلات.
ما هو الجحيم الذي كنا نعيشه؟ (مشاكل السجلات المتناثرة)
قبل ما نحكي عن الحل، خلونا نفصّل أكتر بالمشكلة عشان نحس بقيمة الحل. الوضع اللي كنا فيه، واللي للأسف كثير من الفرق لسا عايشة فيه، كان عبارة عن:
- الدخول المتعدد للخوادم: عشان تتبع طلب واحد، كنت محتاج تفتح 4 أو 5 نوافذ SSH على سيرفرات مختلفة. هذا بحد ذاته مرهق وبضيع وقت.
- أدوات بدائية: الاعتماد على
grep,tail,lessوawk. هاي الأدوات عظيمة للملفات المحلية، لكنها كابوس لما بدك تربط أحداث بين عدة ملفات على عدة سيرفرات. - فقدان السياق (Correlation): لما تشوف خطأ في خدمة “الطلبات”، كيف بدك تعرف شو هي البيانات اللي استقبلتها من خدمة “السلة” واللي أدت لهذا الخطأ؟ بدون وجود مُعرّف موحد للطلب (Correlation ID) يمر بين كل الخدمات، أنت فعليًا أعمى.
- ضياع السجلات: السجلات على الخوادم عادةً بيصيرلها عملية تدوير (Log Rotation) عشان ما تملأ القرص الصلب. يعني ممكن سجلات خطأ قديم إلها كم يوم تكون انمسحت للأبد.
- مشاكل أمنية وصلاحيات: هل من المنطقي تعطي كل مطور صلاحية SSH على خوادم الإنتاج (Production) عشان يشوف السجلات؟ طبعًا لأ. هذا بيفتح باب لمشاكل أمنية لا حصر لها.
الضوء في نهاية النفق: التجميع المركزي للسجلات (Centralized Logging)
الفكرة بكل بساطة عبقرية: بدل ما كل خدمة تكتب سجلاتها في ملف محلي على السيرفر تبعها، بنخلي كل الخدمات “تبث” أو “ترسل” سجلاتها إلى مكان مركزي واحد. هذا المكان المركزي هو عبارة عن نظام متخصص في استقبال، تخزين، فهرسة، والبحث في كميات هائلة من السجلات.
لما يصير عندك نظام زي هيك، الفوائد بتكون خرافية:
- مكان واحد للحقيقة (Single Source of Truth): كل سجلاتك، من كل خدماتك، في مكان واحد. بدك تبحث عن خطأ؟ بتروح على واجهة مستخدم واحدة وبتبحث.
- بحث وتحليل قوي: هاي الأنظمة بتوفر لغات استعلام (Query Language) قوية جدًا بتخليك تفلتر السجلات، تبحث عن أنماط معينة، وتشوف الأخطاء اللي صارت في خدمة معينة خلال فترة زمنية محددة بضغطة زر.
- ربط الأحداث (Event Correlation): بتقدر تبحث عن “Correlation ID” معين وتشوف رحلة هذا الطلب بالكامل عبر كل الخدمات المصغرة، من لحظة دخوله للنظام للحظة خروجه.
- تنبيهات ذكية (Alerting): بتقدر تعمل تنبيهات ذكية. مثلاً: “إذا صار عندي أكتر من 100 خطأ من نوع 500 في خدمة الدفع خلال دقيقة واحدة، ابعثلي إيميل واتصل فيي”.
- رسوم بيانية ولوحات عرض (Visualization & Dashboards): تحويل السجلات من مجرد نصوص إلى رسوم بيانية جميلة. بتقدر تعمل داشبورد يعرض لك صحة النظام، عدد الأخطاء في كل خدمة، أبطأ العمليات، إلخ.
أشهر الأبطال في ساحة المعركة: ELK Stack و Loki
في عالم تجميع السجلات، في بطلين مشهورين كل واحد إله فلسفته الخاصة. خلونا نتعرف عليهم.
مكدس ELK: الوحش الكلاسيكي (The Classic Beast)
ELK هو اختصار لثلاثة مشاريع مفتوحة المصدر بتشتغل مع بعضها بشكل رائع:
- E – Elasticsearch: هو القلب النابض. محرك بحث وتحليل قوي جدًا مبني على مكتبة Lucene. هو اللي بخزن كل السجلات وبيفهرسها عشان يخلي عملية البحث سريعة بشكل صاروخي، حتى لو عندك مليارات السجلات.
- L – Logstash: هو عامل التوصيل والمعالجة. Logstash هو عبارة عن “pipeline” بيستقبل السجلات من مصادر مختلفة (ملفات، Docker, …)، بيقدر يعالجها ويغير صيغتها (مثلاً يحول سطر نص عادي إلى صيغة JSON منظمة)، وبعدين يرسلها إلى Elasticsearch.
- K – Kibana: هي الواجهة الجميلة. Kibana بتخليك تتصفح، تبحث، وتعمل رسوم بيانية ولوحات عرض (Dashboards) مذهلة من البيانات المخزنة في Elasticsearch.
نصيحة أبو عمر: في نسخة أحدث من المكدس اسمها Elastic Stack، وصاروا يستخدموا أداة أخف من Logstash اسمها Beats (مثل Filebeat) عشان تجمع السجلات وترسلها. الفكرة العامة ما زالت نفسها.
متى تختار ELK؟ لما تكون محتاج تحليل عميق جدًا للبيانات، لما تكون سجلاتك غنية بالمعلومات وبدك تعمل استعلامات معقدة على محتوى السجلات نفسها. ELK قوي جدًا، لكنه بيستهلك موارد (رام ومعالج) بشكل كبير، وإعداده ممكن يكون معقد شوي.
Grafana Loki: البطل الخفيف والرشيق (The Lean and Agile Hero)
Loki هو لاعب أحدث في الساحة، لكنه اكتسب شعبية جبارة بسبب فلسفته المختلفة والمبتكرة. الفلسفة تبعته هي: “زي Prometheus، بس للسجلات”.
فكرته كالتالي: بدل ما يفهرس محتوى السجل بالكامل (زي Elasticsearch)، لوكي بيفهرس فقط “الوسوم” أو “العلامات” (Labels) المرتبطة بالسجل، زي اسم الخدمة، اسم السيرفر، البيئة (انتاج، تجريبي)، إلخ. أما نص السجل نفسه، فبيتم ضغطه وتخزينه كما هو.
هذا النهج له ميزتين أساسيتين:
- خفيف جدًا: لأنه ما بفهرس كل كلمة في السجلات، فهو بيحتاج موارد أقل بكثير من Elasticsearch.
- أقل تكلفة: تخزين الفهارس (Indexes) هو اللي بكلف مساحة وموارد. بما إنه فهارس Loki أصغر بكثير، فالتكلفة الإجمالية أقل.
المكدس الخاص بـ Loki عادةً يتكون من:
- Loki: الخادم الرئيسي اللي بخزن وبيستعلم عن السجلات.
- Promtail: هو العميل (Agent) اللي بيشتغل على سيرفراتك، بيقرأ السجلات وبيضيفلها الـ Labels الصحيحة (مثلاً من Docker labels أو Kubernetes labels) وبيرسلها لـ Loki.
- Grafana: نعم، نفس Grafana اللي بنستخدمها للرسوم البيانية للمقاييس (Metrics) من Prometheus. Grafana عندها دعم ممتاز لـ Loki كمصدر بيانات (Data Source)، فبتقدر تشوف مقاييسك وسجلاتك في نفس الداشبورد.
متى تختار هذا أو ذاك؟ قرار أبو عمر
ببساطة يا جماعة:
- إذا كنت شركة كبيرة، عندك فريق DevOps متخصص، وبتحتاج تعمل تحليلات معقدة جدًا على محتوى السجلات (Full-text search on content)، وعندك الميزانية والموارد اللازمة، ELK Stack هو وحش لا يُعلى عليه.
- إذا كنت فريق صغير أو متوسط، أو بتستخدم Grafana و Prometheus أصلًا، وبهمك بشكل أساسي إنك تبحث وتفلتر السجلات بناءً على الخدمة أو السيرفر أو معرف الطلب (Correlation ID)، وبدك حل خفيف وسهل الإعداد وقليل التكلفة، Grafana Loki هو خيارك الأمثل. بصراحة، لـ 90% من حالات تتبع الأخطاء اليومية، Loki أكتر من كافي.
لنطبق الأمر عملياً: مثال بسيط مع Docker و Loki
الحكي النظري حلو، بس خلينا نشوف شغل عملي. رح نعمل مثال بسيط جدًا باستخدام Docker Compose عشان نشغّل Loki, Promtail, Grafana, وتطبيق بسيط يطبع سجلات.
أنشئ مجلد جديد، وداخله أنشئ الملفات التالية:
1. ملف docker-compose.yml:
version: "3"
services:
loki:
image: grafana/loki:2.9.2
ports:
- "3100:3100"
command: -config.file=/etc/loki/local-config.yaml
promtail:
image: grafana/promtail:2.9.2
volumes:
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml
command: -config.file=/etc/promtail/config.yml
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
# تطبيق بسيط يطبع سجلات كل ثانية
my-app:
image: alpine
command: sh -c 'while true; do echo "[`date`] INFO: Processing user request..."; sleep 5; echo "[`date`] WARN: User cache is getting full."; sleep 5; done'
2. ملف promtail-config.yml:
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: containers
static_configs:
- targets:
- localhost
labels:
job: container-logs
__path__: /var/lib/docker/containers/*/*-json.log
pipeline_stages:
- docker: {}
- static_labels:
# نضيف علامة ثابتة لتمييز السجلات القادمة من هذا الـ Promtail
host: my-local-machine
الشرح السريع:
docker-compose.yml: يقوم بتشغيل 4 خدمات: Loki (الخادم)، Promtail (جامع السجلات)، Grafana (الواجهة)، و `my-app` (تطبيق وهمي يطبع سجلات كل 5 ثواني).promtail-config.yml: هذا هو العقل المدبر لـ Promtail. نحن نقول له أن يقرأ سجلات Docker مباشرة من النظام (من المسار/var/lib/docker/containers). سيقوم تلقائيًا بإضافة وسوم (labels) لكل سجل بناءً على معلومات الحاوية (اسم الحاوية، اسم الصورة، إلخ).
خطوات التشغيل:
- ضع الملفين في نفس المجلد.
- افتح الطرفية (Terminal) في هذا المجلد وشغل الأمر:
docker-compose up - انتظر قليلاً حتى تعمل كل الحاويات.
- افتح المتصفح واذهب إلى
http://localhost:3000(هذا هو Grafana). - اسم المستخدم وكلمة المرور الافتراضية هما
admin/admin. سيطلب منك تغيير كلمة المرور. - من القائمة الجانبية، اذهب إلى Connections > Data Sources > Add data source.
- اختر Loki.
- في حقل URL، اكتب:
http://loki:3100 - اضغط “Save & Test”. المفروض تشوف رسالة خضراء تقول “Data source connected”.
- الآن، من القائمة الجانبية، اذهب إلى “Explore”.
- من القائمة المنسدلة في الأعلى، تأكد من اختيار “Loki”.
- في حقل “Log browser”، اضغط على
{job="container-logs"}ثم{container_name="your-folder-name-my-app-1"}. - مبروك! سترى الآن السجلات القادمة مباشرة من حاوية
my-app. جرب كتابة استعلامات مثل{container_name="your-folder-name-my-app-1"} |= "WARN"للبحث عن السجلات التي تحتوي على كلمة “WARN”.
هذا مثال بسيط جدًا، لكنه يوضح قوة الفكرة. تخيل أن لديك 50 خدمة، Promtail سيقوم بجمع سجلاتها كلها وإرسالها إلى Loki، وستتمكن من تصفحها والبحث فيها من مكان واحد. شغل مرتب!
نصائح أبو عمر الذهبية
بعد سنوات من التعامل مع السجلات، اسمحولي أقدم لكم كم نصيحة من القلب:
- وحّدوا صيغة سجلاتكم (Standardize Your Logs): أهم نصيحة على الإطلاق. اتفقوا مع الفريق على صيغة موحدة للسجلات، وأفضل صيغة هي JSON. السجلات بصيغة JSON سهلة جدًا على الأنظمة مثل Logstash أو Promtail أنها تحللها وتستخرج منها حقول ووسوم بشكل تلقائي. بدل ما يكون السجل مجرد نص، بصير عبارة عن كائن (object) منظم.
- استخدموا مُعرّف الطلب (Correlation ID): لما يوصل طلب جديد لنظامك (مثلاً لـ API Gateway)، قم بإنشاء مُعرّف فريد (UUID مثلاً)، وضعه في Header الطلب. كل خدمة مصغرة تستلم هذا الطلب، يجب أن تقرأ هذا المُعرّف وتضعه في كل سجلاتها المتعلقة بهذا الطلب. بهذه الطريقة، يمكنك البحث عن هذا الـ ID في Loki/Kibana ورؤية قصة حياة الطلب كاملة عبر كل الخدمات.
- لا تسجل كل شيء، ولا تسجل القليل جدًا: استخدم مستويات السجلات (Log Levels) بحكمة (DEBUG, INFO, WARN, ERROR, FATAL). في بيئة الإنتاج، عادةً ما يتم تسجيل INFO فما فوق. سجل ما يكفي لتشخيص المشاكل، ولكن تجنب إغراق النظام بسجلات DEBUG غير الضرورية.
- لا تسجل معلومات حساسة: إياك ثم إياك أن تسجل كلمات مرور، أرقام بطاقات ائتمان، أو مفاتيح API في سجلاتك. هذه كارثة أمنية. استخدم تقنيات “Masking” لإخفاء هذه المعلومات تلقائيًا.
الخلاصة: من الفوضى إلى النظام 🚀
يا جماعة، الانتقال من السجلات المتناثرة إلى نظام تجميع مركزي هو ليس رفاهية، بل هو ضرورة قصوى لأي تطبيق حديث. هو الفرق بين قضاء ليلة كاملة في البحث عن خطأ، وبين إيجاده في خمس دقائق وأنت تشرب فنجان قهوتك.
سواء اخترت وحش الـ ELK أو رشاقة Loki، المهم أن تبدأ. ابدأ صغيرًا، حتى لو بالمثال اللي عرضناه. ستكتشف أن الوقت والجهد الذي ستستثمره في إعداد هذا النظام سيعود عليك بأضعاف مضاعفة من راحة البال، وسرعة حل المشاكل، وجودة المنتج النهائي.
لا تنتظروا حريق الساعة الثانية صباحًا عشان تبنوا محطة الإطفاء الخاصة بكم. ابدأوا اليوم.
والله ولي التوفيق.