مشروع عملي: بناء CI/CD كامل يختبر وينشر تطبيق ويب إلى السيرفر آلياً

دقائق القراءة: 5

مشروع عملي: بناء 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 لكن على مستوى البنية التشغيلية.

تحسينات احترافية بعد اكتمال المشروع

الخلاصة

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

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

8 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *