arc42 §7 — عرض النشر
الحالة: مسودة v0.1 · التاريخ: 2026-07-20
يتتبع إلى: ADR-0001 (الاستضافة)، ADR-0003 (Kubernetes)، CON-13 (إقامة البيانات)، QAS-SCL-01، QAS-OPS-01
وثيقة مرافقة: تعمّق DevOps — تفاصيل الـ container الداخلية، وCI/CD، والتحصين
7.1 نظرة عامة على البنية التحتية#
flowchart TB
subgraph INTERNET["🌐 Internet"]
MOB["📱 Mobile clients"]
WEB["🌐 Web / Admin"]
VERIFY["🔍 Public certificate<br/>verification"]
end
subgraph KSA["🇸🇦 Kingdom of Saudi Arabia — CON-13"]
subgraph PRIMARY["Primary Region — Riyadh"]
subgraph EDGE["Edge"]
CDN["CDN"]
WAF["WAF + DDoS"]
LB["Load Balancer"]
end
subgraph K8S["Kubernetes Cluster (managed control plane)"]
subgraph NP1["Node pool: general"]
API["API pods<br/><i>HPA: RPS + p95</i>"]
WRK["Worker pods<br/><i>HPA: queue depth</i>"]
SCH["Scheduler<br/><i>singleton</i>"]
end
subgraph NP2["Node pool: memory"]
PDF["Document renderer"]
SRCH["Search engine"]
AV["Malware scanner"]
end
subgraph NP3["Node pool: media — conditional, ADR-0005"]
MEDIA["Media servers<br/><i>hostNetwork</i>"]
TURN["TURN"]
end
end
subgraph DATA["Managed data services"]
PG[("PostgreSQL<br/>primary + replica<br/>RLS enabled")]
RD[("Redis<br/>cache · queues · locks")]
OBJ[("Object Storage<br/>encrypted")]
end
end
subgraph DR["DR Region — Jeddah"]
PGDR[("PostgreSQL<br/>standby")]
OBJDR[("Object Storage<br/>replica")]
end
end
MOB & WEB --> CDN --> WAF --> LB --> API
VERIFY --> WAF
MOB -.->|"WebRTC — bypasses ingress"| MEDIA
MOB -.->|"signed URL — direct"| OBJ
API --> PG & RD & OBJ & SRCH
WRK --> PG & RD & OBJ & SRCH
WRK --> PDF & AV
SCH --> RD
PG ==>|"async replication<br/><b>never leaves KSA</b>"| PGDR
OBJ ==>|"replication"| OBJDR
style KSA fill:#e8f5e9,stroke:#2d8659
style DR fill:#f0f0f0,stroke:#888
style NP3 fill:#fff4e5,stroke:#d98c1f
منطقة التعافي من الكوارث (DR) هي الخاصية الحاسمة في ADR-0001. Oracle هو المزوّد الوحيد القابل للتطبيق الذي يملك منطقتين داخل المملكة في مرحلة الإتاحة العامة (GA)، ما يجعل التعافي من الكوارث قابلًا للتحقيق دون أن تغادر بايت واحدة أرض السعودية. وكل بديل آخر يفرض أن يكون الـ DR إمّا عابرًا للحدود — فيعيد إدخال مسار عمل الامتثال للنقل عبر الحدود بأكمله — أو داخل المنطقة نفسها بتعدد مناطق الإتاحة (multi-AZ)، وهو ما لا يحمي من فقدان منطقة كاملة.
7.2 البيئات#
| البيئة | الغرض | الحجم | البيانات |
|---|---|---|---|
| Local | التطوير | Docker Compose | بيانات اصطناعية مُبذَّرة |
| CI | الاختبارات الآلية | containers مؤقتة | Fixtures |
| Staging | التحقق قبل الإنتاج | عنقود بحدّه الأدنى، بالطوبولوجيا نفسها | اصطناعية فقط — لا بيانات شخصية إنتاجية إطلاقًا |
| Production | التشغيل الحيّ | كامل، بـ autoscaling | حيّة |
⚠️ يجب ألا تحتوي بيئة Staging على بيانات شخصية إنتاجية إطلاقًا. نسخ قاعدة بيانات إنتاجية إلى بيئة أدنى من أشيع خروقات PDPL عمليًا — فبيئة staging لديها ضوابط وصول أضعف، وعدد أكبر من الأشخاص الذين يملكون الوصول، ولا سجل تدقيق. وإذا لُزمت أحجام واقعية، فوَلِّدها.
قاعدة تكافؤ البيئات: تشغّل staging الـ manifests نفسها بعدد نسخ (replicas) مختلف وطلبات موارد مختلفة. والتباعد في الطوبولوجيا يعني أن staging تكفّ عن التنبؤ بسلوك الإنتاج، وهو غرضها الوحيد.
7.3 مجموعات العُقد (node pools) والتوزيع#
| المجموعة | أعباء العمل | محرّك الحجم | ملاحظات |
|---|---|---|---|
general |
API، الـ workers، الـ scheduler | توازن CPU/ذاكرة | مُوسَّعة تلقائيًا؛ الأغلبية المرنة |
memory |
مُصيِّر المستندات، البحث، فاحص البرمجيات الخبيثة | الذاكرة | المُصيِّر يستهلك مئات الميغابايتات لكل عملية تصيير؛ والبحث يحتفظ بفهرسه في RAM؛ وفاحص الفيروسات يحمل قاعدة تواقيع بحجم ~1 GB |
media (مشروطة) |
خوادم الوسائط، TURN | الشبكة | hostNetwork ← pod وسائط واحد لكل عقدة. وحدة التوسّع هي العقدة، بزمن إقلاع يُقاس بالدقائق. |
قيود التوزيع#
| القيد | السبب |
|---|---|
| توزيع pods الـ API عبر مناطق الإتاحة | فشل منطقة إتاحة يجب ألا يُسقط الـ API |
| الـ scheduler: نسخة واحدة بالضبط | المهام المجدولة المكرّرة ستُرسل الإشعارات مرتين وتخصم قيمة الاشتراكات مرتين |
| pods الوسائط: واحد لكل عقدة، مع anti-affinity | تعارض منفذ المضيف |
| البحث: وحدة تخزين دائمة، مثبّتة | حالة مُحتفَظ بها (stateful) |
| الـ workers: مفصولون حسب مدة المهمة | كي لا ينتظر نشرُ إشعار مدته 60 ثانية مهمةَ PDF مدتها 600 ثانية |
7.4 طبقة البيانات#
PostgreSQL#
| الجانب | التصميم |
|---|---|
| الطوبولوجيا | Primary + standby متزامن (المنطقة نفسها)، وstandby غير متزامن (منطقة الـ DR) |
| RLS | مُفعَّل بـ FORCE ROW LEVEL SECURITY على كل الجداول المحصورة بالمستأجر (ADR-0007) |
| دور التطبيق | أقل الصلاحيات — لا UPDATE/DELETE على سجل التدقيق، ولا BYPASSRLS |
| تجميع الاتصالات (pooling) | ⚠️ التجميع بوضع المعاملة (transaction-mode pooling) يتفاعل بخطورة مع متغيرات جلسة RLS — انظر ADR-0007. استخدم إعدادات محصورة بالمعاملة وأعد ضبطها في الـ middleware المُنهي. |
| مسار التوسّع | نسخ قراءة (read replicas) لإسقاطات (projections) التقارير ← تقسيم (partitioning) الجداول عالية الحجم. مُحفِّزات موثّقة، لا ردود فعل. |
| النسخ الاحتياطي | أرشفة WAL مستمرة + لقطات دورية، مشفّرة، ومحتفظ بها داخل المملكة |
Redis#
الذاكرة المؤقتة، والطوابير، والأقفال، وعدّادات تحديد المعدل. ليس مخزنًا معمّرًا — فأي شيء يجب أن ينجو من فقدان Redis يعيش في PostgreSQL. ومهام الطوابير هي الاستثناء البارز: فقدانها محتمَل لأن المعاملة التي أنشأتها قد أُودعت (committed) بالفعل (انظر قواعد الـ pipeline).
التخزين الكائني (Object Storage)#
| الحاوية (bucket) | المحتويات | الوصول |
|---|---|---|
quarantine/ |
رفعات لم تُفحص بعد | لا وصول للقراءة من التطبيق أو من واجهة المسؤول |
documents/ |
الوثائق القانونية المعتمدة | signed URL، بمدة صلاحية ≤ 5 دقائق، بعد التخويل فقط |
templates/ |
مكتبة القوالب | signed URL، خلف CDN |
certificates/ |
ملفات PDF المولَّدة | signed URL، للمالك والمُتحقِّق |
public-assets/ |
ثابتة | CDN، عامة |
الكل مشفَّر أثناء السكون. والوثائق القانونية مشفّرة إضافيًا بتشفير مغلَّف على مستوى التطبيق (envelope encryption) (الأمن §4).
7.5 طوبولوجيا الشبكة#
flowchart TB
subgraph PUB["Public subnet"]
LB2["Load balancer"]
NAT["NAT gateway"]
end
subgraph PRIV2["Private subnet — no inbound internet"]
NODES["Kubernetes nodes"]
end
subgraph DATA2["Data subnet — no internet route at all"]
DB2[("PostgreSQL")]
RD2[("Redis")]
end
INT["🌐"] --> LB2 --> NODES
NODES --> NAT --> INT
NODES --> DB2 & RD2
DB2 -.->|"❌ no route"| INT
style DATA2 fill:#e8f5e9,stroke:#2d8659
| القاعدة | التفصيل |
|---|---|
| الشبكة الفرعية للبيانات بلا مسار إلى الإنترنت | ليست "محجوبة بجدار ناري" — لا مسار موجود أصلًا |
| العُقد في شبكات فرعية خاصة | الخروج عبر NAT فقط |
| NetworkPolicy بالرفض الافتراضي، دخولًا وخروجًا، لكل namespace | مع استثناء لـ DNS على UDP و TCP 53 — ونسيان TCP هو السبب الكلاسيكي لـ "كل شيء تعطّل" |
| قائمة سماح للخروج نحو مزوّدي الدفع/الفيديو/الـ SMS | ⚠️ الـ NetworkPolicy القياسية تأخذ نطاقات CIDR لا أسماء DNS، والمزوّدون يدوّرون عناوين anycast. يتطلب ذلك سياسة FQDN أصيلة في الـ CNI أو وكيل خروج (egress proxy) — اختر الـ CNI عن قصد بدل قبول الافتراضي. |
| طبقة الوسائط: مسار منفصل | UDP، وhostNetwork، بلا ingress، وبلا mesh. وبقواعد جدار ناري خاصة بها. |
7.6 التعافي من الكوارث#
الأهداف — مقترحة، وتتطلب تأكيد الراعي#
| السيناريو | RTO | RPO |
|---|---|---|
| فشل pod / عقدة مفردة | 0 (تلقائي) | 0 |
| فشل منطقة إتاحة | < 5 دقائق | 0 |
| فشل منطقة كاملة | < 4 ساعات | < 15 دقيقة |
| حذف بيانات عرضي | < 4 ساعات | < 15 دقيقة (استعادة إلى نقطة زمنية) |
⚠️ لا يُذكر أي RTO أو RPO في أي وثيقة مصدر. هذه مقترحات من المعماري مشتقة من
QAS-AVL-02(توفّر 99.5 % في ساعات العمل) ومن طبيعة عبء العمل — مواعيد مجدولة وتنزيل وثائق، لا مدفوعات لحظية. وهي تحتاج اعتمادًا صريحًا من الراعي، لأنها تقود طوبولوجيا الـ standby ومن ثمّ التكلفة.
التحويل بين المناطق#
يدويّ، وعن قصد. فالتحويل التلقائي عبر المناطق يخاطر بانقسام الدماغ (split-brain) في نظام لا يبرّر هدف توفّره ذلك. والإجراء — ترقية الـ standby في جدة، وإعادة توجيه الـ DNS، والتحقق من تأخّر تكرار التخزين الكائني — يجب أن يكون موثّقًا ومُتمرَّنًا عليه، لا نظريًا.
ما لا يغطّيه التكرار (replication)#
| الخطر | التخفيف |
|---|---|
| الفساد المنطقي (ترحيل خاطئ يتكرر فورًا) | استعادة إلى نقطة زمنية، لا مجرد ترقية standby |
| الحذف العرضي | PITR + حذف ناعم للكيانات عالية القيمة |
| برامج الفدية / اختراق بيانات الاعتماد | نسخ احتياطية غير قابلة للتعديل وباعتمادات منفصلة |
| فقدان فهرس البحث | قابل لإعادة البناء من PostgreSQL — فالفهرس ليس مرجعًا موثوقًا أبدًا |
الصف الأخير خاصية معمارية مقصودة: لأن فهرس البحث projection ويُرشَّح بعديًا مقابل قاعدة البيانات (Flow 5)، فإن فقدانه كليًا حادثة أداء لا حادثة فقدان بيانات.
7.7 السعة — حجم الإطلاق#
تقديرات بترتيب المقدار فقط. الأرقام الحقيقية تتطلب عروض أسعار المزوّدين التي ما تزال معلّقة في ADR-0001.
| المكوّن | عند الإطلاق | مُحفِّز النمو |
|---|---|---|
| pods الـ API | 3 (الحد الأدنى)، توسّع تلقائي إلى 10 | p95 > 300 ms بشكل مستمر |
| الـ workers | 2 لكل صنف طابور | عمق الطابور / عمر أقدم مهمة |
| المُصيِّر | 1، مع التقلّص إلى صفر عند الخمول | عمق طابور التصيير |
| البحث | عقدة واحدة | حجم الفهرس / زمن استجابة الاستعلام |
| PostgreSQL | متواضع، primary + standby | تشبّع الاتصالات ← نسخ قراءة |
| Redis | صغير | ضغط الذاكرة |
| الوسائط | 0 عند الإطلاق | التزامن المحجوز (تنبّؤي) |
خاصيتان جديرتان بالاستغلال#
1. الطلب على الفيديو معروف مسبقًا. الاستشارات محجوزة، لذا فسعة QAS-SCL-02 مجدولة لا عشوائية. وينبغي للـ scheduler أن يُحمِّي سعة الوسائط مسبقًا انطلاقًا من تقويم المواعيد، مع HPA تفاعلي كشبكة أمان فقط. وهذا مهم لأن توسّع الوسائط يجري بحبيبة العقدة وببطء — والتفاعل بعد ثلاث دقائق من بدء استشارة مدفوعة يعني استردادًا.
2. الحمل يتركّز في ساعات العمل السعودية. من الأحد إلى الخميس، 08:00–20:00 بتوقيت AST. وينبغي تقليص السعة خارج الذروة بحزم (QAS-OPS-02)؛ وافترض نسبة ذروة إلى متوسط لا تقل عن 5:1.
7.8 البنود المفتوحة#
| البند | يعيق |
|---|---|
مصفوفة خدمات OCI في me-riyadh-1 — توفّر Redis المُدار والـ registry والأسرار غير مُتحقَّق منه |
يحدّد ما إذا كانت عدة مكوّنات مُدارة ذاتيًا أم مُدارة من المزوّد (ADR-0001 الفجوة 1) |
| عروض أسعار المزوّدين | نموذج التكلفة بأكمله |
تسجيل OQ-08 |
ما إذا كانت مجموعة عُقد الوسائط موجودة، وحجمها |
| اختيار مزوّد ADR-0005 | ما إذا كانت مجموعة عُقد media والـ TURN والـ hostNetwork موجودة أصلًا |
| اختيار الـ CNI | ما إذا كانت سياسة الخروج بـ FQDN متاحة (§7.5) |
| اعتماد RTO/RPO | طوبولوجيا الـ standby والتكلفة |