الملفات
ghaymah-exam--Momen-Lotfy--…/task4-scalability/scalability.md
2026-07-28 20:41:37 +03:00

6.3 KiB
خام اللوم التاريخ

المهمة 4 — قابلية التوسع وتوزيع الأحمال (15,000 req/s)

1) Architecture Diagram

flowchart TD
    U[المستخدمون] --> DNS[Ghaymah DNS / CDN Edge]
    DNS --> LB[Load Balancer<br/>غيمة]

    LB --> C1[حاوية 1<br/>500 req/s]
    LB --> C2[حاوية 2<br/>500 req/s]
    LB --> C3[حاوية 3<br/>500 req/s]
    LB --> C4[...]
    LB --> C39[حاوية 39<br/>500 req/s]

    C1 --> CACHE[(Redis Cache)]
    C2 --> CACHE
    C3 --> CACHE
    C39 --> CACHE

    CACHE --> DB[(قاعدة بيانات رئيسية)]
    C1 --> BLOCK[(Ghaymah Block Storage<br/>للبيانات الدائمة)]
    C39 --> BLOCK

    subgraph Autoscaler["Auto-scaling Controller"]
      METRICS[مقاييس CPU/Memory/RPS] --> DECIDE{تجاوز الحد؟}
      DECIDE -- نعم --> ADD[إضافة حاويات جديدة]
      DECIDE -- لا --> KEEP[إبقاء العدد الحالي]
    end

    LB -. تقرير المقاييس .-> METRICS
    ADD -. توسيع .-> LB

الفكرة: طلبات المستخدمين تصل أولاً لـ Load Balancer الذي يوزّعها على مجموعة حاويات متطابقة (horizontal scaling)، كل حاوية تتعامل مع الحمل الخاص بها وتتصل بطبقة cache مشتركة قبل قاعدة البيانات لتقليل الضغط عليها، بينما البيانات الدائمة (uploads, session files, ...) تُخزَّن على Ghaymah Block Storage القابل للربط بأي حاوية جديدة.


2) حساب عدد الحاويات المطلوبة

المعطيات:

  • الحمل المستهدف: 15,000 req/s
  • سعة الحاوية الواحدة: 500 req/s
  • هامش أمان: 30% (لتفادي التشبع عند تذبذب الحمل أو فقدان حاوية)

الحساب:

عدد الحاويات الأساسي = 15,000 / 500 = 30 حاوية

مع هامش الأمان 30%:
30 × 1.30 = 39 حاوية

➡️ العدد المطلوب فعلياً = 39 حاوية (وليس 30)، بحيث لو سقطت بضع حاويات أو ارتفع الحمل مؤقتاً 20-30% فوق المتوقع، النظام لا يصل للتشبع الكامل.

توصية عملية للإعداد:

min_replicas: 12     # يغطي حمل القاعدة العادي (ليس ذروة اليوم)
max_replicas: 39      # يغطي ذروة 15,000 req/s + الهامش
target_cpu: 65%
target_rps_per_pod: 400   # هدف أقل من السعة القصوى (500) لإعطاء هامش استجابة

3) استراتيجية Cold Start للحاويات الجديدة

المشكلة: حاوية جديدة تحتاج وقتاً (تحميل صورة، تهيئة التطبيق، اتصال بقاعدة البيانات) قبل أن تكون جاهزة فعلياً لاستقبال حمل — لو أرسل لها الـ LB طلبات فوراً ستفشل أو تبطئ الاستجابة.

الحل المقترح:

  1. Readiness Probe صارم: لا يُضاف الـ pod لقائمة الـ Load Balancer إلا بعد نجاح فحص /health عدة مرات متتالية (مثلاً 3 نجاحات متتالية كل 5 ثوانٍ).
  2. Pre-warmed pool (نسخ دافئة جاهزة): الاحتفاظ بعدد أدنى من الحاويات (min_replicas) يعمل باستمرار حتى في أوقات الحمل المنخفض، بدل الاعتماد بالكامل على scale-from-zero، لأن بدء التشغيل من الصفر أبطأ بكثير.
  3. صور خفيفة (slim images): استخدام صور أساس صغيرة (مثل python:3.11-slim) لتقليل وقت سحب الصورة (image pull time) عند جدولة حاوية جديدة على node جديد.
  4. Predictive/Proactive scaling: التوسع بناءً على اتجاه الحمل (trend) وليس فقط عندما يتجاوز الحمل الحد الحالي — مثال: لو الحمل يرتفع بمعدل ثابت خلال آخر دقيقتين، ابدأ بإضافة حاويات الآن بدل الانتظار حتى الوصول للحد الأقصى.
  5. Connection draining عند الإزالة: عند تقليص العدد (scale-down)، إعطاء الحاوية مهلة لإنهاء الطلبات الجارية قبل إيقافها فعلياً، لتفادي أخطاء للمستخدمين.

4) استخدام Ghaymah Block Storage لأحمال العمل ذات الحالة (Stateful)

التطبيق نفسه (API) عادة stateless — أي حاوية جديدة يمكن أن تخدم أي طلب دون الحاجة لبيانات محفوظة محلياً. لكن بعض المكونات تحتاج تخزيناً دائماً:

  • ملفات مرفوعة من المستخدمين (صور، مستندات) يجب أن تبقى متاحة حتى لو تغيّرت الحاوية التي تخدم الطلب التالي.
  • قواعد بيانات أو أنظمة queue تحتاج تخزيناً لا يُفقد عند إعادة تشغيل الحاوية.
  • ملفات cache دائمة أو logs يُراد الاحتفاظ بها عبر إعادة الجدولة.

كيف يُستخدم Ghaymah Block Storage هنا:

  • يُربط (mount) كـ volume دائم لأي حاوية تحتاج تخزيناً (مثل خدمة قاعدة البيانات أو خدمة رفع الملفات)، بحيث تبقى البيانات موجودة حتى لو حُذفت الحاوية وأُعيد إنشاؤها من جديد على node مختلف.
  • يُفصل عن الحاويات نفسها (decoupled)، فيمكن لأي نسخة جديدة من الخدمة أن "ترث" نفس البيانات بمجرد إعادة ربط نفس الـ volume، بدل تخزين البيانات داخل الحاوية (وهو ما يفقد عند إعادة التشغيل).
  • بالنسبة للـ API نفسه (الطبقة الأمامية عالية التوسع من 39 حاوية)، يبقى stateless تماماً ولا يحتاج Block Storage مباشرة — فقط الطبقات الخلفية (قاعدة البيانات، تخزين الملفات) هي من تحتاجه، مما يسمح للطبقة الأمامية بالتوسع والتقلص بحرية دون القلق على فقدان بيانات.