لماذا تمركز السجلات على VPS بدلًا من الاكتفاء بـ SSH
حين تصيب مشكلةٌ عدة خدمات في الوقت ذاته — انتهاء مهلة على مستوى التطبيق، وخطأ 502 على مستوى nginx، واستثناء داخل worker — لا تتيح أداة SSH واحدة ربط هذه الأحداث دون قراءتها واحدة تلو الأخرى. docker compose logs -f app ثم docker compose logs -f nginx ثم docker compose logs -f worker: هذا تشخيص تسلسلي على خادم واحد. وعلى خادمَي VPS يتضاعف الوقت، وعلى ثلاثة يتضاعف ثلاثًا.
Loki هو نظام مفتوح المصدر لتجميع السجلات، طوّرته Grafana Labs وهو مدرج في كتالوج CNCF. على عكس Elasticsearch الذي يُفهرس كامل محتوى كل سطر، لا تُفهرس Loki إلا التسميات — البيانات الوصفية الخفيفة كاسم الحاوية والخدمة والبيئة. أما السجلات ذاتها فتُضغط وتُخزَّن كما هي. لهذا الاختيار التصميمي نتيجة مباشرة: تعمل Loki بارتياح على VPS بذاكرة 2 غيغابايت، في حين تتطلب مكدّسة ELK الأساسية 8 إلى 16 غيغابايت على الأقل لتبقى مستقرة. بالنسبة لمطور أو مسؤول نظام يدير تطبيقات باستضافة ذاتية متعددة بدون ميزانية APM مؤسسية، هذا هو الفارق بين حل قابل للتطبيق وحل مثالي غير عملي.
ما تقدمه هذه المكدّسة بشكل ملموس
- بحث موحّد عبر جميع الحاويات — استعلام LogQL واحد يشمل جميع الخدمات على كل VPS الخاص بك، دون SSH أو
grep موزّع. - ربط زمني — تعرض Grafana كلًا من Loki (السجلات) وPrometheus (المقاييس) على نفس محور الزمن: ترى خطأ 502 وذروة CPU التي سبقته في عرض واحد.
- لا حصص استيعاب — تخزّن من السجلات ما يسمح به قرصك دون اشتراك أو حدود شهرية مفروضة من طرف ثالث.
- بصمة ذاكرة محدودة — تستهلك Loki ذاكرة أقل بكثير من Elasticsearch عند نفس حجم السجلات، بفضل الفهرسة بالتسميات فقط.
- احتفاظ قابل للتهيئة — حدّد مدة الاحتفاظ لكل تسمية (
chunk_retain_period،retention_period) وفقًا لقيود القرص والامتثال لديك. - تكامل أصلي مع Grafana — Loki هو مصدر بيانات من الدرجة الأولى في Grafana: لا مكوّن إضافي من طرف ثالث، ولا واجهة API وسيطة.
- تنبيهات على محتوى السجلات — يمكن لـ Grafana تشغيل تنبيه حين يطابق تعبير نظامي تدفق Loki، دون المرور بخدمة خارجية.
- البيانات مستضافة على بنيتك التحتية — تبقى السجلات على VPS الخاص بك دون عبور خدمة سحابية.
المتطلبات الأساسية قبل النشر
مكدّسة Loki + Promtail + Grafana خفيفة، لكن لها متطلباتها الخاصة. إليك ما تحتاجه قبل البدء.
الموارد الموصى بها لـ VPS: يُمثّل 2 vCPU و2 غيغابايت RAM الحدَّ الأدنى المعقول للاستخدام على خادم واحد مع عشرات الحاويات النشطة. إن كنت تجمع سجلات من عدة VPS أو عدد كبير من الخدمات، استهدف 4 غيغابايت RAM. لا تحتفظ Loki بالسجلات في الذاكرة: تضغطها وتكتبها على القرص، مما يُبقي بصمة الذاكرة مستقرة بمرور الوقت. خطّط لـ 20 إلى 50 غيغابايت من تخزين SSD بحسب حجم سجلاتك ومدة الاحتفاظ.
البرامج المطلوبة: Docker وDocker Compose v2 مثبَّتان على VPS، ونطاق فرعي مخصص (logs.your-domain.com) يشير إلى عنوان IP للـ VPS، مع إمكانية الوصول الداخلي إلى المنفذين 3100 (Loki) و3000 (Grafana). يجب ألا يُكشف المنفذ 3100 للعموم — Loki لا تمتلك طبقة مصادقة أصلية، ويُكشف Grafana فقط عبر بروكسي عكسي.
النشر خطوة بخطوة
إنشاء هيكل الدلائل
تواصل مع VPS الخاص بك وأنشئ دليلًا مخصصًا: mkdir -p /opt/loki-stack/{loki,promtail} && cd /opt/loki-stack. سيستضيف هذا المجلد ملفات إعداد Loki وPromtail إلى جانب docker-compose.yml.
كتابة إعداد Loki
أنشئ /opt/loki-stack/loki/loki-config.yaml بالمحتوى التالي:
auth_enabled: false — يُعطّل المصادقة متعددة المستأجرين، وهو كافٍ للاستخدام على VPS واحد.
server: { http_listen_port: 3100 }
ingester: { lifecycler: { address: 127.0.0.1, ring: { kvstore: { store: inmemory }, replication_factor: 1 } }, chunk_idle_period: 5m, chunk_retain_period: 30s }
schema_config: { configs: [ { from: 2020-10-24, store: boltdb-shipper, object_store: filesystem, schema: v11, index: { prefix: index_, period: 24h } } ] }
storage_config: { boltdb_shipper: { active_index_directory: /loki/boltdb-shipper-active, cache_location: /loki/boltdb-shipper-cache, shared_store: filesystem }, filesystem: { directory: /loki/chunks } }
limits_config: { retention_period: 720h }
كتابة إعداد Promtail
أنشئ /opt/loki-stack/promtail/promtail-config.yaml. Promtail هو العميل الذي يجمع السجلات من حاويات Docker ويرسلها إلى Loki:
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: docker, docker_sd_configs: [ { host: unix:///var/run/docker.sock, refresh_interval: 5s } ], relabel_configs: [ { source_labels: ['__meta_docker_container_name'], regex: '/(.*)', target_label: container }, { source_labels: ['__meta_docker_container_log_stream'], target_label: stream } ] } ]
يستخدم هذا الإعداد الاكتشاف التلقائي لـ Docker (docker_sd_configs)، الذي يكتشف كل حاوية مُشغَّلة دون تدخل يدوي.
كتابة ملف docker-compose.yml
أنشئ /opt/loki-stack/docker-compose.yml:
version: '3.8'
services:
loki: image: grafana/loki:3.0.0 ports: ['127.0.0.1:3100:3100'] volumes: [loki-data:/loki, ./loki/loki-config.yaml:/etc/loki/local-config.yaml] command: -config.file=/etc/loki/local-config.yaml restart: unless-stopped
promtail: image: grafana/promtail:3.0.0 volumes: [/var/run/docker.sock:/var/run/docker.sock:ro, ./promtail/promtail-config.yaml:/etc/promtail/config.yml, /var/log:/var/log:ro] command: -config.file=/etc/promtail/config.yml restart: unless-stopped depends_on: [loki]
grafana: image: grafana/grafana:latest ports: ['127.0.0.1:3000:3000'] volumes: [grafana-data:/var/lib/grafana] environment: [GF_SECURITY_ADMIN_PASSWORD=غيّر-هذا] restart: unless-stopped depends_on: [loki]
volumes: loki-data: grafana-data:
لاحظ أن Loki (3100) وGrafana (3000) مرتبطان بـ 127.0.0.1 فقط — لا يمكن الوصول إليهما مباشرةً من الخارج.
تشغيل المكدّسة
من داخل /opt/loki-stack، شغّل: docker compose up -d. انتظر 20 إلى 30 ثانية ثم تحقق من أن الحاويات الثلاث تعمل: docker compose ps. ثم تحقق من سجلات Loki لتأكيد أنها بدأت دون أخطاء: docker compose logs -f loki. يجب أن ترى السطر msg="Loki started" في المخرجات.
إعداد البروكسي العكسي لـ Grafana
اكشف Grafana عبر nginx بنطاق فرعي وشهادة TLS. أنشئ /etc/nginx/sites-available/grafana.conf:
server { listen 443 ssl; server_name logs.your-domain.com; ssl_certificate /etc/letsencrypt/live/logs.your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/logs.your-domain.com/privkey.pem; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }
aحصل على الشهادة بـ: certbot certonly --nginx -d logs.your-domain.com، ثم فعّل الموقع: ln -s /etc/nginx/sites-available/grafana.conf /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx.
إضافة Loki كمصدر بيانات في Grafana
افتح https://logs.your-domain.com في متصفحك. سجّل الدخول باسم المستخدم admin وكلمة المرور التي حددتها في GF_SECURITY_ADMIN_PASSWORD. انتقل إلى Connections → Data sources → Add data source، اختر Loki، وأدخل الرابط: http://loki:3100. انقر Save & test — يجب أن ترى الرسالة Data source connected and labels found.
استعلام أول سجلاتك
في Grafana، افتح تبويب Explore وحدد مصدر Loki. استخدم Log Browser لاختيار تسمية container والتصفية باسم الحاوية. على سبيل المثال، لرؤية جميع سجلات حاوية nginx: {container="nginx"}. للتصفية على سلسلة نصية: {container="nginx"} |= "error". لحساب الأخطاء بحسب الحاوية خلال الساعة الأخيرة: sum by (container) (count_over_time({container=~".+"} |= "error" [1h])).
جمع سجلات من VPS ثانٍ
لمركزة سجلات VPS ثانٍ في نفس Loki، ثبّت Promtail فقط على ذلك VPS الثانوي. في إعداده، استبدل رابط العميل بالعنوان الداخلي لـ VPS الأول: url: http://<Loki-VPS-IP>:3100/loki/api/v1/push. افتح المنفذ 3100 فقط بين الخادمَين (جدار حماية أو شبكة خاصة)، ولا تكشفه للعموم أبدًا. أضف تسمية ثابتة لتمييز السجلات بحسب الخادم: static_configs: [ { labels: { host: vps2 } } ].
الإعداد بعد التثبيت: الاحتفاظ والنسخ الاحتياطي والتنبيهات
بعد تشغيل المكدّسة، ثمة ثلاثة تعديلات مهمة قبل اعتبار النشر مكتملًا.
الاحتفاظ. تمثل قيمة retention_period: 720h في loki-config.yaml ما يعادل 30 يومًا. اضبط هذا المعامل بحسب المساحة المتاحة على قرصك (docker system df للتحقق من استخدام الأحجام). تُطبّق Loki الاحتفاظ في الخلفية دون تأثير ملحوظ على الأداء.
النسخ الاحتياطي. يحتوي حجم loki-data على فهارس BoltDB والمقاطع المضغوطة — وهو العنصر الوحيد الذي يحتاج نسخًا احتياطيًا. يكفي لقطة يومية لحجم Docker (docker run --rm -v loki-data:/data -v /backup:/backup alpine tar czf /backup/loki-$(date +%F).tar.gz /data) للاستخدام القياسي. لا تنسَ تضمين حجم grafana-data في النسخ الاحتياطي (يحتوي على لوحات التحكم والتنبيهات).
التنبيهات على محتوى السجلات. في Grafana، أنشئ تنبيهًا من نوع Grafana managed alert على استعلام Loki: مثلًا، تشغيل إشعار حين يتجاوز عدد السطور التي تحتوي على FATAL الصفر خلال آخر 5 دقائق. هيّئ قناة إشعارات (بريد إلكتروني، Slack، webhook) في Alerting → Contact points قبل إنشاء القاعدة.
لا تُوفّر Loki أي آلية مصادقة أصلية على المنفذ 3100. إن احتجت إلى كشف واجهة Loki لشبكة غير موثوقة (لعملاء Promtail بعيدين مثلًا)، ضع بروكسي عكسيًا لـ nginx مع مصادقة بشهادة العميل أو basic auth أمامه. لا تترك المنفذ 3100 مفتوحًا على 0.0.0.0 في VPS الإنتاج أبدًا. على جانب Grafana، فعّل المصادقة القوية: GF_AUTH_ANONYMOUS_ENABLED=false وGF_USERS_ALLOW_SIGN_UP=false في متغيرات بيئة الحاوية.
استكشاف الأخطاء: الأخطاء الشائعة وكيفية حلها
إليك المشكلات الأكثر شيوعًا عند نشر مكدّسة Loki + Promtail + Grafana.
الأخطاء الشائعة
msg="error creating ingester" err="context deadline exceeded"عند بدء تشغيل Loki — حاوية Loki لا تملك صلاحية الكتابة إلى الحجم المُركَّب. تحقق من صلاحيات الدليل المقابل لحجم Docker (docker inspect loki-dataللعثور على المسار الفعلي) وتأكد من أن مستخدم الحاوية (UID 10001 في صور Grafana الحديثة) يمكنه الكتابة:chown -R 10001:10001 /مسار/الحجم.- غياب
Data source connected and labels foundفي Grafana واستبداله بـconnection refused— لا تستطيع Grafana الوصول إلى Loki. تحقق من أن الرابط المُدخَل في مصدر البيانات هوhttp://loki:3100(اسم خدمة Docker، وليسlocalhost) وأن الحاويتين في نفس شبكة Docker (docker inspect loki-grafana-stack_default). - عدم جمع Promtail لأي سجلات، وظهور
component=discovery.docker msg="refreshing targets"في حلقة بلا تقدم عند تشغيلdocker compose logs -f promtail— Promtail لا يملك صلاحية الوصول إلى socket Docker. تحقق من أن الحجم/var/run/docker.sock:/var/run/docker.sock:roمُعرَّف في خدمة Promtail وأن الـ socket موجود على المضيف:ls -la /var/run/docker.sock. err="entry out of order for stream"في سجلات Loki — تصل سجلات بتوقيتات مضطربة الترتيب (حاوية أُعيد تشغيلها تُعيد إرسال سجلات قديمة مثلًا). أضفmax_stream_label_count: 0تحتlimits_configوunordered_writes: trueتحتingesterفيloki-config.yamlلقبول الإدخالات غير المتسلسلة.- عرض Grafana لـ
no dataعلى استعلام LogQL صحيح — تحقق أولًا من نطاق الوقت المُحدد في أعلى اليمين (الفخ الأكثر شيوعًا: نطاق مضبوط علىlast 5 minutesبينما لم يجمع Promtail بعد سجلات حديثة). ثم تحقق من وجود التسمية المُستخدمة في استعلامك:{container="nginx"}يفشل إن كانت الحاوية تُسمىnginx-1— استخدم Log Browser لاستكشاف التسميات المتاحة.
لوحة تحكم لربط السجلات بالمقاييس
الفائدة الرئيسية لـ Loki في بيئة Grafana قائمة هي الربط مع مقاييس Prometheus. إن كان لديك بالفعل مصدر Prometheus (راجع مقال مراقبة VPS بـ Grafana وPrometheus)، يمكنك إنشاء لوحة تحكم مختلطة: صف من لوحات المقاييس (CPU والذاكرة ومعدل طلبات HTTP) في الأعلى، ولوحة سجلات Loki مُصفَّاة على نفس الخدمة في الأسفل. حين يظهر ذروة CPU عند الساعة 14:37، ترى فورًا أي السجلات صدرت في تلك اللحظة بالذات، دون تبديل أدوات أو إعادة تشغيل أمر SSH. هذا النوع من الربط هو حالة الاستخدام التي تُبرر نشر المكدّستين معًا بدلًا من واحدة منهما: Prometheus لـ"ما يُقاس"، وLoki لـ"ما يُروى".
Loki مقابل ELK مقابل سجلات SSH — أي نهج لأي حالة
| المعيار | SSH + docker logs | مكدّسة ELK (Elastic) | Loki + Grafana |
|---|---|---|---|
| RAM المطلوبة | لا شيء (لا خدمة) | 8–16 غيغابايت كحد أدنى لكل عقدة | 2–4 غيغابايت لـ VPS قياسي |
| الفهرسة | لا شيء | فهرسة نصية كاملة (Elasticsearch) | فهرسة بالتسميات فقط |
| البحث متعدد الخوادم | مستحيل بلا نص برمجي | نعم، بشكل أصلي | نعم، عبر Promtail متعدد المضيفين |
| الربط بالمقاييس | يدوي | مع Kibana + APM (معقد) | أصلي في Grafana |
| تكلفة البرمجيات | لا شيء | الرخصة الأساسية مجانية، الميزات محدودة | مفتوح المصدر كليًا، بلا حصص |
ما وضعته الآن في المدار
أصبح لديك الآن مكدّسة تجميع سجلات تعمل بالكامل: تُخزّن Loki وتُفهرس بالتسميات، ويجمع Promtail تلقائيًا تدفقات جميع حاويات Docker الخاصة بك، وتوفر Grafana واجهة بحث وتنبيه. تعمل المكدّسة بالكامل على VPS الخاص بك، دون خدمة طرف ثالث، ودون حصص استيعاب، ودون تكلفة برمجية إضافية. الخطوات الطبيعية التالية: ربط هذه المكدّسة بمراقبة Prometheus الحالية لربط المقاييس بالسجلات، وتفعيل تتبع موزّع مع Tempo إن كانت تطبيقاتك تُصدر تتبعات OpenTelemetry، واستكشاف تنبيهات Grafana على محتوى السجلات لتكون على دراية بالمشكلة قبل أن يُبلّغك عنها مستخدموك.