اثنا عشر بناءً

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

نسخ مسبق، ونسخة متماثلة متدفقة، وتحويل وكيل، مع ضبط TCP الذي يحدد ما إذا كان النقل لمسافات طويلة يستغرق ساعة أم يومًا.

ما الذي يبنيه هذا

نقل تيرابايتين من الملفات وقاعدة بيانات Postgres الحية من موقع إلى آخر، مع استمرار التطبيق في الاستجابة للطلبات طوال الوقت. الطريقة هي نسخ مسبق شامل بينما يستمر المصدر في الخدمة، ونسخة متماثلة بثية تبقى متزامنة، وقطع تحويل يقاس بالثواني مع قيام المضيف القديم بالوكالة عن الجديد حتى يلحق DNS.

المسافة هي الجزء المثير للاهتمام. بين AMS-01 وFRA-01، زمن الذهاب والإياب هو سبع ميلي ثانية، ودفق واحد سيملأ المنفذ. قم بنفس النسخ إلى GRU-01، حيث زمن الذهاب والإياب يقترب من مئتين، وسيتحرك النقل غير المضبوط بجزء صغير مما تدفع مقابله بينما تكون الآلتان شبه خاملتين. تلك الفجوة تعود بالكامل إلى كمية البيانات المسموح بها في الطيران لاتصال واحد.

قبل أن تبدأ

  • عيّنات المصدر والهدف، ونفق بينهما، وصلاحية root على كلاهما.
  • DNS تتحكم فيه، مع خفض TTL للسجل إلى ستين ثانية قبل يوم على الأقل. هذه هي الخطوة التي يتجاهلها الناس ثم ينتظرون ست ساعات.
  • مساحة كافية على الهدف للبيانات كاملة بالإضافة إلى النسخة المتماثلة.

1. احسب ما يمكن للرابط تقديمه

حاصل ضرب التأخير في عرض النطاق (bandwidth-delay product) يحدد كل شيء. اضرب زمن الذهاب والإياب في المعدل الذي تريده وستحصل على كمية البيانات التي يجب أن تكون غير مؤكدة في الطيران في أي لحظة:

ping -c20 target.example.com | tail -1
المسارRTTفي الطيران لـ 1 Gbit/sنافذة kernel الافتراضية
AMS-01 إلى FRA-017 ms0.9 MBكافية
AMS-01 إلى NYC-0176 ms9.5 MBغير كافية
AMS-01 إلى SIN-01168 ms21 MBغير قريبة
AMS-01 إلى GRU-01196 ms24.5 MBغير قريبة

على كلا الجهازين:

cat > /etc/sysctl.d/75-transfer.conf <<EOF
net.core.rmem_max = 134217728
net.core.wmem_max = 134217728
net.ipv4.tcp_rmem = 4096 262144 134217728
net.ipv4.tcp_wmem = 4096 262144 134217728
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
net.ipv4.tcp_mtu_probing = 1
EOF
sysctl --system

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

قم بالقياس قبل أن تلتزم بتيرابايتين لافتراض:

# target
apt install -y iperf3 && iperf3 -s
# source
iperf3 -c target.example.com -P 8 -t 30

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

2. النسخ المسبق الشامل

قم بتشغيل هذا ضد مصدر حي. سيستغرق ساعات، ولا شيء ينقطع أثناء ذلك:

apt install -y rsync
rsync -aHAX --numeric-ids --partial --inplace --info=progress2 \
  -e "ssh -c [email protected] -o Compression=no" \
  /srv/data/ [email protected]:/srv/data/

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

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

ls /srv/data | xargs -P 8 -I{} rsync -aHAX --numeric-ids \
  -e "ssh -c [email protected] -o Compression=no" \
  /srv/data/{}/ [email protected]:/srv/data/{}/

ثمانية نسخ متوازية على ثمانية أنوية مخصصة هو الرقم الصحيح هنا؛ ستة عشر ليست ضعف السرعة وستجعل آلة المصدر غير مريحة لأي شيء لا تزال تقدمه.

3. قاعدة البيانات، متماثلة بدلاً من منسوخة

تفريغ واستعادة مئتي غيغابايت من Postgres يعني نافذة طويلة حيث تكون الكتابات مفقودة. قم ببثها بدلاً من ذلك. على المصدر:

sudo -u postgres psql -c "CREATE USER repl WITH REPLICATION PASSWORD '<a long password>';"
sudo -u postgres psql -c "SELECT pg_create_physical_replication_slot('target_site');"
echo "host replication repl 10.9.0.2/32 scram-sha-256" >> /etc/postgresql/17/main/pg_hba.conf
systemctl reload postgresql

على الهدف:

systemctl stop postgresql
rm -rf /var/lib/postgresql/17/main/*
sudo -u postgres pg_basebackup -h 10.9.0.1 -U repl -D /var/lib/postgresql/17/main \
  -S target_site -X stream -R -P -c fast
systemctl start postgresql

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

4. قطع التحويل

ابدأ مسبارًا من جهاز ثالث واتركه يعمل. هو تحققك وسجلك لما كلفه التبديل:

while true; do curl -s -o /dev/null -w "%{http_code} %{time_total}\n" https://app.example.com/health; sleep 1; done | tee cutover.log

ثم، بهذا الترتيب وبدون توقف:

# 1. final delta, source still live
rsync -aHAX --numeric-ids --delete -e "ssh -c [email protected]" /srv/data/ [email protected]:/srv/data/

# 2. stop writes on the source
systemctl stop app

# 3. one more delta, now tiny
rsync -aHAX --numeric-ids --delete -e "ssh -c [email protected]" /srv/data/ [email protected]:/srv/data/

# 4. promote the replica
ssh [email protected] "sudo -u postgres pg_ctl promote -D /var/lib/postgresql/17/main"

# 5. start the application on the target
ssh [email protected] "systemctl start app"

الخطوات من الثانية إلى الخامسة تستغرق بضع ثوانٍ. الآن أوقف المضيف القديم من تقديم محتوى قديم بينما ينتشر DNS، عن طريق تحويله إلى وكيل للجديد:

server {
  listen 443 ssl;
  server_name app.example.com;
  ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
  location / {
    proxy_pass https://10.9.0.2;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

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

تحقق منه

ابدأ بسجل المسبار الذي كنت تجمعه:

sort cutover.log | uniq -c | sort -rn | head

يجب أن يقرأ كل سطر 200. عدد قليل من الاستجابات الأبطأ أثناء التبديل متوقع؛ أي non-200 يعني أن الخطوة الخامسة بدأت بعد الثانية بفارق كبير جدًا، والوكيل في القسم السابق هو ما يقلص تلك الفجوة إلى لا شيء.

ثم أثبت أن البيانات متطابقة بدلاً من كونها موجودة فحسب:

rsync -aHAXn --delete --itemize-changes /srv/data/ [email protected]:/srv/data/ | head
du -s --bytes /srv/data
ssh [email protected] "du -s --bytes /srv/data"

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

قاعدة البيانات بعد ذلك:

ssh [email protected] "sudo -u postgres psql -c 'SELECT pg_is_in_recovery();'"
ssh [email protected] "sudo -u postgres psql app -c 'SELECT count(*) FROM orders;'"
sudo -u postgres psql app -c "SELECT count(*) FROM orders;"

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

dig +short app.example.com
tail -f /var/log/nginx/access.log | wc -l   # on the old host, an hour later

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

بعد ذلك

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

جاهز عندما تكون

اختر مدينة. اختر الحجم. ادفع بالعملة الرقمية.

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