يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.
خليني أحكيلكم قصة صارت معي قبل كم سنة، لما كنت بشتغل مع فريق على مشروع حساس ومهم لعميل كبير. كانت الساعة حوالي 11 بالليل، والمفروض نسلم تحديث جديد فيه ميزة أساسية طلبها العميل بإلحاح. الأجواء كانت متوترة، والقهوة شغالة زي المي. أنا، كالعادة، كنت مسؤول عن عملية النشر (Deployment).
عملية النشر وقتها كانت “يدوية” بحتة. يعني بفتح الـ terminal، بعمل SSH على السيرفر، بسحب آخر التعديلات من Git بـ git pull، بشغل أمر الـ build يدوي، وبعدين بعمل restart للسيرفر وبقعد أدعي… آه والله، حرفيًا كنت أدعي: “يا رب يشتغل”، “يا رب ما يضرب إشي”، “يا رب ما يرن تلفوني بعد دقيقتين”. كان كل نشر للكود دعاءً وصلاة، وشعور بالقلق كأنه أول يوم امتحان بالجامعة.
في هذيك الليلة المشؤومة، بعد ما ضغطت Enter على أمر الـ restart… الموقع وقع. شاشة بيضاء. العميل بلش يرن، والرسائل على جروب الشغل صارت زي المطر. قضينا الساعتين اللي بعدهم في جحيم حقيقي، بنحاول نكتشف شو المشكلة، ونرجع النسخة القديمة يدويًا. المشكلة طلعت بسيطة جدًا: متغير بيئة (environment variable) ناقص على سيرفر الإنتاج. خطأ بشري بحت، كلفنا سمعتنا، ووقتنا، وأعصابنا.
هذيك الليلة كانت نقطة التحول. قلت للفريق: “يا جماعة، بكفي هيك. الشغل بالطريقة هاي مش احترافي ولا مستدام. لازم نلاقي حل”. ومن هنا بدأت رحلتنا مع ما يسمى بالـ CI/CD، الحل اللي أنقذنا من جحيم “إن شاء الله يشتغل”.
ما هو جحيم “إن شاء الله يشتغل”؟
قبل ما ندخل بالحلول، خلينا نوصف المشكلة اللي كلنا عشناها أو سمعنا عنها. هذا “الجحيم” إله أعراض واضحة:
- النشر اليدوي: كل خطوة بتعتمد على شخص بيعملها يدويًا، وهذا باب مفتوح للأخطاء البشرية والنسيان.
- الخوف من النشر: بيصير الفريق يخاف من عملية النشر، فبقلل عدد المرات اللي بنشر فيها، وهالشي بيراكم التغييرات وبيخلي كل عملية نشر أكبر وأخطر.
- بيئات غير متطابقة: الكود بيشتغل على جهاز المبرمج، بس بيضرب على السيرفر. ليش؟ لأنه البيئات مختلفة (إصدار Node.js مختلف، مكتبة ناقصة، متغير بيئة مش موجود).
- بطء في اكتشاف الأخطاء: ممكن خطأ صغير يدخل على الكود الرئيسي ويضل موجود لأيام أو أسابيع، وما نكتشفه إلا وقت النشر، لما يكون إصلاحه أصعب وأكثر تكلفة.
- إهدار الوقت والمجهود: بدل ما المطورين يكتبوا كود ويحلوا مشاكل حقيقية، بيقضوا وقتهم في عمليات نشر روتينية ومملة وإصلاح مشاكلها.
المنقذ: خط أنابيب CI/CD
الـ CI/CD هو اختصار لمصطلحين أساسيين، هما حجر الزاوية في ممارسات الـ DevOps الحديثة. خلونا نبسطهم.
CI: التكامل المستمر (Continuous Integration)
ببساطة، هاي الفكرة بتقول إنه كل مطور في الفريق لازم يدمج شغله مع الكود الرئيسي بشكل متكرر، عالأقل مرة باليوم. طيب شو الفائدة؟
الفائدة إنه مع كل عملية دمج، في نظام آلي (سيرفر الـ CI) بيقوم بالتالي:
- سحب الكود الجديد: أول ما تعمل
pushعلى الـ repository. - بناء المشروع (Build): يتأكد إنه الكود الجديد ما كسر عملية البناء الأساسية.
- تشغيل الاختبارات الآلية (Automated Tests): هاي هي النقطة الأهم. بيشغل كل أنواع الاختبارات (Unit, Integration) ليتأكد إنه التغيير الجديد ما خرب إشي قديم.
إذا فشلت أي خطوة من هدول، النظام بيرسل تنبيه فوري للفريق. هيك بنكتشف المشاكل خلال دقائق من كتابتها، مش بعد أسابيع.
CD: التسليم/النشر المستمر (Continuous Delivery/Deployment)
هاي هي المرحلة اللي بتيجي بعد الـ CI. بعد ما نتأكد إنه الكود سليم ونجح في كل الاختبارات، شو بيصير؟
- التسليم المستمر (Continuous Delivery): الكود اللي نجح في كل الاختبارات يتم تجهيزه وتغليفه (كـ Docker image مثلاً) ووضعه في مكان جاهز للنشر على بيئة الإنتاج (Production). عملية النشر نفسها بتضل تحتاج ضغطة زر يدوية. هاي بتعطينا تحكم إضافي، ومناسبة في كثير من الحالات.
- النشر المستمر (Continuous Deployment): هاي هي الدرجة الأعلى من الأتمتة. إذا نجح الكود في كل مراحل الـ CI، يتم نشره تلقائيًا على بيئة الإنتاج بدون أي تدخل بشري. هاي الطريقة بتحتاج ثقة عالية جدًا في اختباراتك الآلية.
نصيحة أبو عمر: لا تقفز مباشرة إلى النشر المستمر (Deployment). ابدأ بالتسليم المستمر (Delivery). ابنِ الثقة في خط أنابيبك واختباراتك أولًا. الأتمتة الكاملة هدف عظيم، لكن الوصول إليه بشكل متدرج أكثر أمانًا وواقعية.
رحلتنا العملية لبناء خط الأنابيب: من الفوضى إلى الأتمتة
طيب يا أبو عمر، حكيك حلو، بس كيف أطبق هالكلام؟ خليني أعطيكم خريطة طريق عملية بناءً على تجربتنا.
الخطوة 1: الأساس – Git Flow والانضباط
قبل أي أداة، لازم يكون عندك نظام في الشغل. اتفقنا على استراتيجية بسيطة في Git:
main: هذا الفرع يمثل دائمًا الكود الموجود على سيرفر الإنتاج. ممنوع الـ push عليه مباشرة.develop: هذا فرع التطوير الرئيسي.- Feature Branches: أي ميزة جديدة أو إصلاح لعلة، بنعمله فرع جديد من
develop(مثلاً:feature/user-login). بس يخلص الشغل، بنعمل Pull Request لـdevelop.
هذا التنظيم أساسي عشان أدوات الـ CI/CD تعرف متى تشتغل وعلى أي كود.
الخطوة 2: اختيار الأداة المناسبة
في أدوات كثيرة مثل Jenkins, GitLab CI, CircleCI. لكن نصيحتي، إذا كنت بتستخدم GitHub، فابدأ بـ GitHub Actions. ليش؟
- مدمجة مباشرة في GitHub.
- سهلة التعلم والبدء.
- مجانية لحد كبير للمشاريع الخاصة والعامة.
كل اللي بتحتاجه هو إنشاء ملف بامتداد .yml داخل مجلد .github/workflows في مشروعك.
الخطوة 3: أول خط أنابيب (CI)
كان هدفنا الأول بسيط: مع كل push على فرع develop، بدنا نتأكد إنه الكود سليم وبيجتاز الاختبارات. عملنا ملف اسمه ci.yml بهذا الشكل لمشروع Node.js:
# .github/workflows/ci.yml
name: CI Pipeline
# متى يتم تشغيل هذا الـ workflow
on:
push:
branches: [ develop ] # يشتغل عند أي push على فرع develop
pull_request:
branches: [ develop ] # ويشتغل عند فتح أي pull request على develop
jobs:
build-and-test:
# البيئة اللي رح يشتغل عليها (أحدث نسخة من أوبنتو)
runs-on: ubuntu-latest
steps:
# الخطوة 1: سحب الكود من الـ repository
- name: Checkout code
uses: actions/checkout@v3
# الخطوة 2: إعداد بيئة Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18' # تحديد إصدار Node.js لضمان تطابق البيئات
cache: 'npm' # لتسريع عملية تنزيل الـ packages في المرات القادمة
# الخطوة 3: تنزيل الاعتماديات (Dependencies)
- name: Install dependencies
run: npm install
# الخطوة 4: تشغيل الاختبارات الآلية
- name: Run tests
run: npm test
هذا الملف البسيط كان ثورة بالنسبة لنا. صرنا نعرف فورًا إذا كان الكود الجديد فيه مشكلة، قبل حتى ما يتم دمجه في develop. صار الـ Pull Request هو حارس البوابة.
الخطوة 4: مرحلة التسليم (CD)
بعد ما صار عنا ثقة في مرحلة الـ CI، قررنا نأتمت عملية النشر على السيرفر التجريبي (Staging). الفكرة كانت إنه أي كود يتم دمجه في فرع develop، يتم نشره تلقائيًا على بيئة الـ Staging.
أضفنا job جديد في ملف الـ workflow تبعنا. هذا مثال مبسط جدًا لاستخدام SSH و SCP (في الواقع، هناك طرق أكثر أمانًا مثل استخدام Docker أو actions مخصصة لمزود الخدمة السحابية الخاص بك، لكن هذا المثال يوضح الفكرة):
# ... نفس ملف الـ ci.yml السابق ...
jobs:
build-and-test:
# ... نفس الـ job السابق ...
deploy-to-staging:
# هذا الـ job لن يعمل إلا بعد نجاح الـ job السابق
needs: build-and-test
runs-on: ubuntu-latest
# لا يعمل إلا عند الدمج في فرع develop
if: github.ref == 'refs/heads/develop' && github.event_name == 'push'
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm install
# خطوة جديدة: بناء المشروع للإنتاج
- name: Build project
run: npm run build
# خطوة جديدة: نشر الملفات على السيرفر
- name: Deploy to staging server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USERNAME }}
key: ${{ secrets.STAGING_SSH_KEY }}
source: "build/" # المجلد الذي يحتوي على الملفات المبنية
target: "/var/www/staging-app" # المسار على السيرفر
# خطوة أخيرة: عمل restart للسيرفر على بيئة الـ staging
- name: Restart server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.STAGING_HOST }}
username: ${{ secrets.STAGING_USERNAME }}
key: ${{ secrets.STAGING_SSH_KEY }}
script: 'pm2 restart staging-app' # مثال باستخدام PM2
ملاحظة مهمة: قيم مثل STAGING_HOST و STAGING_SSH_KEY هي معلومات حساسة. لا نكتبها مباشرة في الملف، بل نستخدم ما يسمى بـ GitHub Secrets لتخزينها بشكل آمن.
النتيجة: من الدعاء إلى الثقة
بعد تطبيق هذه المنظومة، تغيرت طريقة عملنا جذريًا:
- نشر سريع ومتكرر: صرنا ننشر تحديثات على بيئة الـ Staging عدة مرات في اليوم، وعلى الإنتاج مرة كل يوم أو يومين، بثقة تامة وبضغطة زر.
- جودة أعلى: الاختبارات الآلية صارت جزء لا يتجزأ من عملنا، وهذا قلل من الأخطاء بشكل كبير.
- تركيز أعلى: المطورون صاروا يركزوا على كتابة الكود وحل المشاكل، بدل القلق من عمليات النشر.
- نوم هانئ: اختفى شعور القلق اللي كان يصاحب كل عملية نشر. صار الموضوع روتيني، موثوق، وممل… وهذا أفضل شيء يمكن أن يقال عن عملية نشر!
الخلاصة ونصيحة أخيرة 👍
يا صديقي المبرمج، إذا كنت لا تزال تعيش في جحيم “إن شاء الله يشتغل”، فاعلم أن هناك طريقة أفضل. رحلة التحول إلى CI/CD قد تبدو شاقة في البداية، لكنها استثمار سيعود عليك وعلى فريقك بفوائد لا تقدر بثمن: وقت، وجودة، وراحة بال.
نصيحتي لك: ابدأ صغيرًا. لا تحاول أتمتة كل شيء دفعة واحدة. ابدأ بأتمتة الاختبارات (CI). عندما تبني الثقة في هذه الخطوة، انتقل لأتمتة النشر على بيئة تجريبية. ثم، وفقط ثم، فكر في أتمتة النشر على بيئة الإنتاج.
تذكر دائمًا، الهدف ليس مجرد استخدام تقنية جديدة، بل بناء نظام عمل مستقر وموثوق يحررك لتبدع في ما تحب: كتابة كود رائع يحل مشاكل حقيقية.
بالتوفيق في رحلتكم من الدعاء إلى الثقة!