الإقليم ١ — داخل الصندوق الواحد: LAMP وتقسيم الأدوار
النبذة
وصل الطلب إلى 8.8.8.8. في الإقليم السابق رأينا الخادم دوراً لا صندوقاً — والآن نفتح الصندوق ونكتشف أنه يحمل عدّة أدوار تتعاون. هذا هو قلب Task 0: «خادمٌ واحد يشغّل web server و application server و application files و database». السؤال الذي يقودنا: لماذا أربعة أشياء؟ لماذا لا برنامجٌ واحدٌ يفعل كل شيء؟
عندك ملفٌّ ثابت logo.png وصفحةٌ تعرض اسم المستخدم الحالي مأخوذاً من قاعدة البيانات. كلاهما يصل عبر نفس الـ HTTP، نفس الـ port. ومع ذلك، في كل بنيةٍ حقيقية، يخدمهما برنامجان مختلفان: واحدٌ يسلّم logo.png، وآخرُ يولّد صفحة الاسم.
لماذا؟ ما الفرق الجوهري بين «سلّم ملفاً موجوداً» و«ولّد صفحةً لم تكن موجودة»؟ ولو أجبرتَ برنامجاً واحداً على الاثنين، ما الذي ستخسره؟ فكّر قبل أن تكمل — الجواب يفسّر كل تقسيمٍ لاحقٍ في هذا المنهج.
الدرس
ليش نفصل: ملفٌّ جاهز مقابل صفحةٍ تُحسَب
الفرق الجوهري ليس في HTTP، بل في مصدر المحتوى:
- محتوى ثابت (static): بايتاتٌ موجودةٌ سلفاً على القرص (
logo.png,style.css). «الخدمة» = اقرأ الملف وأرسله. عملٌ بسيط، متكرّر، يجب أن يكون سريعاً جداً وآمناً ويتحمّل آلاف الطلبات المتوازية. - محتوى ديناميكي (dynamic): صفحةٌ لا توجد حتى تُطلب — يجب تنفيذ كود (منطق العمل)، وغالباً استشارة قاعدة بيانات، لتُبنى لحظتها. عملٌ ثقيل، مخصّص لكل طلب، ويحمل منطق تطبيقك الحسّاس.
هاتان طبيعتان متناقضتان: الأولى تريد سرعةً وبساطةً وصلابة؛ الثانية تريد مرونةً وقدرةً على تشغيل لغةٍ كاملة. حشرهما في برنامجٍ واحدٍ يعني أن البرنامج الذي يكشف وجهه للإنترنت (سطح الهجوم) هو نفسه الذي يشغّل منطقك ويلمس قاعدة بياناتك. الفصل يجعل كل طرفٍ يتقن دوره — وهذا مبدأ فصل الاهتمامات (separation of concerns) الذي سيصير لاحقاً مبدأ التوسّع (الإقليم ٧).
الأدوار الثلاثة (+ الكود) داخل صندوق Task 0
١) Web server — مثال Nginx. أول من يستقبل HTTP من الإنترنت. مهامه:
- يخدم المحتوى الثابت مباشرةً (سريعاً).
- يتعامل مع تفاصيل HTTP الخام: الاتصالات، الـ keep-alive، لاحقاً إنهاء TLS (إقليم ٥)، الـ headers.
- يعمل reverse proxy: ما لا يستطيع خدمته بنفسه (الطلبات الديناميكية) يُمرّره إلى الـ application server خلفه، ثم يعيد جوابه للمستخدم.
٢) Application server — مثال Gunicorn / uWSGI / PHP-FPM. يشغّل كودك فعلياً: يستقبل الطلب الممرَّر من Nginx، ينفّذ منطق التطبيق (يقرأ/يكتب في قاعدة البيانات، يطبّق القواعد)، ويعيد صفحةً مولَّدة. هو من يفهم منطق تطبيقك؛ Nginx لا يفهمه.
٣) Application files — قاعدة الكود (codebase). ليست خادماً، بل ما ينفّذه الـ application server: ملفّاتك (Python/PHP/…)، القوالب، الإعدادات. الـ app server محرّك؛ هذه هي الوقود.
٤) Database — مثال MySQL. خادمٌ آخر يحفظ الحالة (state): البيانات التي تبقى بين الطلبات (مستخدمون، طلبات، محتوى). الـ application server يسأله عبر استعلام (SQL query)، فيعيد البيانات. لماذا منفصلٌ عن الكود؟ لأن الحالة تحتاج معاملةً خاصة (ثبات، نسخ احتياطي، تكامل) — وستكتشف في الإقليم ٤ أنها أصعب جزءٍ تكاثره.
تتبّع طلبٍ ديناميكيٍّ واحدٍ خلال الصندوق
ارسمها في ذهنك الآن (وستعيد رسمها على السبورة):
كل سهمٍ هنا بروتوكول أو واجهة. على السبورة، سمِّ كل سهم. هذه الرسمة هي نواة Task 0 كلها.
ليش الاسم LAMP — وفخّ Nginx
المشروع يطلب أن تعرف الاختصار LAMP:
- Linux — نظام التشغيل.
- Apache — الـ web server.
- MySQL — قاعدة البيانات.
- PHP / Perl / Python — لغة/طبقة التطبيق.
لكن انتبه للفخّ: مشروعك يستخدم Nginx لا Apache. الـ stack الذي web server فيه Nginx يُسمّى تقليدياً LEMP (الـ E من «engine-x»). لا تقل «LAMP» وأنت ترسم Nginx دون أن تعي الفرق؛ لو سُئلت، اذكر أنك تعرف الاختصار LAMP، وأن استبدال Apache بـ Nginx يجعله LEMP. معرفة لماذا تختلف الحروف تُظهر أنك تفهم الأدوار لا الحروف.
تحليل الأخطاء: ثلاثة أوهامٍ شائعة
- «Nginx يشغّل كودي.» لا — Nginx يخدم الثابت ويمرّر الديناميكي. الكود يشغّله الـ app server. الخلط بينهما يجعل سؤال «الفرق بين web و app server» يسقطك.
- «قاعدة البيانات جزءٌ من التطبيق.» هي خادمٌ منفصلٌ بدورٍ منفصل، حتى لو سكنت نفس الصندوق. هذا التمييز هو ما يسمح لاحقاً بفصلها على صندوقٍ خاص (إقليم ٧).
- «الـ codebase خادم.» ليست خادماً؛ هي ملفّاتٌ ينفّذها الـ app server.
أنجِز مخطّط Task 0 كاملاً على ورقة، ثم اكتب بجانب كل عنصرٍ جملةً واحدةً تجيب عن سؤال المشروع المقابل:
- ما دور domain name؟ ما نوع سجلّ
www(من الإقليم ٠)؟ - ما دور web server؟ ما دور application server؟ ما دور database؟
- بأي شيءٍ يتواصل الخادم مع جهاز المستخدم؟
ثم — وهذا الأهم — اكتب تحت المخطّط ثلاث مشاكل في هذه البنية. لا تقرأ القسم التالي قبل أن تحاول استخراجها بنفسك من العدسة: «ما الذي يُسقط هذا النظام؟».
بذرة الانهيار: لماذا هذا الصندوق الأنيق قنبلةٌ موقوتة
المشروع يطلب أن تشرح مشاكل هذه البنية. لاحظ أنها كلها تنبع من حقيقةٍ واحدة: كل شيءٍ في صندوقٍ واحد.
- SPOF (نقطة انهيارٍ واحدة): الصندوق يموت (قرص، طاقة، تحديث فاشل) ⟵ الموقع كله يختفي. لا بديل.
- توقّفٌ عند الصيانة (downtime on deploy): تنشر كوداً جديداً أو تعيد تشغيل الـ web server ⟵ الموقع ينقطع أثناء ذلك.
- لا يتوسّع (cannot scale): يزيد الزوّار فوق طاقة الصندوق ⟵ يبطؤ ثم يسقط، ولا مكان لإضافة طاقة.
هذه الثلاث ليست عيوباً متفرّقة — هي عَرَضٌ واحدٌ: التفرّد. كل ما تبقّى من المنهج هو تفكيك هذا التفرّد. لكن أول ما يجب أن نسمّيه ونفهمه بدقّةٍ هو الوحش المركزي: SPOF. وهذا هو الإقليم ٢.
الخلاصة — وصلٌ في الشجرة
- فتحتَ الصندوق: أربعة أدوار (web server, app server, codebase, database) ولماذا تُفصَل — مبدأ فصل الاهتمامات.
- ميّزتَ الثابت من الديناميكي، وهو الفرق الذي يفسّر كل تقسيمٍ قادم.
- ربطتَ كل دورٍ بسؤال Task 0، وعرفتَ LAMP وفخّ LEMP/Nginx.
- استخرجتَ المشاكل الثلاث، ورأيتَ أنها وجهٌ واحد: التفرّد ⟵ بوّابة الإقليم ٢.
Inside the one server, roles are separated:
- Web server (Nginx): receives HTTP, serves static files, and reverse-proxies
dynamic requests to the app server.
- Application server: runs my codebase to generate dynamic pages.
- Application files (codebase): the code the app server executes.
- Database (MySQL): stores persistent data; the app queries it over SQL.
This is a LAMP stack (Linux/Apache/MySQL/PHP) — with Nginx instead of Apache it's technically LEMP.
Problems: it's a SPOF (server dies → whole site down); downtime on maintenance (restarting/deploying takes the site offline); and can't scale beyond this one machine's capacity.
وقفة الانضباط: الفرق بين web و app server يُجاب في جملتين (يخدم static + يمرّر / يشغّل الكود). لا تشرح PHP-FPM أو WSGI ما لم يُطلب.