مشروع عملي: بناء CI/CD كامل يختبر وينشر تطبيق ويب إلى السيرفر آلياً
مشروع عملي: بناء CI/CD كامل يختبر وينشر تطبيق ويب إلى السيرفر آلياً
بناء خط نشر آلي متكامل لم يعد رفاهية، بل أصبح جزءاً أساسياً من أي مشروع يريد تقليل الأخطاء وتسريع التسليم. إذا كنت قد قرأت سابقاً ما هو الـ CI/CD؟ ولماذا نؤتمت عمليات اختبار ونشر الأكواد؟ فهذه المقالة تنقلك من الفكرة النظرية إلى التطبيق العملي الكامل.
السيناريو الذي سنبنيه هنا بسيط في الشكل لكنه احترافي في الجوهر: مطور يدفع التعديلات إلى فرع الإنتاج، فتبدأ منصة GitHub Actions بتشغيل الاختبارات، ثم تبني صورة Docker، ثم ترفعها إلى السجل، وأخيراً تتصل بالسيرفر لتحديث التطبيق دون تدخل يدوي.
هذا الأسلوب يعالج مشكلات تشغيلية متكررة مثل اختلاف البيئات، نسيان خطوة أثناء النشر، أو إدخال تحديث غير مختبر إلى النسخة الحية. وهو امتداد عملي لأفكار ما هو DevOps؟ ولماذا تدفع الشركات ثروات لمهندسي الأتمتة السحابية؟ حيث تتحول العمليات اليدوية إلى إجراءات قابلة للتكرار والقياس والتحسين.
المعمارية التي سنعتمدها في المشروع
سنفترض وجود تطبيق ويب يعمل مثلاً عبر Node.js داخل حاوية، مع سيرفر Linux يستقبل النسخة الجديدة ويشغلها بواسطة Docker Compose.
- المطور يرفع الكود إلى مستودع
GitHub. - يبدأ
Workflowبتنفيذ الاختبارات. - إذا نجحت، يتم بناء الصورة ورفعها إلى
Docker Hub. - يتصل النظام بالسيرفر عبر
SSHلسحب الصورة الجديدة وإعادة التشغيل.
إذا كنت بحاجة إلى أساس أقوى لفهم الحاويات، فمقال مشكلة “الكود يعمل على جهازي فقط” وكيف يحلها Docker نهائياً؟ يفسر لماذا نستخدم هذه الطبقة قبل الانتقال إلى النشر المؤتمت.
المتطلبات قبل بناء خط النشر
1) تجهيز التطبيق للحاويات
يجب أولاً أن يكون التطبيق قابلاً للبناء كصورة. إذا لم تنشئ ذلك بعد، فمقال كتابة أول Dockerfile: تحويل سكربت Python إلى صورة (Image) معزولة أو تحويل سيرفر Node.js متكامل إلى حاوية Docker قابلة للنقل يمثلان أساساً ممتازاً.
مثال ملف Dockerfile لتطبيق ويب:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm test
EXPOSE 3000
CMD ["npm", "start"]
دمج خطوة npm test داخل البناء ليس بديلاً عن اختبارات CI، لكنه يضيف طبقة تحقق إضافية تمنع بناء صورة صالحة شكلياً وفاشلة وظيفياً.
2) إعداد تشغيل التطبيق على السيرفر
في جهة الخادم، سنستخدم ملف docker-compose.yml. وإذا أردت فهماً أعمق، راجع ما هو Docker Compose؟ ولماذا نحتاجه لتشغيل المشاريع المعقدة؟ وكتابة أول ملف docker-compose.yml خطوة بخطوة.
version: "3.9"
services:
web:
image: yourdockerhubusername/myapp:latest
container_name: myapp_web
restart: unless-stopped
ports:
- "80:3000"
environment:
- NODE_ENV=production
هذا التكوين يعني أن السيرفر لا يحتاج إلى نسخ الكود المصدري كل مرة. بدلاً من ذلك، يسحب آخر صورة جاهزة ويشغلها مباشرة، وهو نمط أكثر نظافة وثباتاً من النشر التقليدي المعتمد على رفع الملفات يدوياً.
بناء خط CI/CD داخل GitHub Actions
الملف التالي يترجم الفكرة إلى مسار عمل كامل. وهو متوافق مع مفاهيم مقدمة في GitHub Actions: كتابة أول مسار عمل (Workflow) وبناء أوامر YAML لأتمتة تشغيل السكربتات التلقائية عند كل Push.
name: Deploy Web App
on:
push:
branches:
- main
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
build-and-push:
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push image
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/myapp:latest
deploy:
runs-on: ubuntu-latest
needs: build-and-push
steps:
- name: Deploy on remote server
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /opt/myapp
docker compose pull
docker compose up -d
docker image prune -f
هذا المسار يطبق مبدأ مهم جداً: لا نشر قبل الاختبار، ولا تحديث للسيرفر قبل نجاح بناء الصورة. عملياً، أنت تنفذ ما شرحناه سابقاً في إنشاء خط أنابيب (Pipeline) يرفض الأكواد التي تفشل في الاختبارات وأتمتة تشغيل الاختبارات البرمجية (Unit Tests) في السحابة لاكتشاف الأخطاء.
إدارة الأسرار والمفاتيح بشكل آمن
نجاح المشروع لا يعتمد فقط على التشغيل، بل على تأمين بيانات الوصول. يجب تخزين اسم مستخدم السجل، رمز الوصول، عنوان الخادم، وملف المفتاح الخاص داخل GitHub Secrets وليس داخل المستودع.
هذه النقطة مرتبطة مباشرة بمقال إدارة الأسرار (GitHub Secrets) لحماية كلمات المرور ومفاتيح الـ API في الأتمتة، لأن كشف أي سر داخل ملف YAML قد يحول خط النشر من أداة إنتاجية إلى ثغرة اختراق مباشرة.
لا تستخدم حساب
rootللنشر التلقائي. أنشئ مستخدماً محدود الصلاحيات، وقيّد وصوله إلى مجلد التطبيق فقط، وفعّل المصادقة بالمفاتيح بدلاً من كلمات المرور لتقليل مساحة الهجوم.
قبل اعتماد أي نشر حي، فعّل حماية الفروع (Branch Protection) ومنع رفع الأكواد الخاطئة للنسخة الحية. بذلك يصبح فرع
mainمحكوماً بالمراجعة والاختبار، لا بالعشوائية.
كيف تمنع التوقف أثناء التحديثات؟
في المشاريع الصغيرة، يكفي غالباً تنفيذ docker compose up -d. لكن في الأنظمة الحساسة، تحتاج إلى استراتيجية أكثر نضجاً مثل نشر تدريجي أو وجود reverse proxy أمام نسختين من الخدمة.
كما يجب ربط النشر بفحوص صحة health checks حتى لا يتم اعتبار الحاوية الجديدة ناجحة فور تشغيلها فقط. نجاح العملية الحقيقي يعني أن التطبيق يستجيب للطلبات ويصل إلى قاعدة البيانات ويخدم المستخدمين دون أخطاء.
لا تحذف الصورة السابقة فوراً إذا كان التطبيق حرجاً. احتفظ بآخر إصدار مستقر لتسهيل التراجع السريع. وعند وقوع مشكلة، استخدم نهجاً مشابهاً لفلسفة التراجع عن الأخطاء الكارثية: الفرق بين Git Reset و Git Revert لكن على مستوى البنية التشغيلية.
تحسينات احترافية بعد اكتمال المشروع
- إرسال رسائل نجاح أو فشل إلى
SlackأوTelegramكما في إرسال إشعارات فورية إلى Telegram أو Slack عند نجاح أو فشل نشر التحديثات. - استخدام وسوم إصدارات مثل
v1.4.2بدلاً من الاعتماد الدائم علىlatest. - إضافة فحوص جودة مثل
lintوsecurity scanningقبل البناء. - فصل بيئات
stagingوproductionوعدم النشر الحي مباشرة من كل دفع جديد.
الخلاصة
المشروع الذي بنيناه هنا ليس مجرد ملف YAML، بل منظومة تشغيل متكاملة تربط بين الاختبار والبناء والتوزيع والحماية. عندما يصبح النشر إجراءً آلياً يمكن التنبؤ به، تنخفض الأخطاء البشرية ويرتفع استقرار التطبيق بشكل ملحوظ.
ابدأ أولاً بخط بسيط: اختبار، بناء، نشر. ثم أضف تدريجياً الإشعارات، الاسترجاع، حماية الفروع، وفحوص الأمان. بهذه الطريقة تنتقل من إدارة سيرفرات يدوية مرهقة إلى منصة تشغيل حديثة تعكس فعلاً جوهر DevOps العملي.
8 comments