SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor

ترحيل خادم إلى VPS جديد دون توقف

تعلّم ترحيل خادم Linux حي إلى VPS جديد بخطة مُجرَّبة: جرد الإعدادات، إعادة البناء، تفريغ قاعدة البيانات، خفض TTL، والتحقق قبل تبديل DNS.

ترحيل خادم إلى VPS جديد بوصفه عملية تحويل مُجرَّبة

لترحيل خادم إلى VPS جديد، تعامل مع النقل بوصفه عملية تحويل مُجرَّبة، لا مجرد نسخ. أنشئ الخادم الجديد من الصفر، وزامن البيانات مرتين، وتحقق من أن الخادم الجديد يعمل بشكل مستقل على عنوان IP الخاص به قبل تعديل DNS، ثم بدّل السجلات واترك الخادم القديم قيد التشغيل حتى تتأكد تماماً. نسخ البيانات هو الجزء السهل. أما ترتيب العمليات فهو الذي يحدد ما إذا كان النقل سيمر بسلاسة أو سيتسبب في تكلفة كبيرة.

يغطي هذا الدليل خادم Linux واحداً يشغّل تطبيق ويب وقاعدة بيانات وشهادة TLS (أمان طبقة النقل). وينطبق ذلك على معظم إعدادات الخوادم المنفردة. يوجد مضيفان هنا، لذلك يوضح كل مثال في تعليق المضيف الذي يُنفَّذ عليه. تأتي العناوين من نطاقات التوثيق: 198.51.100.10 هو الخادم القديم، و203.0.113.20 هو الخادم الجديد.

اقرأ دليل الإجراءات كاملاً قبل البدء. يجب تنفيذ الخطوة الأولى، وهي خفض TTL الخاص بـDNS، قبل عدة أيام من الخطوة الفعلية التي تهمك.

أنشئ جرداً قبل أن تبني أي شيء

لا يمكنك إعادة بناء خادم لم تصف مكوّناته ووظائفه. خصّص ساعة لتدوين ما يفعله الخادم القديم، لأن العنصر الذي يتعطل بعد الترحيل يكون دائماً العنصر الذي لم يتذكره أحد: مهمة cron، أو استثناء في الجدار الناري، أو ملف بيئة موجود خارج دليل التطبيق.

شغّل هذه الأوامر على الخادم القديم، واحتفظ بالمخرجات في مكان يمكنك الوصول إليه من الخادم الجديد.

# old server: packages you asked for, not the dependencies they pulled in
apt-mark showmanual > ~/inv-packages.txt

# old server: what runs now, and what starts at boot
systemctl list-units --type=service --state=running --no-pager > ~/inv-services.txt
systemctl list-unit-files --state=enabled --no-pager >> ~/inv-services.txt
systemctl list-timers --all --no-pager > ~/inv-timers.txt

# old server: what is listening, on which address and port
sudo ss -tulpn > ~/inv-ports.txt

# old server: human accounts, skipping the system ones
awk -F: '$3 >= 1000 && $3 < 65534 {print $1, $3, $6, $7}' /etc/passwd

تستحق apt-mark showmanual الاحتفاظ بها لأنها تعرض كل ما ثُبّت كتبعيات. أما dpkg --get-selections الكامل على خادم عمره خمس سنوات فيُرجع ألفي سطر ولا يخبرك بشيء عن الغرض من هذه الحزم.

توجد المهام المجدولة في مكانين، لذلك تحقّق منهما معاً. المهمة التي تعمل مرة واحدة شهرياً هي التي ستكتشفها بعد ستة أسابيع من الترحيل.

# old server: per-user crontabs, then the system drop-ins
for u in $(cut -d: -f1 /etc/passwd); do sudo crontab -lu "$u" 2>/dev/null | sed "s/^/$u: /"; done
sudo ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly

ثم افحص العناصر التي ليست ملفات عادية: قواعد الجدار الناري، والشهادات، وقواعد البيانات، وحجم البيانات التي ستنقلها فعلياً.

# old server
sudo ufw status verbose        # or: sudo nft list ruleset
sudo certbot certificates
sudo -u postgres psql -c '\l'  # or: sudo mysql -e 'SHOW DATABASES;'
sudo du -xh --max-depth=1 / | sort -h

يطبع certbot certificates اسم كل شهادة، والنطاقات التي تغطيها، وتاريخ انتهاء صلاحيتها، ومسارات الملفات الموجودة على القرص. هذه المخرجات هي قائمة التحقق الخاصة بـTLS. يبقى du -x ضمن نظام ملفات واحد، لذلك لن يدخل إلى وحدة تخزين نسخ احتياطية مركّبة ويعرض رقماً أكبر من الحجم الفعلي بعشر مرات.

هناك عنصران خارج الخادم ويُنسَيان في كل مرة. أولاً، أي جهة خارجية تضع عنوان IP الخاص بخادمك في قائمة سماح: بوابة دفع، أو قاعدة بيانات مُدارة، أو مرحّل SMTP، أو واجهة API لشريك. سيكون للخادم الجديد عنوان مختلف، لذلك يجب إضافة عنوان IP الجديد إلى قوائم السماح قبل التحويل، لا بعده. ثانياً، سجلات DNS التي لم تنشئها بنفسك، مثل سجل MX أو سجل SPF يتضمن عنوان IP القديم في نصه.

لماذا تعيد البناء بدلاً من استنساخ نظام الملفات الجذر القديم

يبدو استنساخ نظام ملفات جذر كامل إلى VPS الجديد أسرع، وهو كذلك إلى أن تظهر مشكلات. يحمل نظام ملفات الجذر الذي ظل في بيئة الإنتاج لسنوات إعدادات عُدّلت يدوياً ولم يوثّقها أحد، وحزمًا من مستودع لم يعد موجوداً، وإعداد إقلاع بُني لعتاد المنصة الافتراضية القديمة. ستستورد كل ذلك، بما في ذلك سبب الترحيل.

إعادة البناء أبطأ في اليوم الأول، لكنها أقل تكلفة في كل يوم بعده. ثبّت الإصدار الحالي، وطبّق إجراءات التحصين الأساسية، ثم انسخ البيانات فقط: مجلد التطبيق، وإعدادات المواقع، وتفريغ قاعدة البيانات، والشهادات، والملفات التي رفعها المستخدمون. لا تنقل أي شيء لا تستطيع تفسيره. ابدأ الخادم الجديد بالطريقة نفسها التي تبدأ بها أي خادم، عبر الدقائق العشر الأولى على VPS جديد، ثم أضف الخدمات من قائمة الجرد واحدةً تلو الأخرى، وتحقق من كل خدمة قبل إضافة التالية.

عندما يكون استعادة صورة أو لقطة هي الخيار المناسب

هناك استثناء واضح لإعادة البناء. إذا كان الخادم القديم لن يقلع، أو كان التطبيق من النوع الذي لم يعد بالإمكان إعادة بنائه من المصدر، فإن استعادة صورة أو لقطة من مزوّد الخدمة تكون الحل العملي. لكن لهذا الخيار قيود فعلية: فهو يعمل داخل مزوّد خدمة واحد، وغالباً ضمن عائلة خطة واحدة فقط، لأن القرص المستعاد يتوقع الأجهزة الافتراضية وتسميات الشبكة الخاصة بذلك النظام الأساسي.

تحمل لقطة خادم قيد التشغيل أيضاً مشكلة الاتساق نفسها الموجودة في أي نسخ آخر على مستوى الملفات لقاعدة بيانات قيد التشغيل. تعامل مع استعادة الصورة كمسار للتعافي، لا كخطة للترحيل، واقرأ لماذا لا تعادل اللقطة نسخة احتياطية قبل أن تبني خطة اعتماداً عليها.

كيفية انتقال الملفات: rsync عبر SSH

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

# old server: dry run first, and read what it says it will do
sudo rsync -aHAX --dry-run --itemize-changes \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

# old server: the real bulk pass
sudo rsync -aHAX --info=progress2 \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

الخيارات مهمة. يحافظ -a على الصلاحيات والطوابع الزمنية والروابط الرمزية والملكية. ويحافظ -H على الروابط الصلبة بوصفها روابط صلبة بدلاً من توسيعها إلى نسخ منفصلة. وينسخ -A قوائم التحكم في الوصول POSIX (ACLs)، بينما ينسخ -X السمات الموسّعة. من دون الخيارين الأخيرين، قد يبدو الملف مطابقاً لكنه يتصرف بشكل مختلف، لأن تسميات SELinux وقوائم ACL موجودة ضمن السمات الموسّعة ولا يسجلها أي شيء آخر.

هناك تفصيلان يسببان معظم حالات الفشل هنا.

تحدد الشرطة المائلة في نهاية المسار مكان وضع البيانات. يعني /srv/app/ محتويات ذلك الدليل. أما /srv/app فيعني الدليل نفسه. إذا أخطأت، فسينتهي بك الأمر إلى /srv/app/app على الخادم الجديد، ثم يبدأ التطبيق ويبلغ عن ملفات مفقودة، لأن المسارات المعرّف بها أصبحت أقصر بمستوى واحد مما ينبغي.

ضمن sudo، تشير التلدة إلى الدليل الرئيسي لـroot. تؤدي كتابة -e 'ssh -i ~/.ssh/id_ed25519' داخل sudo rsync إلى البحث عن المفتاح في /root/.ssh، لا في دليلك الرئيسي. إذا لم يكن المفتاح موجوداً هناك، فسيطبع SSH الرسالة Permission denied (publickey)، ويطبع rsync الرسالة rsync: connection unexpectedly closed ثم ينتهي برمز خروج غير صفري. اكتب مسار المفتاح كاملاً. إذا استمرت رسالة المصادقة هذه في الظهور بعد تصحيح المسار، لفشل publickey قائمة قصيرة من الأسباب، وتكون صلاحيات الدليل على الخادم الجديد هي السبب التالي الذي ينبغي فحصه.

تتطلب الملكية قراراً واحداً. عند تشغيل rsync بصلاحيات root، يطابق المالك والمجموعة حسب الاسم افتراضياً، لذلك يصبح الملف المملوك لـwww-data على الخادم القديم مملوكاً لـwww-data على الخادم الجديد حتى إذا اختلف UID (معرّف المستخدم) الرقمي. هذا هو المطلوب عند إعادة البناء. أضف --numeric-ids فقط عند نسخ نظام ملفات لا توجد حساباته على الهدف، ثم تحقق من النتيجة باستخدام ls -ln، لأن الملف المملوك لـUID لا يقابله حساب يظهر كرقم مجرد، وتُمنع كل خدمة تقرأه من الوصول إليه.

نفّذ النقل الأساسي قبل أيام، بينما لا يزال الخادم القديم يخدم حركة الشبكة. كرره بقدر ما تشاء: يرسل rsync البيانات التي تغيّرت فقط، لذلك تستغرق الجولة الثانية دقائق بدلاً من ساعات. تضيف الجولة النهائية، ضمن نافذة التحويل، --delete حتى تختفي من الخادم الجديد الملفات التي حُذفت من الخادم القديم أيضاً.

# old server: final pass, inside the window, after the app has stopped writing
sudo rsync -aHAX --delete --info=progress2 \
  -e 'ssh -i /root/.ssh/id_ed25519' \
  /srv/app/ deploy@203.0.113.20:/srv/app/

يزيل --delete من الوجهة الملفات غير الموجودة في المصدر، لذلك قد يؤدي مسار مصدر خاطئ مع --delete إلى إفراغ دليل الوجهة. شغّله أولاً باستخدام --dry-run في كل مرة دون استثناء. كما تفشل عمليات النقل الطويلة عندما تنقطع جلسة SSH على حاسوبك المحمول، لذلك ابدأها داخل tmux أو screen على الخادم القديم. أضف --bwlimit=20M إذا كان النسخ يستنفد سعة الاتصال بينما لا يزال الخادم القديم يخدم المستخدمين.

نقل قاعدة البيانات: تفريغ باستخدام الأداة الأصلية

قاعدة البيانات ليست مجلداً من الملفات، حتى إن بدت كذلك. إنها مجموعة من الملفات، وحالة موجودة في الذاكرة، وسجل كتابة مسبق، ولا تكون متسقة إلا في اللحظات التي تحددها قاعدة البيانات نفسها. استخدم أداتها الخاصة.

يحتاج PostgreSQL إلى تفريغين، لأن الأدوار تكون على مستوى العنقود، ولأن pg_dump لا يتضمنها:

# old server
sudo -u postgres pg_dumpall --globals-only -f /var/backups/globals.sql
sudo -u postgres pg_dump -Fc -f /var/backups/appdb.dump appdb
# new server: restore in this order
sudo -u postgres psql -f /var/backups/globals.sql
sudo -u postgres createdb -O appuser appdb
sudo -u postgres pg_restore -d appdb --no-owner /var/backups/appdb.dump

إذا تخطيت globals.sql، فستُستعاد كل الجداول ولن يتمكن أي دور للتطبيق من قراءتها، لأن عبارات GRANT تشير إلى مستخدم غير موجود. يكتب -Fc تنسيق الأرشيف المخصص، ولا يقرأه إلا pg_restore، كما يتيح لك استعادة جداول محددة لاحقاً. استعد البيانات إلى الإصدار الرئيسي نفسه أو إلى إصدار أحدث. لا يُدعم الرجوع من 17 إلى 16، مثلاً، ويرفض pg_restore الأرشيف بخطأ إصدار غير مدعوم في ترويسة الملف قبل أن يكتب أي شيء.

يستخدم MySQL وMariaDB أمراً واحداً، مع أربعة خيارات لا تكون مفعّلة افتراضياً:

# old server
sudo mysqldump --single-transaction --routines --triggers --events \
  --databases appdb > /var/backups/appdb.sql
# new server
sudo mysql < /var/backups/appdb.sql

يأخذ --single-transaction لقطة متسقة من دون حظر عمليات الكتابة، لكن ذلك ينطبق على جداول InnoDB فقط. إذا كان هناك جدول MyISAM في قاعدة البيانات نفسها، فسيُنسخ من دون هذا الضمان. لذلك تحقّق من محركات التخزين قبل أن تثق بالتفريغ. تكون --routines و--triggers و--events معطّلة افتراضياً، ما يعني أن التفريغ النصي يستعيد بياناتك ويترك بصمت الإجراءات المخزنة والأحداث المجدولة. يوجد مستخدمو قاعدة البيانات ومنح الصلاحيات الخاصة بهم في قاعدة النظام mysql، ولا يلمس تفريغ --databases appdb هذه القاعدة مطلقاً. لذلك أعد إنشاءهم على الخادم الجديد باستخدام CREATE USER وGRANT. يوفّر MariaDB 11 الأداة نفسها باسم mariadb-dump، ويحافظ على mysqldump كرابط رمزي، ولذلك يعمل الاسمان حتى August 2026.

SQLite عبارة عن ملف واحد، ونسخه أثناء كتابة التطبيق فيه ينتج ملفاً غير مكتمل. وله مسار آمن خاص به:

# old server
sqlite3 /var/lib/app/app.db ".backup '/var/backups/app.db'"

بغض النظر عن المحرك، افحص التفريغ قبل أن تثق به. التفريغ الذي توقف مبكراً بسبب امتلاء القرص سيُستعاد من دون شكوى، حتى يصل إلى الموضع الذي انقطع عنده.

# old server: a complete mysqldump ends with a line reading "-- Dump completed on ..."
tail -n 3 /var/backups/appdb.sql

# new server: after restore, count rows in a table whose size you know
sudo mysql -e 'SELECT COUNT(*) FROM appdb.orders;'

لماذا لا يمكنك استخدام rsync مع قاعدة بيانات قيد التشغيل

ينسخ rsync الملفات واحداً تلو الآخر. تكتب قاعدة البيانات قيد التشغيل في عدة ملفات في الوقت نفسه، لذلك يكون الملف الأول قد أصبح قديماً بالفعل عندما يصل rsync إلى الملف الأخير. تحتوي النسخة على صفحات من لحظات مختلفة، وهي حالة لم تكن قاعدة البيانات فيها موجودة قط. تكون النتيجة إما خادماً يرفض بدء التشغيل، أو في أسوأ الحالات خادماً يبدأ التشغيل ويقدّم إجابات صحيحة لمدة أسبوع، ثم يتعطل عندما يصل استعلام أخيراً إلى الصفحة التالفة. لا يظهر أي تحذير قبل ذلك.

توجد طريقتان آمنتان لنقل الملفات نفسها. أوقف قاعدة البيانات، وانسخ الملفات، ثم شغّلها مجدداً. هذه الطريقة صحيحة وبسيطة، لكنها تسبب توقفاً يساوي مدة النسخ. أو استخدم الأداة المصممة لإنشاء نسخة فعلية من خادم قيد التشغيل. بالنسبة إلى PostgreSQL، هذه الأداة هي pg_basebackup، وهي تنسّق العملية مع الخادم بحيث تكون النسخة متسقة:

# new server: pull a physical copy from the old one
pg_basebackup -h 198.51.100.10 -U replicator -D /var/lib/postgresql/16/main -X stream -P

تحتاج هذه الطريقة إلى role يحمل السمة REPLICATION وإلى إدخال pg_hba.conf مطابق في الخادم القديم، ولذلك يتطلب إعدادها جهداً أكبر من إعداد dump. تكون مفيدة عندما تكون قاعدة البيانات كبيرة بما يكفي بحيث لا تتسع عملية dump وrestore ضمن نافذة الصيانة. أما في عملية ترحيل عادية لخادم واحد، فاختيار dump أفضل.

أعد إصدار الشهادات قبل التحويل، وليس بعده

ترتبط شهادة TLS باسم النطاق، لا بعنوان IP، لذلك ينتقل ملف الشهادة نفسه دون مشكلة. لكن التجديد لا ينتقل بسلاسة. يطلب تحدي HTTP-01 الافتراضي في Certbot من هيئة إصدار الشهادات جلب ملف عبر المنفذ 80 من الاسم الذي تُصدر له الشهادة. وإلى أن يشير DNS إلى الخادم الجديد، يصل هذا الطلب إلى الخادم القديم، ويفشل تجديد الشهادة على الخادم الجديد.

الخيار الأول هو نسخ الشهادات الحالية وحالة التجديد الخاصة بها. تظل الشهادات صالحة حتى تاريخ انتهاء صلاحيتها، بغض النظر عن الخادم الذي يحتفظ بها.

# old server
sudo rsync -aHAX -e 'ssh -i /root/.ssh/id_ed25519' \
  /etc/letsencrypt/ root@203.0.113.20:/etc/letsencrypt/

يحدّد كل ملف ضمن /etc/letsencrypt/renewal/ إضافة المصادقة التي أصدرت الشهادة. لذلك ثبّت الإضافة نفسها على الخادم الجديد، مثل python3-certbot-nginx، وإلا فسيفشل التجديد الأول برسالة تفيد بوجود إضافة مصادقة غير معروفة. تحقّق من نجاح التجديد قبل الاعتماد عليه:

# new server, after DNS has moved
sudo certbot renew --dry-run

الخيار الثاني هو إصدار شهادة جديدة على الخادم الجديد باستخدام تحدي DNS-01، الذي يثبت التحكم بالنطاق عبر سجل TXT ولا يتصل بالمنفذ 80 مطلقاً. ينجح ذلك قبل الترحيل، ما دام الاسم لا يزال يحل إلى الخادم القديم. لذلك فهو الخيار الأنسب إذا أمكنك أتمتة مزود DNS لديك. يشرح إصدار الشهادات باستخدام تحدي DNS-01 إعداد الإضافة وبيانات الاعتماد.

في كلتا الحالتين، تحقّق مما يقدّمه الخادم الجديد فعلياً، من دون تغيير DNS:

# your laptop
echo | openssl s_client -connect 203.0.113.20:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -subject -dates -issuer

يرسل -servername قيمة SNI (مؤشر اسم الخادم)، وهي التي تجعل خادم الويب يختار المضيف الافتراضي الصحيح. إذا حذفتها، فستحصل على الشهادة الافتراضية لعنوان IP، وعلى عدم تطابق يبدو كمشكلة حقيقية لكنه ليس كذلك.

خفّض مدة DNS TTL قبل يوم الترحيل

قد يفشل DNS في ترحيل دقيق التخطيط، لأن التأخير مضمّن فيه ولا يمكنك تقصيره في يوم الترحيل. يستمر محلّل DNS الذي خزّن سجل A لديك في تقديمه طوال مدة TTL (مدة البقاء) التي تلقّاها. لا يفيد خفض TTL الآن مع محلّل خزّن السجل قبل عشر دقائق بالقيمة القديمة؛ إذ يحتفظ بالقيمة القديمة طوال ما تبقى من مدة TTL القديمة، ثم يتعلّم القيمة الجديدة الأقصر. لذلك خفّض TTL قبل الترحيل بمدة لا تقل عن دورة TTL قديمة كاملة. ويُعدّ الخفض قبل يوم خياراً مريحاً. إذا كانت مكوّنات هذه العملية جديدة عليك، فراجع شرح السجلات ومحلّلات DNS والتخزين المؤقت للتعرّف إلى الخلفية.

الأرقام أدناه ناتجة عن حسابات من قيمة TTL نفسها، وليست قياساً.

ChartHow long a stale IP can survive, by record TTL
The data behind this chart
[
  {
    "label": "TTL 3600 s (common default)",
    "ttl_seconds": 3600,
    "worst_case_stale_minutes": 60
  },
  {
    "label": "TTL 900 s",
    "ttl_seconds": 900,
    "worst_case_stale_minutes": 15
  },
  {
    "label": "TTL 300 s (lowered for cutover)",
    "ttl_seconds": 300,
    "worst_case_stale_minutes": 5
  },
  {
    "label": "TTL 60 s (short window)",
    "ttl_seconds": 60,
    "worst_case_stale_minutes": 1
  }
]

يمكن لسجل منشور بقيمة TTL تبلغ 3600 ثانية أن يستمر في توجيه المستخدمين إلى عنوان IP القديم لمدة 60 دقيقة بعد تغييره. خفّض القيمة إلى 300 ثانية، فتنخفض أسوأ مدة إلى 5 دقيقة. تعامل مع هذه الأرقام باعتبارها حداً أدنى لا وعداً. تفرض بعض محلّلات DNS مدة TTL دنيا خاصة بها وتتجاهل أي قيمة أقصر، كما تخزّن بعض أُطر تشغيل التطبيقات العنوان الذي جرى حلّه طوال مدة تشغيل العملية؛ لذلك قد لا يعيد العميل الذي بدأ قبل التغيير إجراء الاستعلام حتى يُعاد تشغيله.

اقرأ الإجابة المعتمدة، لا نسختك المخزّنة مؤقتاً، عند التحقق من سريان TTL الأقصر:

# your laptop: ask the zone's own nameserver, so no cache is involved
dig +short NS example.com
dig +noall +answer @$(dig +short NS example.com | head -n1) example.com A

الحقل الثاني في سطر الإجابة هو قيمة TTL بالثواني. ثم افحص السجلات التي ينساها الناس: سجل AAAA إذا كان الخادم القديم يستخدم IPv6، واسم www عندما يكون سجل A مستقلاً وليس CNAME، وأي سجل MX يشير إلى الخادم نفسه، وسجل SPF الذي يدرج عنوان IP القديم، وسجل DNS العكسي (PTR) على العنوان الجديد. اضبط PTR من لوحة تحكم مزوّد الخدمة قبل الترحيل إذا كان الخادم يرسل البريد، لأن خوادم البريد المستقبِلة تتحقق منه، ويؤدي غياب PTR إلى رفض البريد بعد ساعات من ظهور كل شيء آخر بصورة سليمة.

تحقّق من الخادم الجديد باستخدام عنوان IP قبل تعديل DNS

يمكنك اختبار التطبيق بالكامل على الخادم الجديد بينما لا يزال DNS يشير إلى الخادم القديم. تجاوز البحث عن الاسم لطلب واحد:

# your laptop: force one name to the new IP, for this request only
curl -sS --resolve example.com:443:203.0.113.20 \
  -o /dev/null -w '%{http_code} %{ssl_verify_result}\n' https://example.com/

يغيّر --resolve وجهة الاتصال فقط. يظل التحقق من شهادة TLS مرتبطاً بالاسم الحقيقي، لذلك يثبت هذا الأمر صحة الشهادة والخدمة معاً. يطبع %{ssl_verify_result} القيمة 0 عند التحقق من السلسلة بنجاح.

لاختبار الموقع بالنقر عليه في متصفح، تجاوز الاسم على كامل جهازك بإضافة سطر واحد إلى /etc/hosts على الحاسوب المحمول، أو إلى C:\Windows\System32\drivers\etc\hosts في Windows:

203.0.113.20 example.com www.example.com

ثم استخدم التطبيق بالطريقة التي يستخدمه بها المستخدم. سجّل الدخول. حمّل صفحة تقرأ البيانات من قاعدة البيانات. أرسل نموذجاً يكتب فيها. ارفع ملفاً وتأكد من وصوله إلى القرص. شغّل الوظيفة التي ترسل البريد الإلكتروني، وتحقق من وصوله، لأن إرسال SMTP الصادر من عنوان IP جديد يسبب مفاجآت شائعة. احذف سطر hosts فور الانتهاء. إبقاؤه موجوداً هو ما يجعلك تقضي ساعة في تصحيح أخطاء موقع يراه جميع المستخدمين الآخرين ويعمل لديهم بصورة طبيعية.

التبديل، خطوة بخطوة

  1. قبل التبديل بعدة أيام: خفّض قيمة TTL، ونفّذ عملية rsync المجمّعة، وأعد بناء الخادم الجديد، واختبره خلف تجاوز إعدادات hosts.
  2. في يوم التبديل، وقبل بدء النافذة: أضف عنوان IP الجديد إلى كل قائمة سماح لدى جهات خارجية، وتأكد من إعداد مهمة النسخ الاحتياطي للخادم الجديد وتوجيهها إلى مستودعك.
  3. عند بدء النافذة: فعّل وضع الصيانة للتطبيق على الخادم القديم، لكي يتوقف عن قبول عمليات الكتابة.
  4. أنشئ النسخة النهائية من قاعدة البيانات، ثم نفّذ تمريرة rsync النهائية باستخدام --delete.
  5. استعد النسخة على الخادم الجديد، ثم شغّل الخدمات.
  6. اختبر مرة أخرى عبر --resolve وتجاوز إعدادات hosts، بما في ذلك تنفيذ عملية كتابة حقيقية واحدة.
  7. غيّر سجلات A وAAAA إلى عنوان IP الجديد.
  8. راقب الخادمين. يوضح سجل الوصول في الخادم القديم من لا يزال يصل إليه، وينبغي أن ينخفض العدد تدريجياً نحو الصفر خلال مدة TTL.
  9. أوقف عرض صفحة الصيانة.
  10. اترك الخادم القديم قيد التشغيل ومن دون تعديل لمدة أسبوع واحد على الأقل.

خطوة وضع الصيانة هي الخطوة التي يتجاوزها الناس غالباً، وهي التي تحميك. بعد أن تقبل قاعدة البيانات الجديدة عملية كتابة، يعني التراجع إما فقدان تلك الكتابة أو تفريغ قاعدة البيانات الجديدة وتحميلها مرة أخرى إلى الخادم القديم. إن نافذة للقراءة فقط مدتها بضع دقائق تكلفة بسيطة. أما وجود قاعدتي بيانات قبلتا عمليات كتابة معاً فيعني قضاء أيام في المطابقة اليدوية.

خطة التراجع

التراجع إجراء واحد: أعد سجلات DNS إلى 198.51.100.10. ينجح ذلك فقط بفضل أربعة أمور نفّذتها سابقاً.

  • لا يزال الخادم القديم قيد التشغيل، ولا تزال خدماته تعمل وبياناته سليمة. أوقفت عمليات الكتابة عليه، ولم تُخرجه من الخدمة.
  • لا تزال قيمة TTL منخفضة، لذلك يكون مسار العودة سريعاً بقدر سرعة الانتقال إلى الأمام.
  • أضفت عنوان IP الجديد إلى قوائم السماح لدى الجهات الخارجية بدلاً من استبدال العنوان القديم. إذا أزلت العنوان القديم، فسيفشل مسار التراجع عند بوابة الدفع.
  • لم ينفّذ الخادم الجديد أي عمليات كتابة لا يمكنك تحديدها، لأن عمليات الكتابة الوحيدة حتى الآن كانت معاملات الاختبار التي نفّذتها بنفسك.

حدّد قبل بدء نافذة التغيير ما الذي يفعّل التراجع. يكفي شرطان: أي خطأ لا يمكنك تشخيصه خلال عدد محدد مسبقاً من الدقائق، وأي فقدان للبيانات مهما كان حجمه. إن تدوين هذه الشروط مسبقاً يمنع ساعة التخمين التي تحوّل انقطاعاً مدته عشر دقائق إلى انقطاع طويل.

إثبات نجاح الترحيل

لا ينتهي الترحيل عند تحميل الموقع. افحص الأمور التي لا تفشل إلا لاحقاً.

# new server
systemctl --failed
journalctl -p err -b --no-pager | tail -n 40
sudo certbot certificates
sudo systemctl list-timers --all --no-pager

يجب أن يعرض systemctl --failed النتيجة التي تريدها عند إعداد التقارير عبر 0 loaded units listed. يجب أن يعرض certbot certificates تواريخ انتهاء الصلاحية المتوقعة، ويجب أن يعرض list-timers كل مهمة مجدولة في قائمتك، مع وقت تشغيل تالٍ فعلي وليس حقلاً فارغاً.

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

# new server
sudo reboot

# your laptop, once it comes back
curl -sS -o /dev/null -w '%{http_code}\n' https://example.com/

إذا كان التطبيق يعمل داخل حاويات، يظهر الخطأ نفسه بصورة مختلفة، لأن حزمة compose تحتاج إلى سياسة إعادة تشغيل صريحة لكي تعود بعد إعادة التشغيل.

الفحص الأخير هو الأسهل تأجيلاً والأهم: مهمة النسخ الاحتياطي. الترحيل الذي ينتهي بخادم لا توجد له نسخة احتياطية يستبدل خطراً بآخر. شغّل النسخ الاحتياطي على الخادم الجديد يدوياً، ثم استعد ملفاً واحداً منه إلى دليل مؤقت. مستودع restic أجريت اختباراً فعلياً لاستعادته هو ما تحتاج إليه عند الضرورة. إذا شغّلت الخادم القديم والجديد جنباً إلى جنب لمدة أسبوع، فإن طريقة متسقة للوصول إلى كل مضيف وتهيئته تمنع اختلاف إعداداتهما أثناء عملهما معاً.

بعد التحويل: الخادم القديم والعناصر الأخيرة

احتفظ بالخادم القديم لمدة أسبوع إلى أسبوعين. ستدفع تكلفة شهر واحد من خطة كنت تستعد لإلغائها، لكنه يمثل خيار التراجع الوحيد المتاح لديك. ثم أنهِ بقية المهام.

  • يؤدي استخدام اسم المضيف نفسه في ~/.ssh/config للوصول إلى الخادم الجديد إلى ظهور WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! عند الاتصال الأول، لأن هذا الاسم يشير الآن إلى مفتاح مضيف مختلف. أزِل الإدخال القديم باستخدام ssh-keygen -R example.com بعد أن تتأكد من سبب تغيّره، ولا تفعل ذلك تلقائياً، لأن التحذير نفسه قد يدل على هجوم اعتراض. تُعدّ عملية الترحيل أيضاً وقتاً مناسباً لمراجعة المفاتيح التي يمكنها الوصول إلى الموارد، وهذا هو الغرض من إدارة مفاتيح SSH على مجموعة خوادم صغيرة.
  • أنشئ لقطة أو نسخة احتياطية نهائية للخادم القديم، وخزّنها في مكان لا يتبع المزوّد القديم.
  • أزِل عنوان IP القديم من فحوصات المراقبة، ومن سجلات SPF، ومن قوائم السماح لدى الجهات الخارجية، بهذا الترتيب وبعد الانتهاء من كل شيء.
  • ألغِ الخطة القديمة فقط بعد التأكد من إمكانية قراءة النسخة النهائية في مكان آخر.

FAQ

كم يستغرق ترحيل خادم إلى VPS جديد؟

يقتصر الانقطاع الذي يلاحظه المستخدم عادةً على تفريغ قاعدة البيانات النهائي، وتمرير rsync النهائي، وبدء تشغيل الخدمة، ولذلك يستغرق من عشر إلى ثلاثين دقيقة في حالة تطبيق صغير. لكن المدة التقويمية أطول، لأن TTL الخاص بـDNS يجب خفضه قبل التبديل بمدة لا تقل عن فترة TTL القديمة، ويكون خفضه قبل يوم أكثر أماناً. خطط أيضاً لبدء نسخ البيانات الأساسية قبل ذلك بعدة أيام. يجري هذا النسخ والخادم قيد التشغيل، وإعادته لاحقاً تنقل فقط البيانات التي تغيرت منذ التمرير السابق.

هل يمكنني استخدام rsync لنسخ قاعدة بيانات MySQL أو PostgreSQL قيد التشغيل بدلاً من تفريغها؟

لا. ينسخ rsync الملفات واحداً تلو الآخر، بينما تكتب قاعدة البيانات في عدة ملفات في الوقت نفسه. لذلك قد تتضمن النسخة صفحات من لحظات مختلفة، وتمثل حالة لم تكن موجودة في قاعدة البيانات قط. قد ترفض قاعدة البيانات البدء، أو تبدأ ثم تفشل لاحقاً عندما يصل استعلام إلى صفحة تالفة. استخدم pg_dump مع pg_dumpall --globals-only، أو mysqldump --single-transaction، أو أوقف قاعدة البيانات أولاً ثم انسخ الملفات. بالنسبة إلى عنقود PostgreSQL كبير، ينشئ pg_basebackup نسخة مادية متسقة من خادم قيد التشغيل.

كيف أختبر VPS الجديد قبل تغيير DNS؟

تجاوز البحث عن الاسم على جهازك. لطلب واحد، يرسل curl --resolve example.com:443:203.0.113.20 https://example.com/ الاتصال إلى عنوان IP الجديد، مع الاستمرار في التحقق من الشهادة مقابل الاسم الحقيقي. لاختبار المتصفح، أضف 203.0.113.20 example.com إلى /etc/hosts على حاسوبك المحمول، ثم اختبر تسجيل الدخول، وقراءة من قاعدة البيانات، وإرسال نموذج، ورفع ملف، وبعد ذلك أزل السطر. لفحص الشهادة وحدها، شغّل openssl s_client -connect 203.0.113.20:443 -servername example.com.

ما قيمة TTL التي ينبغي ضبطها، ومتى ينبغي خفضها؟

اخفض سجلي A وAAAA إلى 300 ثانية، ونفّذ ذلك قبل التبديل بمدة لا تقل عن فترة TTL القديمة كاملة. يحتفظ محلل أسماء خزن السجل قبل تغييرك بالقيمة القديمة طوال ما تبقى من TTL القديمة، لذلك لا يحقق خفضها قبل ساعة أي فائدة إذا كانت TTL القديمة هي 86400. أعدها إلى قيمتها المعتادة بعد بضعة أيام من الترحيل، بعد أن يهدأ سجل الوصول إلى الخادم القديم.

هل ينبغي نسخ شهادة TLS أم إصدار شهادة جديدة على الخادم الجديد؟

كلا الخيارين صالح. يحافظ نسخ /etc/letsencrypt/ على صلاحية الشهادة حتى تاريخ انتهائها الحالي، لكن يجب تثبيت إضافة المصادقة نفسها الخاصة بـcertbot على الخادم الجديد، وإلا ستفشل عملية التجديد الأولى. لذلك شغّل certbot renew --dry-run بعد تبديل DNS للتأكد. يكون إصدار شهادة جديدة أنظف عندما يمكنك استخدام تحدي DNS-01، لأنه يثبت التحكم عبر سجل TXT ويعمل قبل أن يشير DNS إلى الخادم الجديد. لا يمكن استخدام تحدي HTTP-01 على الخادم الجديد قبل انتقال DNS، لأن طلب التحقق سيصل إلى الخادم القديم.