DEVOPS · SERVER MIGRATION · REAL STORY

كيف نقلت موقع ضخم بـ 17 ساب دومين لسيرفر جديد بدون ما أغيّر سطر كود واحد

رحلة نقل منظومة مواقع متكاملة من VPS قديم لسيرفر جديد — بكل التفاصيل والتحديات الحقيقية

وقت القراءة: 8 دقايقDevOps · Server Migrationموجه لـ Developers & Tech Founders
PHP 7.4 LegacyYii2 FrameworkMySQLApacheWebpack 2 + Vue 2Webpack 5 + SassUbuntu 22.04 LTS17 Subdomains ✓SSL Active ✓
// 01

البداية — ليه قررنا ننقل أصلاً؟

المشروع كان شغّال من سنين على VPS بالسحابة. الموقع كان فيه أكتر من 17 ساب دومين، داشبورد إدارة مشتركة لبراندين مختلفين، وقاعدة بيانات كبيرة فيها كل بيانات العملاء والمنتجات.

القرار مجاش من فراغ. الموضوع كان مزيج من: تكلفة الاستضافة بدأت ترتفع، والرغبة في تحكم أكبر في البنية التحتية، وإننا نلاقي بيئة أكثر استقرار وأوفر.

⚠ التحدي الأكبر من الأول

الـ stack المستخدم قديم — PHP 7.4 مع Yii2 Framework، وعندنا نوعين من الـ Frontend Tooling: Webpack 2 مع Vue 2 لجزء، وWebpack 5 مع Sass للجزء التاني. القرار كان واضح من الأول: ننقل من غير ما نعدل كود.


// 02

تجهيز السيرفر الجديد من الصفر

أول حاجة عملناها بعد ما اشترينا السيرفر الجديد — إننا نتأكد من إصدار الـ OS. وهنا واجهنا أول مفاجأة.

🚨 مشكلة ظهرت من أول دقيقة

السيرفر اتفتح بـ Ubuntu 24.04 — وده كان مشكلة. النسخة دي مش بتدعم MySQL 5.7 بشكل طبيعي، والـ PHP 7.4 بيحتاج workarounds كتير عليها. الحل؟ إعادة تنصيب الـ OS فوراً على Ubuntu 22.04 LTS قبل ما نعمل أي حاجة.

بعد كده، بدأنا التجهيز الحقيقي بالترتيب:

تحديث النظام وتنصيب الأدوات الأساسية

apt update, apt upgrade، وتنصيب كل الأدوات المهمة زي curl وgit وufw وfail2ban.

إنشاء يوزر منفصل عن الـ root

أفضل ممارسة أمنية — ما تشتغلش على الـ root طول الوقت. عملنا deploy user بصلاحيات sudo.

إعداد الـ Firewall (UFW)

فتحنا بس 3 بورتات: 22 للـ SSH، 80 للـ HTTP، 443 للـ HTTPS. كل حاجة تانية مقفولة.

تفعيل fail2ban

حماية تلقائية من Brute Force — لو حد غلط في الباسورد 5 مرات، اتبان تلقائياً لمدة ساعة.

firewall verification
ufw status verbose

Status: active
To          Action    From
──          ──────    ────
22/tcp      ALLOW IN  Anywhere
80/tcp      ALLOW IN  Anywhere
443/tcp     ALLOW IN  Anywhere

// 03

تنصيب البيئة الكاملة

الموقع بيحتاج بيئة محددة — والهدف إننا نطابق البيئة القديمة بالظبط من غير ما نرقي حاجة:

ℹ البيئة المطلوبة

Apache 2.4 · PHP 7.4 مع كل الـ Extensions · MySQL 8.0 بوضع التوافق مع 5.7 · Composer · Node.js نسختين مختلفتين (12 و16) عبر nvm

سبب نسختين من Node؟ — جزء من الـ Frontend (الأقدم) بيحتاج Webpack 2 مع Node 12، والجزء التاني بيحتاج Webpack 5 مع Node 16. الاتنين لازم يشتغلوا على نفس السيرفر.

⚠ MySQL 5.7 — الحقيقة المرّة

MySQL 5.7 مش متاح على Ubuntu 22.04 خالص. الحل الاحترافي: تنصيب MySQL 8.0 وضبطه يتصرف زي 5.7 عن طريق إضافة إعدادات توافق في ملف الـ config — وده نجح 100% مع الكود القديم من غير أي تعديلات.

php extensions verification
php -m | grep -E 'pdo_mysql|intl|gd|mbstring|curl|zip|opcache'

gd
intl
mbstring
opcache
pdo_mysql
curl
zip

# ✓ كل الـ Extensions الضرورية موجودة

// 04

نقل الملفات وقاعدة البيانات

الباك اب كان جاهز — ملف tar.gz واحد فيه كل ملفات الموقع، وملفات SQL منفصلة لكل قاعدة بيانات. النقل حصل مباشرة من السيرفر القديم للجديد — أسرع بكتير من إنك تنزله لوكال وترفعه تاني.

server-to-server transfer (fastest method)
# من السيرفر القديم — نقل مباشر للسيرفر الجديد
scp ~/full-backup.tar.gz root@NEW_SERVER_IP:/var/www/project/

# نقل ملفات الـ DB
scp ~/database1.sql ~/database2.sql root@NEW_SERVER_IP:/var/www/project/backups/db/

# على السيرفر الجديد — فك الضغط
cd /var/www/project && tar -xzvf full-backup.tar.gz

هيكل المجلدات اتنظم بشكل احترافي — كل مشروع في مجلد منفصل تحت /var/www/project/، مع مجلدات منفصلة للـ logs والـ backups.

بالنسبة لقواعد البيانات — عندنا 4 databases لمشاريع مختلفة. كل واحدة اتعمل لها:

create isolated db user per project
-- مش بنستخدم root للتطبيق — كل مشروع ليه يوزر منفصل
CREATE DATABASE project_db CHARACTER SET utf8mb4;
CREATE USER 'project_user'@'localhost' IDENTIFIED WITH
  mysql_native_password BY 'StrongPassword!';
GRANT ALL PRIVILEGES ON project_db.* TO 'project_user'@'localhost';

-- ثم الاستيراد
mysql -u root -p project_db < /backups/db/project.sql
💡 نصيحة مهمة

لو الباسورد فيه رمز # — لازم تحطه بين quotation marks في ملف الـ .env. مثال: DB_PASSWORD="MyPass#2024" — من غير الـ quotes، كل حاجة بعد الـ # بتتجاهل وبيجي خطأ في الـ connection.


// 05

ربط الـ Domains وإعداد الـ Apache Virtual Hosts

17 ساب دومين — كل واحد محتاج ملف .conf منفصل في Apache يوجّهه للمجلد الصح. الموضوع منظم ومش معقد، بس محتاج دقة.

apache vhost — pattern for each subdomain
<VirtualHost *:80>
    ServerName subdomain.domain.com
    DocumentRoot /var/www/project/app/web
    <Directory /var/www/project/app/web>
        AllowOverride All
        Require all granted
    </Directory>
    ErrorLog /var/www/project/logs/apache/sub-error.log
</VirtualHost>

قبل ما نلمس الـ DNS — عملنا تست لوكال من خلال ملف الـ hosts على الجهاز:

Windows: C:\Windows\System32\drivers\etc\hosts
# نضيف مؤقتاً لاختبار السيرفر الجديد بدون تغيير DNS
NEW_SERVER_IP  domain.com
NEW_SERVER_IP  admin.domain.com
NEW_SERVER_IP  api.domain.com

# بعد التأكد من كل حاجة — نشيل السطور دي ونغير الـ DNS
✅ الاختبار الذهبي قبل التحويل

التست عبر ملف الـ hosts خلّانا نشوف كل الـ 17 ساب دومين شغالين بشكل كامل على السيرفر الجديد — قبل ما أي مستخدم يحس بأي حاجة.


// 06

الـ SSL Certificates — الموقع يبقى Secure

بعد تحويل الـ DNS وتأكدنا إن كل حاجة وصلت للسيرفر الجديد — جه دور الـ SSL. استخدمنا Let’s Encrypt عبر Certbot، وطلبنا certificate واحد يغطي كل الـ subdomains مرة واحدة.

certbot — single command for all subdomains
certbot --apache \
  -d domain.com -d www.domain.com \
  -d admin.domain.com \
  -d api.domain.com \
  -d storage.domain.com \
  --email admin@domain.com \
  --agree-tos --no-eff-email

Successfully received certificate.
Congratulations! You have successfully enabled HTTPS.
⚠ تفصيلة مهمة في الـ DNS

لو عندك www كـ CNAME record بيشاور على الـ root domain — Certbot مش هيقدر يعمل SSL ليه. الحل: احذف الـ CNAME واعمل A record مباشرة بالـ IP الجديد.


// 07

التحديات — الجزء اللي مش بيتقال عادةً

المشاريع الكبيرة فيها مفاجآت — وده طبيعي. الفرق بين المحترف والمبتدئ إن المحترف يعرف يتوقع المشاكل ويحلها بسرعة. إيه اللي صادفناه:

MySQL 5.7 مش متاح على Ubuntu 22.04

الحل كان MySQL 8.0 مع ضبط إعدادات التوافق — نجح بدون أي تعديل في الكود.

short_open_tag معطّل افتراضياً

الـ Views القديمة بتستخدم <? بدل <?php. تفعيل الـ short_open_tag في php.ini حلّ المشكلة مرة واحدة لكل الملفات.

الـ # في الباسورد بيكسر الـ Dotenv

الـ # في ملف الـ .env بيُعامَل كـ comment. الحل: أي باسورد فيه # لازم يتحط بين quotes.

DNS Propagation وقت الانتظار

تغيير الـ DNS محتاج وقت — من 5 دقايق لـ 48 ساعة حسب الـ TTL. الحل: نخفض الـ TTL لـ 5 دقايق قبل النقل بـ 24 ساعة.

الصور مش بتظهر بعد الـ SSL

ملفات الـ .env كانت بتشاور على storage بـ https لكن الـ SSL ما تفعّلش لسه. الترتيب مهم: SSL الأول، ثم التحقق من الصور.

نسختين من Node على نفس السيرفر

Webpack 2 بيحتاج Node 12، وWebpack 5 بيحتاج Node 16. الحل: nvm لإدارة الإصدارات وسهل التبديل بينها.


// 08

النتيجة النهائية

في النهاية — الموقع شغّال بالكامل على السيرفر الجديد. كل الخدمات مربوطة. كل الساب دومينات آمنة بـ HTTPS. والأهم: ما اتغيّر سطر كود واحد.

17
Subdomain شغّالين
4
Databases منقولة
0
تعديل في الكود
SSL على كل الدومينات
✅ الدرس الأهم

نقل موقع ضخم مش بيعتمد على القوة — بيعتمد على الترتيب والتخطيط والاختبار قبل التحويل. لو اتبعت الخطوات بالترتيب الصح واختبرت كل حاجة لوكال قبل ما تلمس الـ DNS — هتعدي المشروع من غير ما أي مستخدم يحس بأي حاجة.

💬 عندك موقع محتاج ينقل أو سيرفر محتاج يتجهز؟ الخطوات دي قابلة للتطبيق على أي stack — المبدأ واحد، التفاصيل بس بتتغير.

تواصل معي