114 أسطر
6.3 KiB
Markdown
114 أسطر
6.3 KiB
Markdown
# المهمة 4 — قابلية التوسع وتوزيع الأحمال (15,000 req/s)
|
||
|
||
## 1) Architecture Diagram
|
||
|
||
```mermaid
|
||
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% فوق المتوقع، النظام لا يصل للتشبع الكامل.
|
||
|
||
**توصية عملية للإعداد:**
|
||
```yaml
|
||
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، لأن بدء التشغيل من الصفر أبطأ بكثير.
|
||
2. **صور خفيفة (slim images):** استخدام صور أساس صغيرة (مثل `python:3.11-slim`)
|
||
لتقليل وقت سحب الصورة (image pull time) عند جدولة حاوية جديدة على node جديد.
|
||
3. **Predictive/Proactive scaling:** التوسع بناءً على اتجاه الحمل (trend)
|
||
وليس فقط عندما يتجاوز الحمل الحد الحالي — مثال: لو الحمل يرتفع بمعدل ثابت
|
||
خلال آخر دقيقتين، ابدأ بإضافة حاويات الآن بدل الانتظار حتى الوصول للحد الأقصى.
|
||
4. **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 مباشرة — فقط الطبقات الخلفية
|
||
(قاعدة البيانات، تخزين الملفات) هي من تحتاجه، مما يسمح للطبقة الأمامية
|
||
بالتوسع والتقلص بحرية دون القلق على فقدان بيانات.
|