بقلم كلير ليجوري

عندما أجريت مقابلة من أجل وظيفتي في Amazon، تأكدت من سؤال أحد القائمين على إجراء المقابلة، «كم عدد مرات النشر إلى الإنتاج؟» وكنت أعمل في ذلك الوقت على منتج ينشر إصدارًا رئيسيًا مرة أو مرتين في السنة، لكن في بعض الأحيان كنت في حاجة إلى إصدار إصلاح صغير بين الإصدارات الكبيرة. واستغرقت ساعات في نشر كل إصلاح أصدره بعناية. ثم فحصت السجلات والمقاييس بقدة بالغة لمعرفة ما إذا كنت قد أتلفت أي شيء بعد النشر واحتجت إلى التراجع عنه.

وقرأت أن Amazon اعتادت النشر المستمر، لذا عندما أجريت مقابلة، أردت معرفة مقدار الوقت الذي سأقضيه في إدارة عمليات النشر ومشاهدتها بوصفي مطورًا في Amazon. فأخبرني القائم على إجراء المقابلة أنه يتم نشر التغييرات تلقائيًا إلى الإنتاج عدة مرات في اليوم عن طريق تدفقات النشر المستمر. وعندما سألته عن مقدار الوقت المستغرق من يومه الذي كان يقضيه في رعاية عمليات النشر تلك ومراقبة السجلات والمقاييس لمعرفة حدوث أي تأثير كما كنت أفعل بعناية، أخبرني أنه مقدار لا يُذكَر عادةً. ونظرًا لأن التدفقات أنجزت هذا العمل لصالح فريقه، فإن معظم عمليات النشر لم تتم مراقبتها بفعالية من جانب أي شخص. «يا للروعة!» هكذا كان رد فعلي. بعد أن انضممت إلى Amazon، كنت متحمسًا لاكتشاف طريقة عمل عمليات النشر الآلي هذه "غير اليدوية" بصورة دقيقة.

عمليات نشر متواصلة آمنة في Amazon

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

رحلة التسليم المتواصل

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

مراحل التدفقات الأربع

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

المصدر والبناء

يمنحك الرسم التخطيطي التالي نظرة عامة على خطوات البناء والمصادر التي قد تجدها في تدفقات فريق خدمة AWS النموذجية.

مصادر التدفقات

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

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

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

مراجعة التعليمات البرمجية

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

مع التدفقات المؤتمتة بالكامل، تُعَد مراجعة التعليمات البرمجية هي المراجعة اليدوية الأخيرة وهي بمثابة الموافقة التي يتلقاها تغيير التعليمات البرمجية من مهندس قبل نشره في الإنتاج، لذا فهي خطوة مهمة. إذ يقوم مراجعو التعليمات البرمجية بتقييم صحة التعليمة البرمجية وأيضًا تقييم ما إذا كان بالإمكان نشر التغيير بأمان إلى الإنتاج أو لا. كما يقيِّمون ما إذا كانت التعليمات البرمجية تحتوي على اختبارات كافية (اختبارات الوحدة، واختبارات التكامل، واختبارات Canary) أو لا، وما إذا كانت مجهزة بشكل كافٍ لمراقبة النشر أو لا، وما إذا كان بالإمكان التراجع عنها بأمان أو لا. وتستخدم بعض الفرق قائمة تحقق مخصصة كتلك الموجودة في العينة التالية، والتي تتم إضافتها تلقائيًا إلى كل مراجعة من مراجعات التعليمات البرمجية الخاصة بالفريق للتحقق بشكل صريح من مخاوف سلامة النشر.

مثال على قائمة التحقق الخاصة بمراجعة التعليمات البرمجية

## Testing
[ ] Did you write new unit tests for this change?
[ ] Did you write new integration tests for this change?

Include the test commands you ran locally to test this change:
```
mvn test && mvn verify
```

## Monitoring
[ ] Will this change be covered by our existing monitoring?
 (no new canaries/metrics/dashboards/alarms are required)
[ ] Will this change have no (or positive) effect on resources and/or limits?
 (including CPU, memory, AWS resources, calls to other services)
[ ] Can this change be deployed to Prod without triggering any alarms?

## Rollout
[ ] Can this change be merged immediately into the pipeline upon approval?
[ ] Are all dependent changes already deployed to Prod?
[ ] Can this change be rolled back without any issues after deployment to Prod?

البناء واختبارات الوحدة

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

اختبار النشر في بيئات ما قبل الإنتاج

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

اختبارات التكامل

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

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

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

التوافق مع الإصدارات السابقة واختبار المربع الواحد

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

ويوضح الرسم البياني التالي حالة بيئة جاما بعد نشر تعليمات برمجية جديدة في مرحلة المربع الواحد ولكن لم يتم نشرها بعد في بقية أسطول جاما: 

نحتاج أيضًا إلى التأكد من أن أحدث تعليمة برمجية متوافقة مع تبعياتنا، على سبيل المثال إذا كانت هناك حاجة لإجراء تغيير ما عبر الخدمات الصغيرة بترتيب معين. وعادةً ما تستدعي الخدمات المصغرة في بيئات ما قبل الإنتاج نقطة نهاية الإنتاج لأي خدمات مملوكة لفريق آخر، مثل Amazon Simple Storage Service (S3) أو Amazon DynamoDB، كما تستدعي نقطة نهاية ما قبل الإنتاج للخدمات المصغرة الأخرى لفريق الخدمة في المرحلة نفسها. على سبيل المثال، تستدعي الخدمة المصغرة «أ» لفريق ما في جاما الخدمة المصغرة «ب» للفريق نفسه في جاما، ولكنها تستدعي نقطة نهاية الإنتاج لـ Amazon S3.

وتجري بعض التدفقات أيضًا اختبارات التكامل مرة أخرى في مرحلة منفصلة للتوافق مع إصدارات سابقة نسميها زيتا، وهي بيئة منفصلة حيث تستدعي فيها جميع الخدمات المصغرة نقاط نهاية الإنتاج فقط، وتختبر مدى توافق التغييرات التي يتم إجراؤها على الإنتاج مع التعليمات البرمجية المنشورة حاليًا إلى الإنتاج لعدة خدمات مصغرة. على سبيل المثال، تستدعي الخدمة المصغرة (أ) في زيتا نقطة نهاية إنتاج الخدمة المصغرة (ب) ونقطة نهاية الإنتاج لـ Amazon S3.

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

عمليات نشر الإنتاج

يتمثل هدفنا الأول لعمليات نشر الإنتاج في AWS في منع التأثير السلبي على مناطق متعددة في الوقت نفسه وعلى مناطق توافر خدمات متعددة في المنطقة نفسها. ويؤدي تحديد نطاق كل عملية نشر فردية إلى الحد من التأثير المحتمل على العملاء من جراء عمليات نشر الإنتاج الفاشلة ويمنع تأثير منطقة توافر الخدمات المتعددة أو التأثير على مناطق متعددة. وللحد من نطاق عمليات النشر التلقائي، قسَّمنا مرحلة الإنتاج الخاصة بالتدفق إلى عدة مراحل وعمليات نشر عديدة لمناطق فردية. وقسَّمت الفرق عمليات النشر الإقليمية إلى عمليات نشر ذات نطاق أصغر من خلال النشر إلى مناطق توافر الخدمات الفردية أو إلى الأجزاء الداخلية الفردية للخدمة (تسمى الخلايا) في التدفق، لزيادة الحد من نطاق التأثير المحتمل جراء نشر الإنتاج الفاشل.

عمليات النشر المتدرجة

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

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

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

عمليات نشر المربع الواحدة والمتغيرة

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

بعد مرحلة المربع الواحد، تستخدم معظم الفرق عمليات نشر متجددة للنشر في أسطول الإنتاج الرئيسي للموجة. فيما يضمن النشر المتغير أن الخدمة لديها سعة كافية لخدمة أحمال الإنتاج طوال عملية النشر. ويتحكم في معدل وضع التعليمات البرمجية الجديدة في الخدمة (أي عندما يبدأ في خدمة حركة مرور الإنتاج) للحد من تأثير التغييرات. أما في النشر المتغير النموذجي لإحدى المناطق، فيتم استبدال التعليمات البرمجية الجديدة بنسبة 33 بالمائة على الأكثر من مربعات الخدمة في تلك المنطقة (الحاويات أو استدعاءات Lambda أو البرامج التي تعمل على الأجهزة الافتراضية).

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

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

مراقبة المقاييس والتراجع التلقائي

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

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

مثال على إنذار الخدمة المصغرة عالي الخطورة

ALARM("FrontEndApiService_High_Fault_Rate") OR
ALARM("FrontEndApiService_High_P50_Latency") OR
ALARM("FrontEndApiService_High_P90_Latency") OR
ALARM("FrontEndApiService_High_P99_Latency") OR
ALARM("FrontEndApiService_High_Cpu_Usage") OR
ALARM("FrontEndApiService_High_Memory_Usage") OR
ALARM("FrontEndApiService_High_Disk_Usage") OR
ALARM("FrontEndApiService_High_Errors_In_Logs") OR
ALARM("FrontEndApiService_High_Failing_Health_Checks")

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

مثال على الإنذار عالي الخطورة بالتراجع الإجمالي

ALARM("FrontEndApiService_High_Severity") OR
ALARM("BackendApiService_High_Severity") OR
ALARM("BackendWorkflows_High_Severity") OR
ALARM("Canaries_High_Severity")

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

مثال على إنذار تراجع المربع الواحد

ALARM("High_Severity_Aggregate_Rollback_Alarm") OR
ALARM("FrontEndApiService_OneBox_High_Fault_Rate") OR
ALARM("FrontEndApiService_OneBox_High_P50_Latency") OR
ALARM("FrontEndApiService_OneBox_High_P90_Latency") OR
ALARM("FrontEndApiService_OneBox_High_P99_Latency") OR
ALARM("FrontEndApiService_OneBox_High_Cpu_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Memory_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Disk_Usage") OR
ALARM("FrontEndApiService_OneBox_High_Errors_In_Logs") OR
ALARM("FrontEndApiService_OneBox_Failing_Health_Checks")

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

وقت إعداد الخبز

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

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

فيما ينتظر التدفق النموذجي ساعة واحدة على الأقل بعد كل مرحلة من مراحل المربع الواحد، وما لا يقل عن 12 ساعة بعد الموجة الإقليمية الأولى، وما لا يقل عن ساعتين إلى أربع ساعات بعد كل موجة من الموجات الإقليمية المتبقية، مع وقت إضافي لإعداد الخبز للمناطق الفردية، ومناطق توافر الخدمات والخلايا داخل كل موجة. ويتضمن وقت إعداد الخبز متطلبات لانتظار عدد محدد من نقاط البيانات في مقاييس الفريق (على سبيل المثال، "انتظر ما لا يقل عن 100 طلب لواجهة برمجة تطبيقات الإنشاء") وذلك للتأكد من وجود طلبات كافية تجعل من المحتمل أن تكون التعليمات البرمجية الجديدة تمت ممارستها على نحو كامل. وخلال الوقت الكامل لإعداد الخبز، يتم التراجع عن النشر تلقائيًا إذا انتقل إنذار الفريق عالي الخطورة إلى حالة التنبيه.

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

أدوات حظر الإطار الزمني والإنذار

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

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

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

التدفقات بوصفها تعليمة برمجية

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

الختام

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

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

مزيد من القراءة

لمزيد من المعلومات حول كيفية قيام Amazon بتحسين أمان الخدمات وتوافرها مع زيادة رضا العميل وإنتاجية المطور، راجع الإسراع بالتسليم المتواصل

وللحصول على وصف لإستراتيجيات كتابة التغييرات المتوافقة مع الإصدارات السابقة ونشرها، راجع مقال Builders’ Library‏ضمان الرجوع إلى الحالة السابقة بأمان أثناء عمليات النشر 


نبذة عن المؤلف

كلير ليجوري؛ كبير مهندسي برامج في AWS. ينصب تركيزها الحالي على تجربة المطور الخاصة بخدمات الحاوية من AWS، حيث تقوم ببناء الأدوات عند نقطة التقاء الحاويات ومدة تطوير البرامج: التطوير المحلي، والبنية التحتية كتعليمة برمجية، وCI/CD، وقابلية الظهور، والعمليات.