عدة مواقع على VPS واحد: العزل والتنظيم بدون ما يسحب موقع البقية معه

تشغيل عدة مواقع على خادم واحد يوفّر كثيراً، لكن بلا عزل يكفي موقع واحد ثقيل ليُسقط البقية معه. الحل ثلاث طبقات: virtual host لكل موقع، مستخدم لينكس منفصل، وPHP-FPM pool بحدّ ذاكرة لكل موقع.
تشغيل عدة مواقع على VPS واحد قرار صحيح مادياً، بشرط أن يكون كل موقع معزولاً عن جاره في ثلاث طبقات: virtual host (server block) يفصل النطاقات على مستوى الويب سيرفر، مستخدم لينكس منفصل لكل موقع يفصل الملفات والصلاحيات، وPHP-FPM pool مستقل بحدّ ذاكرة يمنع موقعاً واحداً من التهام كل الـRAM. بدون هذه الطبقات يصير الخادم بيتاً بلا جدران داخلية، فطفرة زيارات على موقع واحد أو استعلام قاعدة بيانات ثقيل يجمّد كل المواقع دفعة واحدة. مع العزل الصحيح، الموقع المتعثر يسقط وحده ويبقى الباقي شغّالاً.
القاعدة العملية للسعة: باقة أعمال بـ8GB RAM و4 vCPU تستوعب بأريحية من 8 إلى 12 موقعاً معتدل الحركة (مواقع تعريفية ومدونات ومتاجر صغيرة)، أو من 3 إلى 4 تطبيقات ثقيلة (Laravel أو WordPress بحركة عالية). المتغيّر الحاسم ليس عدد المواقع بل ذروة الطلبات المتزامنة وحجم كل عملية PHP. في هذا الدليل نبني العزل خطوة خطوة، ثم نحسب السعة بأرقام، ونحدد متى تنتقل من هذا النمط إلى سيرفرات منفصلة.
الطبقة الأولى: virtual host لكل نطاق
الـvirtual host (يسمى server block في Nginx) هو ما يجعل الخادم يعرف أن الطلب القادم لـsite-a.sa يخدمه من مجلد، وطلب site-b.sa من مجلد آخر، على نفس الـIP والمنفذ. كل موقع يحصل على ملف تخصيص مستقل، جذر مستندات (document root) خاص به، وشهادة SSL منفصلة. هذا الفصل ضروري لكنه سطحي، لأنه يفصل التوجيه فقط لا الموارد ولا الصلاحيات.
في Nginx يكون لكل موقع server block في /etc/nginx/sites-available/ يشير إلى pool الخاص به عبر fastcgi_pass. الخطأ الشائع أن يشترك موقعان في نفس الجذر أو نفس مجلد الرفع، فيصير اختراق موقع بابَ دخول لجاره. اجعل كل جذر مستندات تحت مجلد مستخدمه الخاص، لا تحت مجلد مشترك.
الطبقة الثانية: مستخدم لينكس منفصل لكل موقع
هذه الطبقة هي جدار الأمان الحقيقي. أنشئ مستخدم نظام لكل موقع، وضع ملفاته تحت مجلده المنزلي بملكية كاملة له. النتيجة أن عملية PHP الخاصة بالموقع الأول لا تقدر تقرأ ملف wp-config.php أو .env الخاص بالموقع الثاني، لأن صلاحيات الملفات تمنعها. لو اختُرق موقع عبر إضافة مصابة، يبقى الضرر محبوساً داخل مستخدمه ولا يمتد للبقية.
الإعداد الصحيح للصلاحيات: المجلدات 750 والملفات 640، والمالك هو مستخدم الموقع ومجموعته هي مجموعة الويب سيرفر (www-data). امنع تماماً استخدام 777 على أي مجلد رفع، فهي تفتح الباب لتنفيذ سكربتات مرفوعة. اربط كل مستخدم بـpool خاص يعمل تحت هويته، وبذلك يتطابق العزل بين طبقة الملفات وطبقة التنفيذ.
الطبقة الثالثة: PHP-FPM pool بحدّ ذاكرة لكل موقع
هذه الطبقة تحسم منع موقع واحد من إسقاط البقية. بدل pool واحد مشترك لكل PHP على الخادم، أنشئ pool مستقلاً لكل موقع، كل واحد يعمل تحت مستخدم الموقع وله حدوده الخاصة في عدد العمليات (workers) وحدّ الذاكرة لكل عملية. الفكرة أن كل pool يحجز حصته من الـRAM بسقف واضح، فلو تعرّض موقع لطفرة طلبات، عملياته تصل سقفها المحدد ثم تنتظر في الطابور، بدل أن تلتهم ذاكرة الخادم كله وتخنق الجيران.
القيم الأهم في كل pool:
- pm.max_children: أقصى عدد عمليات PHP متزامنة للموقع. هذا هو السقف الفعلي لاستهلاك الذاكرة.
- pm = ondemand للمواقع الخفيفة: العمليات تُنشأ عند الطلب وتُغلق بعد خمول قصير، فلا تحجز ذاكرة وهي نائمة.
- pm = dynamic للمواقع النشطة: تبقي عدداً أدنى من العمليات جاهزاً لتقليل زمن الاستجابة.
- php_admin_value[memory_limit]: حدّ ذاكرة العملية الواحدة، عادة من 128MB إلى 256MB لتطبيق متوسط.
حساب السقف بالأرقام
القاعدة البسيطة: أقصى ذاكرة لموقع = pm.max_children × متوسط ذاكرة العملية. لو كل عملية Laravel تستهلك وسطياً 80MB وضبطت max_children على 10، فالموقع لن يتجاوز نحو 800MB مهما كان الضغط. اجمع سقوف كل المواقع واحرص ألا يتعدى المجموع الـRAM المتاح ناقص حصة النظام وقاعدة البيانات.
حساب السعة على باقة أعمال 8GB
لنوزّع 8GB واقعياً على باقة أعمال (4 vCPU / 8GB):
| البند | الحجز | الملاحظة |
|---|---|---|
| نظام التشغيل + Nginx | نحو 1GB | خدمات أساسية |
| MySQL / MariaDB | من 1.5 إلى 2GB | حسب حجم الفهارس والذاكرة المؤقتة |
| متبقٍ للمواقع (PHP-FPM) | من 5 إلى 5.5GB | يُقسَّم على الـpools |
لو خصصنا وسطياً 500MB لكل موقع معتدل (نحو 6 عمليات × 80MB لكل عملية)، فإن 5GB تكفي عملياً من 8 إلى 10 مواقع معتدلة مع هامش أمان. لو المواقع خفيفة جداً (تعريفية بحركة قليلة) بوضع ondemand، يرتفع العدد إلى 12 موقعاً وأكثر لأنها لا تحجز ذاكرة وهي خاملة. أما التطبيقات الثقيلة (متجر بحركة عالية أو تطبيق SaaS)، فيكفي 3 إلى 4 على نفس الباقة قبل أن تصير الترقية أذكى من الازدحام.
لا تنسَ الـCPU والقرص
الذاكرة أول ما ينفد عادة، لكن راقب أيضاً الـ4 vCPU: كل عملية PHP تحت حمل حقيقي تأخذ نصيبها من المعالج، وطفرة متزامنة على عدة مواقع ترفع الحمل (load average) فوق عدد الأنوية فيبطئ الجميع. القرص كذلك مشترك، فموقع يكتب سجلات ضخمة أو نسخاً احتياطية يزاحم البقية على الإدخال والإخراج. راقب الثلاثة معاً لا الذاكرة وحدها.
متى تنتقل إلى سيرفرات منفصلة
النمط المشترك ممتاز حتى تصل إحدى هذه العتبات:
- الاستخدام المستدام فوق 70% من الـRAM أو الـCPU لأيام متتالية لا لحظات ذروة عابرة. هنا الترقية أو الفصل ضرورة.
- موقع صار حرجاً تجارياً: متجر أو تطبيق دخله يعتمد على استقراره لا يجب أن يشارك مصيره مع مواقع تجريبية.
- متطلبات امتثال أو عزل بيانات: جهة حكومية أو بيانات حساسة قد تفرض خادماً مخصصاً لا مشتركاً.
- أنماط حمل متضاربة: موقع يعتمد على المعالج (معالجة صور) بجانب موقع يعتمد على القرص (قاعدة بيانات ثقيلة) يتنافسان على موارد مختلفة، فالفصل يريح الاثنين.
الميزة على كلاود أن الترقية لا تعني نقلاً. تضيف +2GB RAM بـ600 ريال سنوياً أو +1 vCPU بـ500 ريال على نفس الخادم دون ترحيل الملفات، فتؤجل قرار الفصل حتى يصير ضرورياً فعلاً لا احترازياً. راجع صفحة الخادم السحابي والأسعار والباقات لتفاصيل الإضافات.
قاعدة بيانات معزولة لكل موقع
العزل لا يكتمل بلا فصل قواعد البيانات. أنشئ قاعدة بيانات منفصلة ومستخدم MySQL خاصاً لكل موقع، بصلاحيات محصورة على قاعدته وحدها. النتيجة أن تسريب بيانات اتصال موقع لا يفتح بقية قواعد البيانات، وأن استعلاماً ثقيلاً في موقع لا يقفل جداول موقع آخر. اضبط أيضاً حدود الذاكرة المؤقتة في MySQL بوعي، لأن قاعدة بيانات واحدة نهمة قد تلتهم ذاكرة الخادم على حساب الجميع. القاعدة العملية: مستخدم DB لكل موقع بصلاحيات دنيا، وقاعدة منفصلة لكل تطبيق، ومراقبة الاستعلامات البطيئة عبر slow query log لاكتشاف الموقع الذي يزاحم البقية قبل أن يسقطها. لو صار موقع كثيف الاستعلامات، انقل قاعدته إلى خادم قاعدة بيانات منفصل، فهذا أول ما يُفصل عادة قبل فصل الويب سيرفر نفسه.
الخلاصة العملية
لتشغيل عدة مواقع على VPS واحد بأمان: (1) server block مستقل وشهادة SSL لكل نطاق، (2) مستخدم لينكس منفصل بصلاحيات 750/640 لكل موقع، (3) PHP-FPM pool لكل موقع بحدّ max_children وmemory_limit محسوبَين. احسب سقف الذاكرة لكل pool واجمعها لتبقى دون الـRAM المتاح، وراقب الذاكرة والمعالج والقرص معاً. حين يستقر الاستخدام فوق 70% أو يصير موقع حرجاً تجارياً، رقِّ الموارد أولاً على كلاود بلا نقل، ثم افصل السيرفرات إن استمر النمو. ابدأ بباقة أعمال 8GB وضع 8 إلى 10 مواقع معتدلة عليها بثقة. راقب النمو شهرياً، فإذا اقترب أي مورد من 70% مستداماً فالوقت مناسب لإضافة الموارد أو فصل أثقل موقع إلى خادمه الخاص قبل أن يتضرر الجيران، فالعزل الجيد يشتري لك وقتاً والترقية بلا نقل تشتري لك بقية الطريق.


