إصدار تغييرات الواجهة الأمامية دون كسر كل شيء
لقد قمت للتو بدفع إصدار جديد من الواجهة الأمامية. نجح البناء، واجتازت الاختبارات، ويقول خط أنابيب النشر "نجاح". ولكن عندما تتفقد مقاييس الإنتاج، هناك شيء غير صحيح. بعض المستخدمين يرون تخطيطًا مكسورًا. آخرون يبلغون أن زرًا لا يعمل. وقليل من المستخدمين لا يزالون على الإصدار القديم على أي حال، لأن متصفحهم لم يحصل على الملفات الجديدة بعد.
هذا هو واقع إصدارات الواجهة الأمامية. على عكس خدمات الواجهة الخلفية حيث يمكنك إعادة تشغيل خادم وتبديل الإصدارات فورًا، فإن كود الواجهة الأمامية يعيش في متصفح المستخدم. لا يمكنك إجبار كل مستخدم على إعادة التحميل. لا يمكنك التحكم في موعد انتهاء صلاحية ذاكرة التخزين المؤقت الخاصة به. وإذا حدث خطأ ما، لا يمكنك فقط الضغط على زر الاسترجاع وتتوقع أن يحصل الجميع على الإصلاح فورًا.
لماذا تختلف إصدارات الواجهة الأمامية
المشكلة الأساسية هي أن أصول الواجهة الأمامية موزعة عبر آلاف المتصفحات، كل منها بحالة ذاكرة تخزين مؤقت خاصة به. عند نشر إصدار جديد، يحصل عليه بعض المستخدمين فورًا، والبعض الآخر بعد انتهاء صلاحية ذاكرة التخزين المؤقت، وقد يعلق البعض بإصدار معطل حتى يقوموا بالتحديث يدويًا.
بالنسبة للواجهات الأمامية الثابتة التي يتم تقديمها عبر شبكة توصيل المحتوى (CDN)، يكمن التحدي في أنك لا تنشر على خادم تتحكم فيه. أنت تقوم برفع الملفات إلى حاوية تخزين وتترك شبكة CDN توزعها. قد تقدم شبكة CDN ملفات قديمة لبعض المستخدمين وملفات جديدة لآخرين، اعتمادًا على رؤوس التخزين المؤقت وسلوك العقد الطرفية.
بالنسبة للواجهات الأمامية المقدمة من جانب الخادم (SSR)، التحدي مختلف. أنت تتعامل مع خوادم فعلية تحتاج إلى التحديث دون قطع الاتصالات النشطة. المستخدم الذي يملأ نموذجًا أو يتنقل بين الصفحات يجب ألا يفقد جلسته بسبب تبديل الإصدارات.
يساعدك مخطط التدفق التالي في اختيار استراتيجية الإصدار المناسبة بناءً على نوع الواجهة الأمامية ومدى تحملك للمخاطر:
الطرح التدريجي للواجهات الأمامية الثابتة
أكثر طريقة آمنة لإصدار واجهة أمامية ثابتة هي التحكم في عدد المستخدمين الذين يرون الإصدار الجديد. تدعم معظم شبكات CDN التوزيع الموزون، حيث يمكنك إرسال نسبة مئوية من الطلبات إلى الملفات الجديدة والباقي إلى القديمة.
إليك كيف يعمل هذا عادةً:
- رفع الإصدار الجديد إلى حاوية التخزين الخاصة بك بمسار فريد، مثل
app-v2/أو باستخدام أسماء ملفات تحتوي على تجزئة المحتوى. - تكوين شبكة CDN الخاصة بك لتوجيه 5% من الطلبات إلى الملفات الجديدة.
- مراقبة معدلات الأخطاء، وأوقات تحميل الصفحات، والمشكلات التي يبلغ عنها المستخدمون.
- إذا كان كل شيء يبدو جيدًا، قم بزيادة النسبة تدريجيًا إلى 25%، ثم 50%، ثم 100%.
يمكنك أيضًا استخدام أعلام الميزات (Feature Flags) على جانب العميل. قم بتقديم نفس غلاف التطبيق للجميع، ولكن قم بتحميل المكونات أو الميزات الجديدة بشكل مشروط بناءً على ملف تعريف ارتباط، أو معلمة URL، أو معرف مستخدم. يمنحك هذا تحكمًا أدق في من يرى ماذا، دون الحاجة إلى تقسيم حركة المرور على مستوى CDN.
الميزة الرئيسية للطرح التدريجي هي أنه يمكنك التوقف في أي نقطة. إذا ارتفعت معدلات الأخطاء عند 10%، قم بخفض النسبة إلى الصفر. قد لا يزال المستخدمون الذين قاموا بتحميل الإصدار الجديد يرون مشكلات، لكنك حددت نطاق الضرر.
الإصدارات التجريبية (Canary) للواجهات الأمامية SSR
بالنسبة للواجهات الأمامية SSR، يعمل الطرح التدريجي مثل عمليات النشر التجريبية للواجهة الخلفية. تقوم بتشغيل نسخ متعددة من تطبيقك خلف موازن تحميل. عندما تريد إصدار إصدار جديد، تقوم بنشره على نسخة أو اثنتين بينما تستمر البقية في تشغيل الإصدار القديم.
يتم تكوين موازن التحميل لإرسال نسبة صغيرة من حركة المرور إلى النسخ الجديدة. إذا فشلت فحوصات الصحة أو زادت معدلات الأخطاء، تتم إزالة تلك النسخ من التوزيع. الاسترجاع بسيط مثل إيقاف تشغيل النسخ الجديدة والسماح لجميع حركة المرور بالعودة إلى القديمة.
يعمل هذا النهج بشكل جيد لأن تطبيقات SSR تحافظ على الحالة على جانب الخادم. جلسة المستخدم مرتبطة بنسخة معينة، لذلك تحتاج إلى التأكد من تخزين بيانات الجلسة في موقع مشترك مثل Redis أو قاعدة بيانات. وإلا، فإن تبديل حركة المرور بين الإصدارات سيؤدي إلى تسجيل خروج المستخدمين.
لأتمتة هذه العملية، يمكنك تعريف طرح تجريبي في تكوين النشر الخاص بك. إليك مثال باستخدام Argo Rollouts على Kubernetes:
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: frontend-ssr
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: { duration: 5m }
- setWeight: 50
- pause: { duration: 5m }
- setWeight: 100
analysis:
templates:
- templateName: success-rate
startingStep: 0
args:
- name: service-name
value: frontend-ssr
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
metrics:
- name: error-rate
successCondition: result < 0.01
provider:
prometheus:
query: |
sum(rate(http_requests_total{status=~"5.."}[5m])) /
sum(rate(http_requests_total[5m]))
يقوم هذا التكوين بتوجيه 10% من حركة المرور إلى الإصدار الجديد، وينتظر 5 دقائق لمراقبة معدلات الأخطاء، ثم يتقدم إلى 50% وأخيرًا 100% فقط إذا ظل معدل الخطأ أقل من 1%.
النشر الأزرق-الأخضر (Blue-Green) لعدم التوقف عن العمل
إذا كنت بحاجة إلى ضمان عدم تعرض أي مستخدم للتوقف أثناء الإصدار، فإن النشر الأزرق-الأخضر هو خيار قوي. تحتفظ ببيئتين متطابقتين: زرقاء (الإصدار الحالي) وخضراء (الإصدار الجديد). تذهب كل حركة المرور إلى الزرقاء. بمجرد أن تصبح الخضراء جاهزة وتجتاز جميع فحوصات الصحة، تقوم بتبديل موازن التحميل لإرسال كل حركة المرور إلى الخضراء.
إذا حدث خطأ ما بعد التبديل، تقوم بالعودة إلى الزرقاء. تستغرق العملية بأكملها ثوانٍ، ولا يرى المستخدمون أي انقطاع طالما تم التعامل مع حالة الجلسة بشكل صحيح.
العيب هو أنك تحتاج إلى ضعف البنية التحتية. بالنسبة للفرق الصغيرة أو التطبيقات منخفضة الحركة، قد يكون هذا مبالغًا فيه. ولكن بالنسبة لأنظمة الإنتاج عالية الحركة حيث حتى بضع ثوانٍ من التوقف تكلف المال أو الثقة، فإن النشر الأزرق-الأخضر يستحق التكلفة الإضافية.
التحدي الحقيقي: استرجاع الواجهات الأمامية الثابتة
هنا تصبح الأمور صعبة. استرجاع واجهة أمامية ثابتة بسيط من الناحية الفنية ولكنه فوضوي عمليًا. يمكنك إرجاع الملفات في شبكة CDN الخاصة بك إلى الإصدار القديم، ولكن المستخدمين الذين قاموا بالفعل بتنزيل الملفات الجديدة لن يحصلوا تلقائيًا على القديمة. لا تزال ذاكرة التخزين المؤقت للمتصفح لديهم تحتوي على الإصدار الجديد، وستستمر في تقديم ذلك حتى تنتهي صلاحية ذاكرة التخزين المؤقت.
الحل هو عدم استبدال الملفات أبدًا. يجب أن يكون لكل إصدار مسار فريد، يعتمد عادةً على تجزئة المحتوى. عند النشر، تقوم برفع ملفات جديدة بجانب القديمة. تقدم شبكة CDN الإصدار الذي يشير إليه التطبيق. للاسترجاع، تقوم بتغيير المؤشر من الإصدار الجديد إلى القديم. سيستمر المستخدمون الذين لديهم الإصدار الجديد في ذاكرة التخزين المؤقت في استخدامه حتى يتم مسح ذاكرة التخزين المؤقت، لكن المستخدمين الجدد والمستخدمين الذين يقومون بالتحديث سيحصلون على الإصدار القديم.
يعني هذا النهج أنك بحاجة إلى التفكير في الاسترجاع قبل أن تحتاج إليه أبدًا. إذا كان خط أنابيبك مصممًا لاستبدال الملفات في مكانها، فأنت تهيئ نفسك لاسترداد مؤلم عندما يحدث خطأ ما.
قائمة التحقق العملية لإصدارات الواجهة الأمامية
قبل دفع الإصدار التالي، راجع قائمة التحقق السريعة هذه:
- هل جميع مسارات الملفات فريدة لكل إصدار (باستخدام تجزئة المحتوى أو الإصدارات)؟
- هل يمكنك توجيه نسبة صغيرة من حركة المرور إلى الإصدار الجديد؟
- هل لديك طريقة لمراقبة معدلات الأخطاء وأوقات تحميل الصفحات في الوقت الفعلي؟
- هل خطة الاسترجاع الخاصة بك تم اختبارها، وليس مجرد توثيق؟
- بالنسبة لـ SSR: هل حالة الجلسة مخزنة خارج خادم التطبيق؟
- بالنسبة للثابتة: هل لديك آلية لتبديل مؤشر الإصدار دون إعادة النشر؟
ما هو الأكثر أهمية
الطرح التدريجي واستراتيجيات الاسترجاع ليست ميزات يمكنك إضافتها بعد بناء خط الأنابيب. يجب تصميمها في عملية النشر من البداية. إذا كان خط الأنابيب الخاص بك يعرف فقط كيفية نشر إصدار واحد في موقع واحد، فستواجه صعوبة في القيام بالإصدارات التجريبية أو النشر الأزرق-الأخضر. إذا لم يكن لديك إدارة حركة المرور على مستوى CDN أو موازن التحميل، فليس لديك طريقة للحد من تأثير الإصدار السيئ.
الهدف ليس منع كل المشكلات. ستحدث المشكلات. الهدف هو التأكد من أنه عند حدوث مشكلة، يمكنك الاستجابة دون ذعر. تمنحك استراتيجية الإصدار المصممة جيدًا التحكم في نطاق الضرر، ومسار استرجاع واضح، والثقة لشحن التغييرات بشكل متكرر. بدونها، كل إصدار يبدو وكأنه مقامرة.