ADR-0003 — Kubernetes كمنصة orchestration
الحالة: مُعتمَد — مع تحفّظ مُوثَّق · التاريخ: 2026-07-20 · صاحب القرار: أمير هارون (القائد التقني)
ذو صلة: CON-15 · QAS-OPS-01 · QAS-OPS-02 · R-09 · تعمّق DevOps · ADR-0001
السياق#
الحِمل التشغيلي يتكوّن من: طبقة API عديمة الحالة بلغة PHP، ومعالجات طوابير (workers)، وscheduler وحيد، وثلاث خدمات مرافقة (sidecars) ذات حالة أو متخصّصة (document renderer، malware scanner، search engine)، وبشكل مشروط طبقة وسائط بمتطلّبات hostNetwork (ADR-0005).
حجم الإطلاق قريب من الصفر، وينمو إلى نحو 10⁴ حساب ومئات جلسات الفيديو المتزامنة خلال 12–18 شهرًا.
اتُّخذ هذا القرار من قِبل المعماري كقيد مُعلَن (CON-15). يوثّق هذا الـ ADR القرار ومبرراته، و — بصراحة — التوتر الذي يخلقه، كي يُدار عن قصد بدلًا من اكتشافه لاحقًا.
القرار#
Kubernetes، باستخدام control plane مُدار (OKE على Oracle Cloud وفق ADR-0001)، مع الاقتصار على primitives قياسية upstream.
المبررات#
| السبب | التفصيل |
|---|---|
| الحِمل يمتلك فعليًا ملفات توسّع متغايرة | الـ API يتوسّع على RPS؛ والـ workers على عمق الطابور؛ والـ renderer على دفعات المهام؛ والوسائط على عدد الجلسات. Kubernetes يعبّر عن التوسّع لكل حِمل بشكل أصيل. |
| فصل الـ sidecars (ADR-0002) يحتاج منسّقًا | أربعة مكوّنات منشورة بشكل منفصل بدورات حياة مستقلة يتجاوز ما تتعامل معه بيئة أحادية المضيف بأريحية. |
| قابلية النقل في ظل تقلّب المزوّدين | مشهد المزوّدين في السعودية متغيّر — Azure يصل في الربع الرابع 2026، وحالة AWS غير محسومة. Kubernetes القياسي هو الوجهة الأكثر قابلية للنقل. |
| مساحة للمرحلة الثانية | نطاق CONF-04 سيضيف خدمات. قرار الـ orchestration كان سيُفرض حتمًا؛ واتخاذه مرة واحدة أرخص من الترحيل في منتصف النمو. |
| التوسّع التنبّئي قابل للتعبير | QAS-SCL-02 — الطلب على الفيديو معروف مسبقًا من تقويم الحجوزات. Kubernetes يجعل التسخين المسبق المدفوع بالتقويم أمرًا مباشرًا. |
⚠ التحفّظ، مُوثَّقًا بصراحة#
Kubernetes يرفع الأرضية التشغيلية، وهذا يعارض قيدين مباشرة:
CON-01— حساسية الميزانيةQAS-OPS-01— قابلية التشغيل من قِبل فريق صغير عند الساعة 2:00 فجرًا
كان بإمكان orchestration أبسط (Compose/Swarm أو PaaS مُدار) أن يفي بحِمل الإطلاق بسطح تشغيلي أقل بكثير. لذا فقرار استخدام Kubernetes هو استثمار في وضعية الثمانية عشر شهرًا، يُدفع ثمنه من الشهر الأول.
النتيجة ليست "علينا توخّي الحذر". بل هي أن استثمار المراقبة والأتمتة الذي يفرضه Kubernetes أصبح الآن متطلَّبًا مُعلَنًا، لا إضافة اختيارية. اختيار Kubernetes دون دفع تلك التكلفة يحوّل قرار بنية تحتية إلى ضريبة تشغيلية دائمة — وهذا بالضبط كيف ينتهي الحال بالفرق الصغيرة إلى cluster لا يستطيع أحد تشخيصه.
هذا موثّق بوصفه R-09.
النتائج#
استثمارات إلزامية — ثمن هذا القرار#
هذه ليست "من الجيد وجودها". بدونها يصبح القرار سالب القيمة.
| المتطلَّب | المبرر |
|---|---|
| correlation IDs من الطرف إلى الطرف — API ← وظائف الطوابير ← الـ sidecars ← الاستدعاءات الخارجية ← كل سطر سجل | بدونها، تشخيص upload←scan←review←approve←notify يعني مطابقة الطوابع الزمنية يدويًا عبر أربعة مكوّنات |
| التنبيهات على SLOs المرئية للمستخدم، لا على رسوم CPU | زمن الكشف < 5 دقائق (QAS-OPS-01) |
| rollback بأمر واحد خلال أقل من 10 دقائق، مُختبَر | الـ rollback غير المُختبَر ليس rollback |
| أدلة تشغيل (runbooks) لأهم 10 أنماط فشل متوقّعة | تحلّ محل المعرفة الشفهية |
| تحصين مرحلي مرتّب بخفض المخاطر لكل ساعة عمل | DevOps §9 — يقاوم إغراء البدء بالأمور المثيرة |
قيد قابلية النقل#
لأن مشهد المزوّدين متقلّب، يجب ألّا يصبح النشر خاصًا بـ Oracle:
- primitives قياسية upstream؛ دون ميزات control plane خاصة بمزوّد
- PostgreSQL وRedis وتخزين متوافق مع S3 — واجهات قياسية فقط
- بنية تحتية ككود مع عزل خصوصيات المزوّد في وحدات منفصلة
- دون اعتماد على طابور أو function runtime أو منتج حاويات serverless خاص بمزوّد
هذا يقايض بعض راحة الخدمات المُدارة بالقدرة على التنقّل. وبالنظر إلى فجوات التحقق في ADR-0001، فهذه المقايضة صحيحة.
مصائد خاصة بـ Laravel يُدخلها هذا القرار#
كل واحدة نمط فشل حقيقي لا وجود له بدون منسّق:
| المصيدة | التخفيف |
|---|---|
الهجرات في initContainer — كل pod ينفّذها، فينتج عن نشر بثلاث نسخ تنفيذ الهجرات 3 مرات بالتوازي |
نفّذها كـ Job، مرة واحدة لكل إصدار، قبل الطرح |
| النشر التدريجي يُشغّل الكود القديم والجديد على schema واحد | يجب أن تكون الهجرات متوافقة رجعيًا؛ والتغييرات الهدّامة تستخدم نمط expand/contract عبر إصدارات |
terminationGracePeriodSeconds أقصر من أطول وظيفة ← SIGKILL في منتصف الوظيفة عند كل نشر |
يجب أن تتجاوز فترة السماح أطول وظيفة؛ وافصل نشرات الـ workers حسب مدة الوظيفة كي لا ينتظر نشر إشعارات وظيفة PDF |
| PHP ليس PID 1 ← لا يستقبل SIGTERM أبدًا | استخدم صيغة exec في نقطة الدخول |
| liveness probe يفحص قاعدة البيانات ← عطل لحظي في القاعدة يعيد تشغيل كل الـ pods دفعة واحدة | الـ readiness يجوز أن يفحص التبعيات؛ الـ liveness لا يجوز |
PSA restricted مقابل نظام ملفات قابل للكتابة |
تسخين الكاش وقت البناء + Redis + stderr + object storage (DevOps §1.3) |
مؤجَّل عن قصد#
| المؤجَّل | أعِد النظر عند |
|---|---|
| Service mesh | خدمات كثيرة باحتياجات mTLS معقّدة. الـ network policies تغطي المتطلّب الأمني اليوم، والـ mesh سيضيف تعقيدًا كبيرًا يعارض QAS-OPS-01. |
| API gateway مخصّص | تحوّل الوحدات إلى خدمات فعلية (المرحلة الثانية) |
| Multi-cluster | التوسّع الخليجي بمتطلبات إقامة بيانات لكل دولة |
| أمن وقت التشغيل (Falco/Tetragon) | فقط عندما يوجد من سيفرز التنبيهات. نشره دون مالك يُنتج طمأنينة زائفة، وهي أسوأ من غيابه. |
أعِد النظر إذا#
تجاوز العبء التشغيلي طاقة الفريق بشكل واضح — مقيسًا باتجاه تصاعدي في MTTR للحوادث، أو باستهلاك صيانة الـ cluster أكثر من نحو خُمس وقت الهندسة. عندها يكون التراجع العملي هو نقل الطبقة عديمة الحالة إلى PaaS مُدار، مع الإبقاء على الـ cluster للوسائط والـ sidecars ذات الحالة فقط.