أتمتة بناء صور Docker ورفعها إلى Docker Hub آلياً بدون تدخل بشري

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

أتمتة بناء صور Docker ورفعها إلى Docker Hub آلياً بدون تدخل بشري

عندما يبدأ الفريق في الاعتماد على الحاويات، تصبح عملية بناء الصورة ورفعها يدوياً إلى Docker Hub نقطة ضعف تشغيلية واضحة. فكل خطوة يدوية تزيد احتمال نسيان تحديث الوسوم، أو رفع صورة غير مختبرة، أو استخدام بيانات اعتماد حساسة على جهاز مطور. لهذا السبب تُعد أتمتة دورة البناء والنشر جزءاً أساسياً من أي بيئة CI/CD حديثة.

الهدف من هذا النموذج ليس فقط تشغيل أمر docker build ثم docker push تلقائياً، بل بناء مسار عمل هندسي موثوق يربط Git مع الاختبارات والوسوم والإصدارات وإدارة الأسرار. إذا كنت قد قرأت مقدمة في GitHub Actions: كتابة أول مسار عمل (Workflow) فالمقال هنا يأخذك إلى مستوى أكثر احترافية ومباشرة في الإنتاج.

لماذا تحتاج إلى أتمتة بناء الصور أصلاً؟

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

  • ضمان توحيد بيئة البناء بدلاً من اختلاف أجهزة المطورين.
  • إنشاء صور قابلة للتعقب عبر وسم مثل latest وgit-sha.
  • تقليل الأخطاء البشرية في تسجيل الدخول ورفع الصور.
  • ربط النشر بالاختبارات التلقائية ضمن أنبوب واحد.
  • جعل أي خادم أو منصة نشر تسحب نفس الصورة المعتمدة.

وهذا ينسجم مباشرة مع فلسفة DevOps التي تبني الثقة من خلال التكرارية، القياس، وقابلية المراجعة.

المتطلبات المعمارية قبل بناء خط الأتمتة

قبل كتابة ملف Workflow، يجب أن يكون لديك مستودع يحتوي على Dockerfile نظيف وقابل للبناء المستقر. وإذا لم تكن هيكلة الصورة واضحة بعد، فراجع كتابة أول Dockerfile: تحويل سكربت Python إلى صورة (Image) معزولة أو فهم صور دوكر (Docker Images) وكيفية سحبها وإدارتها.

المتطلبات الأساسية:

  1. مستودع على GitHub.
  2. حساب فعال على Docker Hub.
  3. مستودع صور باسم واضح مثل username/app-name.
  4. تفعيل Secrets داخل المستودع.
  5. وجود فرع رئيسي محمي مثل main وفق ممارسات حماية الفروع (Branch Protection) ومنع رفع الأكواد الخاطئة للنسخة الحية.

لا تستخدم كلمة مرور حساب Docker Hub داخل خط الأتمتة. أنشئ Access Token مخصصاً بصلاحيات محدودة، وخزّنه داخل GitHub Secrets فقط.

بناء صورة قابلة للأتمتة بشكل صحيح

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

إذا كان هدفك تقليل وقت البناء واستهلاك الشبكة، فمقال تقليل حجم الحاويات: بناء صور دوكر خفيفة جداً وسريعة (Alpine Linux) يشرح ذلك من زاوية عملية مهمة جداً.

مثال مبسط على Dockerfile

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

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

إعداد الأسرار داخل GitHub بشكل آمن

اذهب إلى إعدادات المستودع ثم قسم Settings > Secrets and variables > Actions وأضف القيم التالية:

  • DOCKERHUB_USERNAME
  • DOCKERHUB_TOKEN

بهذا الشكل لن تظهر بيانات الاعتماد في الكود، ولن يحتاج أي مطور إلى حفظها محلياً. هذه الخطوة مرتبطة أيضاً بمبادئ تمرير الأسرار التي ناقشناها في تمرير المتغيرات البيئية (Environment Variables) للحاويات بشكل آمن.

ملف GitHub Actions لأتمتة البناء والرفع

الآن ننتقل إلى قلب العملية: إنشاء ملف .github/workflows/docker-publish.yml. هذا الملف سيعمل عند كل push إلى الفرع الرئيسي، ويبني الصورة ثم يرفعها آلياً.

name: Build and Push Docker Image

on:
  push:
    branches:
      - main

jobs:
  docker:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ secrets.DOCKERHUB_USERNAME }}/my-app
          tags: |
            type=raw,value=latest
            type=sha

      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

هذا المسار يستخدم عدداً من الإجراءات الجاهزة ذات الاعتمادية العالية. وهو أفضل بكثير من كتابة أوامر متفرقة يدوياً داخل shell script ما لم تكن لديك متطلبات خاصة جداً.

ماذا يفعل هذا المسار بالتحديد؟

  • يسحب الكود من المستودع باستخدام actions/checkout.
  • يجهز محرك البناء الحديث Buildx لتحسين البناء ودعم سيناريوهات متعددة.
  • يسجل الدخول إلى Docker Hub عبر الأسرار.
  • ينشئ وسوماً تلقائية مثل latest ووسم مبني على commit SHA.
  • يبني الصورة ويدفعها مباشرة إلى المستودع البعيد.

إضافة مرحلة اختبارات قبل الرفع

من الأخطاء الشائعة دفع الصورة مباشرة بعد البناء دون التحقق من أن التطبيق يعمل فعلاً. الأفضل هو ربط المسار بمرحلة فحص أو اختبار، كما شرحنا سابقاً في أتمتة تشغيل الاختبارات البرمجية (Unit Tests) في السحابة لاكتشاف الأخطاء.

name: Test Build and Push Docker Image

on:
  push:
    branches:
      - main

jobs:
  test-and-publish:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout source code
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm test

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build and push image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/my-app:latest

هنا لن يتم النشر إلا إذا نجحت الاختبارات أولاً. هذا يرفع جودة الإصدارات ويقلل احتمالات إرسال صورة مكسورة إلى البيئة الحية.

لا تجعل كل push من أي فرع يرفع صوراً إلى السجل العام. اربط النشر بفرع موثوق أو بوسوم release tags حتى لا تنشر إصدارات تجريبية بالخطأ.

أفضل ممارسات الوسوم والإصدارات

الاعتماد على latest فقط ممارسة ضعيفة لأنك تفقد القدرة على الرجوع إلى إصدار سابق بدقة. الأفضل هو نشر أكثر من وسم لكل صورة:

  • latest للنسخة المستقرة الحالية.
  • v1.4.2 عند وجود إصدار رسمي.
  • sha-xxxx أو وسم مبني على commit للتتبع الكامل.

هذا مهم جداً إذا كنت تشغّل خدمات متعددة عبر Docker Compose أو تنقل الصور لاحقاً إلى منصات أكبر مثل Kubernetes.

أخطاء شائعة يجب تجنبها

  • رفع صورة تحتوي ملفات حساسة بسبب غياب .dockerignore.
  • تشغيل النشر من فرع تطوير غير مستقر.
  • استخدام صورة أساسية قديمة مليئة بالثغرات.
  • حفظ اسم الصورة ثابتاً دون استراتيجية إصدار.
  • إهمال فحص السجلات الناتجة عن فشل pipeline.

كما أن إدارة الفروع بوضوح وفق استراتيجيات العمل الجماعي: نظام GitHub Flow وكيف تعمل الشركات الكبرى؟ تمنع خلط التعديلات غير الجاهزة مع مسار النشر التلقائي.

الخلاصة

أتمتة بناء صور Docker ورفعها إلى Docker Hub ليست رفاهية، بل طبقة أساسية من الانضباط الهندسي. عندما تربط GitHub Actions مع اختباراتك ووسومك وأسرارك، تحصل على سلسلة نشر قابلة للتكرار وآمنة وسريعة.

والنتيجة النهائية هي تقليل التدخل البشري، تسريع إصدار البرمجيات، ورفع موثوقية التشغيل في كل بيئة لاحقة. هذا هو الفرق بين مشروع يستخدم الحاويات، ومشروع يدير الحاويات باحتراف حقيقي.

4 comments

اترك تعليقاً

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