ما هو نشر الكناري (Canary Deployments) ولماذا سينقذ بيئة الإنتاج الخاصة بك؟
\nهل سبق لك أن قمت بنشر تحديث في منتصف الليل، متوسلاً ألا يحدث أي عطل؟ مألوف. وماذا لو أخبرتك أنه يمكنك اختبار كود جديد على مستخدمين حقيقيين، ولكن بطريقة لا يتضرر فيها أحد؟ الحديث عن نشر الكناري (canary deployments) — استراتيجية نشر حيث تحصل النسخة الجديدة من التطبيق أولاً على جزء صغير فقط من حركة المرور. إذا كان كل شيء على ما يرام — تزداد حركة المرور. إذا لم يكن كذلك — التراجع في ثوانٍ.
\nفي عالم .NET و C#، هذا مهم بشكل خاص: الخدمات المصغرة، الأحمال العالية، متطلبات الاستقرار. سنكتب الكود، ونضبط نشر الكناري (canary deployment)، ونحلل أمثلة حقيقية. هيا بنا!
\n\nلماذا الكناري (Canary) ليس مجرد كلمة موضة؟
\nالنشر التقليدي الأزرق-الأخضر (blue-green deployment) يحول كل حركة المرور مرة واحدة. التحديث المتداول (Rolling update) — تدريجياً، ولكن بدون تحكم دقيق. الكناري (Canary) يمنحك قوة خارقة: تقوم بإصدار النسخة الجديدة، ولكنك توجه إليها، على سبيل المثال، 5% من المستخدمين. أما الـ 95% المتبقية فتعمل على النسخة القديمة المستقرة. تراقب المقاييس، الأخطاء، التأخيرات. إذا كان كل شيء على ما يرام — تزيد النسبة تدريجياً إلى 100. إذا لم يكن كذلك — تقوم ببساطة بإيقاف تشغيل الكناري.
\nلمطور C#، هذا مريح بشكل خاص: يمكننا استخدام أدوات ASP.NET Core المضمنة، Kubernetes، أعلام الميزات (Feature Flags) أو حتى موازن تحميل بسيط. الشيء الرئيسي هو البنية الصحيحة.
\n\nبنية نشر الكناري (Canary Deployment) لتطبيق .NET
\nلنتخيل أن لدينا واجهة برمجة تطبيقات ويب (Web API) على ASP.NET Core تقوم بمعالجة الطلبات. نريد إصدار منطق جديد لحساب الخصومات. بدلاً من تحديث جميع الحاويات (pods) مرة واحدة، نقوم بما يلي:
\n- \n
- تشغيل نسختين من الخدمة: مستقرة (v1) وكناري (v2). \n
- ضبط موازن التحميل (مثل Nginx أو Kubernetes Service) بحيث تذهب 95% من حركة المرور إلى v1، و 5% إلى v2. \n
- إضافة رأس HTTP
X-Canary: trueفي استجابات نسخة الكناري لتحديد الهوية. \n - جمع المقاييس (كود 200/500، زمن الاستجابة، عدد الطلبات). \n
- إذا لم تكن الأخطاء خلال 30 دقيقة أكثر من v1، نزيد وزن الكناري إلى 50%، ثم إلى 100%. \n
التنفيذ بلغة C#: من البسيط إلى المعقد
\n\nالطريقة 1: الكناري عبر رؤوس HTTP وموازن التحميل
\nأبسط طريقة هي استخدام طبقة وسيطة (middleware) في ASP.NET Core تقوم بتمييز الطلبات. لكن أولاً، دعنا نرى كيف يبدو الكود الذي يميز نسخة الكناري.
\n// Startup.cs أو Program.cs\npublic class CanaryMiddleware\n{\n private readonly RequestDelegate _next;\n private readonly string _version;\n\n public CanaryMiddleware(RequestDelegate next, string version)\n {\n _next = next;\n _version = version;\n }\n\n public async Task InvokeAsync(HttpContext context)\n {\n // إضافة رأس لتحديد هوية النسخة\n context.Response.Headers.Add("X-App-Version", _version);\n await _next(context);\n }\n}\n\n// التسجيل\napp.UseMiddleware<CanaryMiddleware>("v2-canary");\n\nالآن على مستوى موازن التحميل (مثل Nginx)، نقرر أي نسبة من حركة المرور نرسلها إلى حاوية الكناري:
\nupstream backend {\n server app-stable:5000 weight=95;\n server app-canary:5000 weight=5;\n}\n\nالإيجابيات: البساطة. السلبيات: لا يوجد تحكم برمجي، صعوبة في إجراء اختبار A/B حسب المستخدمين.
\n\nالطريقة 2: أعلام الميزات (Feature Flags) في C#
\nنهج أكثر مرونة هو استخدام أعلام الميزات (feature flags). تقوم بتشغيل المنطق الجديد فقط لمجموعة معينة من المستخدمين. في ASP.NET Core، هناك دعم مدمج:
\n// قم بتثبيت الحزمة Microsoft.FeatureManagement.AspNetCore\n// أضف في Program.cs\nbuilder.Services.AddFeatureManagement();\n\n// حدد العلم في appsettings.json\n{\n "FeatureManagement": {\n "NewDiscountLogic": false\n }\n}\n\n// استخدمه في المتحكم (controller)\n[ApiController]\n[Route