السجلات على نطاق واسع: ELK وLoki

الاحتفاظ بالسجلات والتكلفة والامتثال

18 دقيقة الدرس 9 من 28

الاحتفاظ بالسجلات والتكلفة والامتثال

على نطاق صغير، يبدو تسجيل الأحداث مجانياً. أما على نطاق الإنتاج — ملايين الحاويات، ومئات الخدمات، وآلاف الطلبات في الثانية — فيتحول التسجيل إلى أحد أكبر بنود الإنفاق في ميزانية البنية التحتية. يمكن لمنصة الخدمات المصغّرة المشغولة أن تولّد 10 تيرابايت من السجلات الخام يومياً. تخزين كل شيء إلى الأبد في Elasticsearch الساخنة هو انتحار مالي. وعدم الاحتفاظ بشيء يستدعي الغرامات التنظيمية ويجعل تحليل ما بعد الحوادث مستحيلاً. فن إدارة الاحتفاظ هو معرفة ما يجب الاحتفاظ به بالضبط، وإلى متى، وبأي دقة، وبأي تكلفة.

الاحتفاظ متعدد الطبقات: نموذج المناطق الثلاث

تطبّق منصات تسجيل الإنتاج في كبرى شركات التكنولوجيا بنية ثلاثية الطبقات، مستوحاة من تدرّج التخزين. لكل منطقة خصائص مختلفة من حيث التكلفة وسرعة الاستعلام ومدة الاحتفاظ.

Three-tier log retention architecture Hot Tier Elasticsearch / Loki Retention: 7-30 days Full fidelity, fast queries SSD-backed indexes $0.30-1.50/GB/mo Use case: incident response, dashboards Latency: under 1 s rollover Warm Tier S3 + Parquet / Loki chunks Retention: 30-365 days Compressed, sampled Object storage backend $0.02-0.05/GB/mo Use case: compliance audits, trend analysis Latency: 5-30 s archive Cold Tier S3 Glacier / GCS Archive Retention: 1-7 years Write-once immutable WORM locks for compliance $0.004/GB/mo Use case: legal hold, forensics, PCI/HIPAA Latency: hours Cost decreases left-to-right; query speed decreases left-to-right
ثلاث طبقات للاحتفاظ بالسجلات: الساخنة (سريعة ومكلفة) ثم الدافئة (متوازنة) ثم الباردة (رخيصة وبطيئة). انقل السجلات تلقائياً بناءً على العمر وأنماط الوصول.

في Elasticsearch، يُتمتَّت نقل الطبقات عبر إدارة دورة حياة الفهرس (ILM). في Loki، المكافئ هو قواعد الضغط وسياسات دورة حياة تخزين الكائنات. الفكرة الجوهرية: نادراً ما تحتاج إلى الاستعلام عن سجلات تعود لأكثر من 30 يوماً بزمن استجابة أقل من ثانية — تلك الاستعلامات للتدقيق والطب الشرعي، حيث الانتظار 30 ثانية مقبول.

# سياسة ILM في Elasticsearch: hot -> warm -> cold -> delete PUT _ilm/policy/logs-standard { "policy": { "phases": { "hot": { "min_age": "0ms", "actions": { "rollover": { "max_primary_shard_size": "50gb", "max_age": "1d" }, "set_priority": { "priority": 100 } } }, "warm": { "min_age": "7d", "actions": { "shrink": { "number_of_shards": 1 }, "forcemerge": { "max_num_segments": 1 }, "set_priority": { "priority": 50 } } }, "cold": { "min_age": "30d", "actions": { "searchable_snapshot": { "snapshot_repository": "s3-logs-archive" }, "set_priority": { "priority": 0 } } }, "delete": { "min_age": "365d", "actions": { "delete": {} } } } } }
إجراء searchable_snapshot في المرحلة الباردة هو المفتاح: يُحمِّل Elasticsearch الفهرس مباشرة من S3 دون استعادته الكاملة على القرص. تدفع أسعار S3 (~0.023 دولار/GB/شهر) بدلاً من أسعار EBS (~0.10 دولار/GB/شهر)، والاستعلامات لا تزال تعمل — غير أنها أبطأ. هكذا تخفض الفرق تكلفة التخزين بنسبة 60-80% دون فقدان قابلية الاستعلام.

أخذ عينات من السجلات الصاخبة

ليس لكل سطر سجل قيمة متساوية. واجهة برمجية للمدفوعات تعمل بشكل صحي وتسجّل كل معاملة ناجحة على مستوى INFO تنتج حجماً هائلاً ذا قيمة تشخيصية شبه معدومة. أخذ عينات السجلات هو ممارسة الاحتفاظ بجزء إحصائي فقط من السطور المتكررة منخفضة القيمة، مع الاحتفاظ بنسبة 100% من السجلات عالية الإشارة (الأخطاء، والتحذيرات، والطلبات البطيئة، وأحداث الأمان).

هناك استراتيجيتان سائدتان:

  • أخذ العينات من الرأس (Head-based): يُتخذ القرار في بداية الطلب — احتفظ بطلب واحد من كل 100 بشكل غير مشروط. سهل التنفيذ في الشاحن، لكنك قد تسقط الطلب النادر الذي تسبب في الخطأ.
  • أخذ العينات من الذيل (Tail-based): احتفظ بالطلب مؤقتاً، وانتظر النتيجة، ثم اتخذ القرار — احتفظ دائماً بالأخطاء والطلبات البطيئة، وخذ عينات من الباقي. يتطلب معالجة إضافية في الجامع، لكنه أذكى بكثير. هذا هو نموذج Google وNetflix.
# OpenTelemetry Collector: ضبط أخذ العينات من الذيل (otel-collector-config.yaml) processors: tail_sampling: decision_wait: 10s num_traces: 50000 policies: - name: always-keep-errors type: status_code status_code: { status_codes: [ERROR] } - name: always-keep-slow type: latency latency: { threshold_ms: 2000 } - name: sample-healthy-traffic type: probabilistic probabilistic: { sampling_percentage: 1 } filter/drop-noisy-info: logs: exclude: match_type: expr expressions: # تجاهل 99% من سجلات INFO العشوائية لخدمة nginx - 'attributes["log.level"] == "INFO" and resource.attributes["service.name"] == "nginx" and int(string(Now().Unix())) % 100 != 0' exporters: loki: endpoint: http://loki:3100/loki/api/v1/push service: pipelines: logs: receivers: [otlp] processors: [filter/drop-noisy-info] exporters: [loki]
احتفظ دائماً بـ تسمية معدل أخذ العينات على تدفقات السجلات (مثل sampling_rate="0.01"). عند الاستعلام عن بيانات مأخوذة عينات منها وتريد استنتاج الأعداد الحقيقية، اقسم على المعدل. بدون هذه التسمية، ستحسب لوحات التحكم بأعداد أقل بصمت ولن يعرف أحد السبب.

إخفاء المعلومات الشخصية: حماية بيانات المستخدمين في السجلات

السجلات هي مصدر مخاطر الخصوصية. يسجّل المهندسون في أغلب الأحيان أجسام طلبات HTTP والترويسات أو نتائج استعلامات قواعد البيانات التي تحتوي على معلومات تعريف شخصية (PII) — عناوين البريد الإلكتروني، وأرقام الهواتف، وأرقام بطاقات الائتمان، ورموز الجلسات، وكلمات المرور. بموجب GDPR وPCI-DSS وHIPAA، يُعدّ تخزين PII غير مُخفاة انتهاكاً للامتثال قد يُفضي إلى غرامات تنظيمية. النهج الصحيح هو الإخفاء عند نقطة الجمع قبل وصول البيانات إلى طبقة التخزين.

يجب أن يحدث الإخفاء في خط أنابيب الجامع وليس في التطبيق — لأنك لا تستطيع الوثوق بأن كل مطور سيُعقّم كل استدعاء للسجلات. نموذج الدفاع متعدد الطبقات يُعامل الجامع باعتباره خط الدفاع الأخير.

# OpenTelemetry Collector: تحويلات إخفاء PII processors: transform/redact-pii: log_statements: - context: log statements: # إخفاء عناوين البريد الإلكتروني - replace_pattern(attributes["message"], "([a-zA-Z0-9._%+\\-]+@[a-zA-Z0-9.\\-]+\\.[a-zA-Z]{2,})", "[EMAIL REDACTED]") # إخفاء أرقام بطاقات الائتمان (13-19 رقم) - replace_pattern(attributes["message"], "\\b(?:\\d[ \\-]?){13,19}\\b", "[CARD REDACTED]") # إخفاء رموز Bearer من ترويسات التفويض - replace_pattern(attributes["http.request.header.authorization"], "Bearer\\s+[A-Za-z0-9\\-._~+\\/]+=*", "Bearer [TOKEN REDACTED]") # حذف المفاتيح عالية الخطورة كلياً (دفاع متعدد الطبقات) - delete_matching_keys(attributes, "password|secret|token|ssn|dob") # المكافئ في Promtail / Grafana Alloy (قسم pipeline_stages): pipeline_stages: - replace: expression: '([a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,})' replace: '[EMAIL REDACTED]' - replace: expression: '(\b\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}\b)' replace: '[CARD REDACTED]' - replace: expression: '(Authorization: Bearer )[^\s]+' replace: '${1}[TOKEN REDACTED]'
الإخفاء بالتعبيرات النمطية ليس مضموناً. قد يفوته PII مُشفَّر أو مُرمَّز (بريد إلكتروني بترميز Base64، أو هاتف بترميز URL). لمتطلبات الامتثال العالية — HIPAA أو PCI Level 1 — ادمج الإخفاء بالتعبيرات النمطية مع خدمة منع فقدان البيانات (DLP) مثل Google Cloud DLP أو AWS Macie أو Microsoft Presidio مفتوح المصدر، كطبقة ثانية قبل وصول البيانات إلى التخزين البارد. التعبيرات النمطية وحدها طبقة جيدة أولى؛ لكنها ليست ضماناً للامتثال.

أطر الامتثال وما تطلبه فعلاً

لكل لائحة متطلبات احتفاظ مختلفة. بوصفك مهندس DevOps يعمل في بيئات خاضعة للتنظيم، يجب أن تعرف هذه الأرقام دون الحاجة للبحث:

  • PCI-DSS v4.0 (المتطلب 10.5): يجب الاحتفاظ بسجلات التدقيق لمدة 12 شهراً على الأقل، مع توفر أحدث 3 أشهر فوراً. يجب حماية السجلات من التعديل.
  • HIPAA: 6 سنوات للاحتفاظ بسجلات تدقيق الوصول إلى بيانات PHI. التشفير في حالة السكون إلزامي. يجب أن تُسجّل سجلات الوصول من وصل إلى ماذا ومتى.
  • SOC 2 Type II: لا مدة إلزامية محددة، لكن المدققين يتوقعون عادةً 12 شهراً من السجلات تغطي فترة التدقيق مع أدلة على عدم التلاعب.
  • GDPR: لا حد أدنى للاحتفاظ، لكن الحق في المحو يُنشئ حداً أقصى — لا يمكنك تخزين سجلات تحتوي على بيانات شخصية إلى الأبد. طبّق سير عمل الحذف على مخازن السجلات لا قواعد البيانات فحسب.

تطبيق S3 Object Lock في وضع WORM — أو ما يعادله في GCS — يُرضي متطلب إثبات عدم التلاعب لـ PCI وSOC 2. بمجرد كتابة كائن السجل وقفله، لا يستطيع حتى مسؤول السحابة ذو الصلاحيات الجذرية حذفه أو الكتابة فوقه قبل انتهاء فترة الاحتفاظ.

رافعات تحسين التكلفة عملياً

حين تكون فاتورة التسجيل مرتفعة، اعمل عبر هذه الرافعات مرتّبةً حسب نسبة الجهد إلى التأثير:

  1. ارفع عتبات مستوى السجلات في الإنتاج. التحول من DEBUG إلى INFO في الإنتاج يُقلص الحجم عادةً بنسبة 50-80% بجهد هندسي شبه معدوم.
  2. احذف فئات السجلات عالية الحجم منخفضة القيمة عند الشاحن. نقاط نهاية فحص الصحة، وجلسات Kubernetes التلقائية، وطلبات الأصول الثابتة هي أكبر المخالفين — فلترها قبل وصولها إلى الخلفية.
  3. اضغط بقوة. يستخدم Loki Snappy أو gzip افتراضياً. ترميز best_compression في Elasticsearch يوفر 20-40% مقارنة بالافتراضي. فعّله على جميع الفهارس الدافئة والباردة.
  4. حجّم الطبقة الساخنة بدقة. قِس كم مرة يستعلم مهندسو المناوبة عن سجلات أقدم من 7 أيام خلال الحوادث. إن كانت الإجابة نادراً، قلّص نافذة الحفظ الساخن من 30 يوماً إلى 7 أيام.
  5. عطّل رسم خرائط الحقول الديناميكي في Elasticsearch. كل حقل جديد تسجّله يصبح حقلاً مرسوماً افتراضياً. عطّل الرسم الديناميكي وارسم فقط الحقول التي تستعلم عنها — الحقول غير المرسومة لا تزال مخزّنة في _source لكنها لا تستهلك ذاكرة المصطلحات عالية الأساسية.
أنشئ وثيقة سياسة الاحتفاظ بالسجلات وضعها تحت نظام إدارة الإصدارات جنباً إلى جنب مع IaC. يجب أن تحدد: تعيينات الطبقات لكل خدمة، ومدد الاحتفاظ لكل طبقة، وتصنيف PII لكل خدمة، ودورية التدقيق للتحقق من عمل الإخفاء. سيطلب المدققون هذه الوثيقة — امتلاكها جاهزة هو الفرق بين تدقيق نظيف وملاحظة سلبية.

إدارة الاحتفاظ ليست مهمة تُنجز مرة واحدة. بنِ دورة مراجعة شهرية: تحقق من أعلى 10 مصادر للسجلات من حيث الحجم، وتحقق من تغطية الإخفاء، وقارن تكاليف الاحتفاظ الفعلية بأهدافك. منصات التسجيل تميل إلى التكلفة الزائدة مع الوقت كلما أضاف الفرق خدمات جديدة ولم يُزل أحد المصادر الصاخبة القديمة.