من الـ Commit إلى الإنتاج: كيف تتواصل الأدوات في خط أنابيب حقيقي
تقوم بدفع commit. ثم ماذا؟
إذا سبق لك أن شاهدت نشرًا يتعطل لأن خادم CI لم يتلق الإشعار، أو لأن شخصًا ما اضطر لنسخ artifact يدويًا من مكان إلى آخر، فأنت تعرف الألم جيدًا. الأدوات موجودة كلها. خط الأنابيب مُهيأ. لكن في المنتصف، تنقطع السلسلة. يضطر أحدهم إلى عمل SSH إلى خادم، وتشغيل أمر يدويًا، والأمل ألا يحدث خطأ.
هذه هي اللحظة التي يتوقف فيها CI/CD عن كونه آليًا ويتحول إلى مجموعة من الخطوات اليدوية المقنعة بأدوات.
العمل الحقيقي لخط الأنابيب ليس في أداة واحدة. بل في كيفية اتصال هذه الأدوات. يجب أن يؤدي commit في مستودعك إلى تشغيل خادم CI. يجب أن يعرف خادم CI أين يرسل الـ artifact النهائي. يجب أن يُعلم سجل الـ artifacts أداة النشر أن إصدارًا جديدًا جاهز. ويجب أن تتنسق أداة النشر مع عملية ترحيل قاعدة البيانات قبل أو بعد بدء تشغيل الإصدار الجديد.
هذه السلسلة من المشغلات وتدفق البيانات هي ما يجعل خط الأنابيب يعمل فعليًا. دعنا نستعرضها من البداية.
سلسلة المشغلات: كل أداة لها وظيفتان
كل أداة في خط الأنابيب تلعب دورين. تستقبل مشغلًا من الأداة التي قبلها، وترسل مشغلًا إلى الأداة التي بعدها. إذا انقطع أي اتصال، يتوقف خط الأنابيب. يضطر الفريق للتدخل يدويًا، وتعود إلى المشكلة ذاتها التي يفترض أن يحلها CI/CD: عمليات يدوية بطيئة وعرضة للأخطاء.
تبدأ السلسلة بـ commit. يدمج المطور التغييرات في الفرع الرئيسي، أو يفتح طلب سحب يتم دمجه. يكتشف خادم Git هذا الحدث. يجب أن يصل هذا الحدث إلى خادم CI. يمكن أن تكون آلية التسليم webhook، أو استقصاء دوري، أو ناقل أحداث. الطريقة لا تهم بقدر حقيقة أن خادم CI يعلم بوجود كود جديد في انتظاره.
يوضح مخطط التسلسل التالي سلسلة المشغلات وتدفق البيانات هذه:
إليك مثال ملموس لسلسلة المشغلات هذه في سير عمل GitHub Actions:
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build artifact
run: docker build -t myapp:${{ github.sha }} .
- name: Push to registry
run: |
docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
docker push registry.example.com/myapp:${{ github.sha }}
- name: Trigger deployment
run: |
curl -X POST https://deploy.example.com/api/deploy \
-H "Authorization: Bearer ${{ secrets.DEPLOY_TOKEN }}" \
-H "Content-Type: application/json" \
-d '{"artifact": "myapp", "version": "${{ github.sha }}", "env": "staging"}'
يقوم خادم CI بتشغيل خط الأنابيب الخاص به. يشمل ذلك مراحل البناء والاختبار والتعبئة. الناتج هو artifact. يمكن أن يكون هذا الـ artifact ثنائيًا مُجمّعًا، أو صورة حاوية، أو ملف تكوين، أو حزمة ترحيل قاعدة بيانات. يجب أن يذهب إلى سجل (registry). السجل ليس مجرد مجلد على خادم. إنه نظام تخزين منظم حيث كل artifact له إصدار وبيانات وصفية ومعلومات عن مصدره وكيفية بنائه.
الآن تحتاج أداة النشر إلى معرفة أن إصدارًا جديدًا متاح. بعض أدوات النشر تراقب السجل مباشرة. البعض الآخر ينتظر مشغلًا من خادم CI أو webhook من السجل. في كلتا الحالتين، يجب أن تتلقى أداة النشر معلومات تفيد بأن الإصدار X من artifact Y جاهز لبيئة معينة.
ثم تقوم أداة النشر بالنشر إلى البيئة المستهدفة. لكن عمليات نشر التطبيقات نادرًا ما تكون منفردة. قبل تشغيل الإصدار الجديد، غالبًا ما تحتاج قاعدة البيانات إلى تغييرات: عمود جديد، فهرس معدّل، أو ترحيل بيانات. يجب أن تعمل ترحيلات قاعدة البيانات كجزء من النشر، وليس كخطوة يدوية منفصلة. الترتيب مهم. أحيانًا يجب أن يعمل الترحيل قبل بدء تشغيل إصدار التطبيق الجديد. وأحيانًا بعده. يعتمد ذلك على ما إذا كان التغيير متوافقًا مع الإصدارات السابقة أم لا.
بعد انتهاء النشر، لا ينتهي خط الأنابيب. تحتاج إلى التحقق من أن الإصدار الجديد يعمل بشكل طبيعي. يمكن أن يكون هذا فحص صحة، أو اختبار تدخين، أو إشارة مراقبة تظهر عدم وجود ارتفاع مفاجئ في الأخطاء. إذا فشل التحقق، يجب تشغيل التراجع تلقائيًا أو بنقرة واحدة.
تدفق الـ Artifact: البيانات التي تنتقل بين الأدوات
عبر هذه السلسلة، تتدفق البيانات بين الأدوات. بيانات الـ commit، إصدار الـ artifact، حالة خط الأنابيب، نتائج الاختبار، تكوين البيئة، وبيانات الاعتماد للوصول إلى كل أداة. يجب أن تنتقل هذه البيانات من أداة إلى أخرى دون تدخل يدوي.
هذا هو تدفق الـ artifact. كلما كان تدفق الـ artifact أنظف، قلّت الأخطاء الناتجة عن فقدان المعلومات في المنتصف. عندما يفشل النشر لأن شخصًا ما نسي تحديث ملف تكوين، أو لأنه تم نشر إصدار artifact خاطئ، فإن السبب الجذري دائمًا تقريبًا هو تدفق artifact مكسور.
يتضمن تدفق الـ artifact المُصمم جيدًا ما يلي:
- معرف فريد لكل artifact يرتبط بالـ commit المحدد وتشغيل خط الأنابيب.
- تكوين خاص بالبيئة يُخزن بشكل منفصل عن الـ artifact نفسه، بحيث يمكن ترقية نفس الـ artifact عبر مراحل التجربة إلى الإنتاج دون إعادة بناء.
- إدارة بيانات الاعتماد التي تسمح لكل أداة بالمصادقة على الأداة التالية دون أسرّار مشفرة بشكل ثابت.
- نشر الحالة بحيث تعرف كل أداة في السلسلة ما إذا كانت الخطوة السابقة قد نجحت أم فشلت.
خط الأنابيب ليس دائمًا خطيًا
تبدو سلسلة المشغلات خطية، لكن خطوط الأنابيب الحقيقية ليست بهذه البساطة. أحيانًا يؤدي commit واحد إلى تشغيل خطوط أنابيب متعددة في وقت واحد: واحد للتطبيق، وآخر للبنية التحتية، وآخر لقاعدة البيانات. أو تتراكم عدة commits قبل تشغيل النشر. تستخدم بعض الفرق نموذج فرع الإصدار حيث يتم تجميع commits ونشرها معًا. ينشر آخرون كل commit إلى الإنتاج مباشرة.
يحدد نمط المشغلات وتدفق البيانات كيفية تصرف خط الأنابيب تحت الضغط. إذا اخترت أدوات دون فهم كيفية اتصالها، سينتهي بك الأمر بخط أنابيب يعمل في العروض التوضيحية لكنه يتعطل في الإنتاج. الأداة التي تبدو رائعة على الورق قد لا تتكامل جيدًا مع سجل artifacts الخاص بك. أداة النشر التي تتعامل مع الحاويات بشكل جميل قد لا تدعم ترحيلات قاعدة البيانات.
لهذا السبب يجب أن يبدأ اختيار الأداة بالسلسلة، وليس بالميزات الفردية. ارسم كيف يصبح commit خدمة قيد التشغيل في الإنتاج. حدد كل نقطة تسليم. ثم قيّم الأدوات بناءً على كيفية تعاملها مع نقاط التسليم هذه.
خطر انتشار الأدوات غير المنسق
عندما يختار كل فريق أدواته الخاصة دون تنسيق، لا تكون النتيجة خط أنابيب سلسًا. بل انتشارًا للأدوات. فريق يستخدم Jenkins. وآخر يستخدم GitHub Actions. فريق قاعدة البيانات لديه أداة الترحيل الخاصة به. فريق البنية التحتية يستخدم Terraform مع خلفية حالة مختلفة. كل أداة تعمل بمعزل عن الأخرى، لكن ربطها يتطلب نصوصًا برمجية مخصصة، وخطوات يدوية، والكثير من المعرفة الضمنية.
انتشار الأدوات ليس مجرد مشكلة صيانة. إنها مشكلة موثوقية. كل تكامل مخصص هو نقطة فشل. كل تسليم يدوي هو مكان تحدث فيه الأخطاء. الهدف ليس استخدام نفس الأداة لكل شيء. الهدف هو الحصول على سلسلة مشغلات وتدفق artifact متماسكين عبر أي أدوات تختارها.
قائمة تحقق عملية لربط الأدوات
قبل أن تقوم بإنهاء اختيار الأداة، راجع قائمة التحقق هذه لكل نقطة تسليم في خط الأنابيب الخاص بك:
- كيف يُعلم خادم Git خادم CI بوجود commit جديد؟
- كيف يدفع خادم CI الـ artifact إلى السجل؟
- كيف تتعلم أداة النشر عن إصدار artifact جديد؟
- كيف تشغل أداة النشر ترحيلات قاعدة البيانات؟
- كيف يتحقق خط الأنابيب من نجاح النشر؟
- كيف يتم تشغيل التراجع، وهل يشمل تراجع قاعدة البيانات؟
- ما البيانات التي تتدفق بين كل زوج من الأدوات، وهل يتم تمريرها تلقائيًا؟
إذا تطلبت أي من نقاط التسليم هذه قيام شخص بشيء يدويًا، فقد وجدت فجوة ستسبب مشاكل عاجلاً أم آجلاً.
الخلاصة
خط الأنابيب قوي بقدر أضعف اتصال فيه. الأدوات التي تختارها أقل أهمية من كيفية تمرير المشغلات والبيانات لبعضها البعض. ابدأ برسم السلسلة من الـ commit إلى الإنتاج. حدد كل نقطة تسليم. ثم اختر أدوات تتناسب مع تلك السلسلة، وليس أدوات تبدو مثيرة للإعجاب بمعزل عن غيرها. أفضل خط أنابيب ليس هو الذي يحتوي على أكبر عدد من الميزات. بل هو الذي يتدفق فيه الـ commit إلى الإنتاج دون أن يضطر أحد للتوقف ومعرفة ما يجب فعله بعد ذلك.