كيفية استخدام Docker Compose لمشروعات متعددة الخدمات

كيفية استخدام Docker Compose لمشروعات متعددة الخدمات (Docker Compose متعدد الخدمات)

في أغلب مشروعات الويب والتطبيقات الحديثة لن تتعامل مع خدمة واحدة فقط، بل ستحتاج إلى خلفية (Backend)، وقاعدة بيانات، وربما واجهة أمامية (Frontend)، وخدمات مساعدة أخرى مثل Redis أو Nginx. هنا يأتي دور Docker Compose متعدد الخدمات ليجعل إدارة كل هذه المكونات أسهل بكثير.

في هذا الشرح سنرى كيفية إنشاء ملف docker-compose.yml لتشغيل عدة خدمات معًا، مع توضيح:

  • بنية ملف Docker Compose لمشروع متعدد الخدمات.
  • تشغيل قواعد بيانات، واجهات، وخلفية في وقت واحد.
  • إدارة الشبكات بين الخدمات داخل Docker.
  • استخدام المتغيرات البيئية (Environment Variables).
  • تجهيز بيئة تطوير محلية مريحة وسهلة.

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

ما هو Docker Compose متعدد الخدمات؟

Docker Compose هو أداة تتيح لك تعريف وتشغيل عدة حاويات Docker كمنظومة واحدة. من خلال ملف واحد docker-compose.yml يمكنك:

  • تعريف كل خدمة (service) في مشروعك: مثل backend, frontend, db.
  • تحديد الإعدادات، المتغيرات البيئية، المنافذ، وحدات التخزين (Volumes) لكل خدمة.
  • تشغيل وإيقاف كل الخدمات بأمر واحد فقط.

عندما نقول Docker Compose متعدد الخدمات فنحن نقصد مشروعًا يعتمد على أكثر من حاوية متصلة معًا عبر شبكة داخلية، بحيث يتواصل الـ Backend مع قاعدة البيانات، ويتصل الـ Frontend بالـ Backend، وهكذا.

بنية ملف docker-compose.yml لمشروع متعدد الخدمات

ملف docker-compose.yml يعتمد على صيغة YAML، وأبسط هيكل لمشروع متعدد الخدمات يكون بالشكل التالي:

version: "3.9"

services:
  backend:
    image: my-backend-image
    ports:
      - "8000:8000"

  frontend:
    image: my-frontend-image
    ports:
      - "3000:3000"

  db:
    image: postgres:16
    ports:
      - "5432:5432"

هذا مثال مبسط، لكنه يوضح الفكرة:

  • services: القسم الذي يحتوي على كل الخدمات.
  • backend, frontend, db: أسماء الخدمات التي ستظهر داخل Docker.
  • image: الصورة المستخدمة لكل خدمة.
  • ports: إعادة توجيه المنافذ من الجهاز المضيف إلى الحاوية.

مثال عملي: مشروع Backend + Database + Frontend

لنأخذ مثالًا أكثر واقعية لمشروع مكون من:

  • خدمة Backend (مثل FastAPI أو Django أو Node.js).
  • قاعدة بيانات PostgreSQL.
  • واجهة أمامية Frontend (مثل React أو Vue).

إنشاء ملف docker-compose.yml

افترض أن هيكل مشروعك كالتالي:

project/
  backend/
    Dockerfile
    ...
  frontend/
    Dockerfile
    ...
  docker-compose.yml
  .env

ملف docker-compose.yml يمكن أن يكون بالشكل التالي:

version: "3.9"

services:
  db:
    image: postgres:16
    container_name: project_db
    restart: always
    environment:
      POSTGRES_USER: app_user
      POSTGRES_PASSWORD: app_password
      POSTGRES_DB: app_db
    ports:
      - "5432:5432"
    volumes:
      - db_data:/var/lib/postgresql/data
    networks:
      - project_network

  backend:
    build: ./backend
    container_name: project_backend
    depends_on:
      - db
    environment:
      DATABASE_URL: postgres://app_user:app_password@db:5432/app_db
      ENV: development
    ports:
      - "8000:8000"
    volumes:
      - ./backend:/app
    networks:
      - project_network

  frontend:
    build: ./frontend
    container_name: project_frontend
    depends_on:
      - backend
    environment:
      VITE_API_URL: http://localhost:8000
    ports:
      - "3000:3000"
    volumes:
      - ./frontend:/app
    networks:
      - project_network

volumes:
  db_data:

networks:
  project_network:
    driver: bridge

ما الذي يحدث هنا؟

في هذا المثال ركزنا على فكرة Docker Compose متعدد الخدمات وشرحنا كيف تتكامل المكونات:

  • الخدمة db:
    • تستخدم صورة postgres:16.
    • تعريف متغيرات إنشاء المستخدم وقاعدة البيانات.
    • حفظ بيانات قاعدة البيانات داخل Volume يسمى db_data ليبقى بعد إعادة تشغيل الحاوية.
    • مربوطة بنفس الشبكة project_network مع باقي الخدمات.
  • الخدمة backend:
    • يتم بناؤها من Dockerfile داخل مجلد backend.
    • depends_on: db يعني: انتظر قاعدة البيانات لتبدأ قبل الـ Backend.
    • متغير DATABASE_URL يشير إلى خدمة db بالاسم داخل الشبكة.
    • نربط كود المشروع من الجهاز المضيف داخل الحاوية عبر volumes لتسهيل التطوير.
  • الخدمة frontend:
    • يتم بناؤها من Dockerfile في مجلد frontend.
    • تعتمد على الـ Backend عبر depends_on: backend.
    • تستخدم متغير بيئي مثل VITE_API_URL للإشارة إلى عنوان الـ Backend.
  • القسم networks:
    • يتم إنشاء شبكة معزولة باسم project_network تتواصل من خلالها الخدمات فقط.

إدارة الشبكات بين الخدمات داخل Docker Compose

من أهم مزايا Docker Compose متعدد الخدمات أنه ينشئ شبكة خاصة بالخدمات، بحيث:

  • يمكن للحاويات التواصل مع بعضها عن طريق اسم الخدمة (مثل db أو backend).
  • لا تحتاج لاستخدام IP ثابت لكل حاوية، فالأسماء تحل المشكلة.
  • يمكنك عزل الخدمات عن الشبكة الخارجية إذا لم تحتاج لفتح منافذ خارجية.

في المثال السابق، الـ Backend يتصل بقاعدة البيانات عبر:

DATABASE_URL=postgres://app_user:app_password@db:5432/app_db

لاحظ استخدام اسم الخدمة db بدلًا من localhost، لأن الـ Backend داخل شبكة Docker لن يستطيع الوصول لقاعدة البيانات عبر localhost، بل عبر اسم الحاوية داخل الشبكة.

استخدام أكثر من شبكة

يمكنك أيضًا تعريف أكثر من شبكة لعزل بعض الخدمات عن الأخرى، مثل أن يكون لديك:

  • شبكة داخلية internal_net بين الـ Backend و قاعدة البيانات فقط.
  • وشبكة أخرى public_net للـ Frontend المتصل بالعالم الخارجي.

مثال مختصر:

networks:
  internal_net:
    internal: true
  public_net:
    driver: bridge

services:
  db:
    ...
    networks:
      - internal_net

  backend:
    ...
    networks:
      - internal_net
      - public_net

  frontend:
    ...
    networks:
      - public_net

هنا قاعدة البيانات db لا يمكن الوصول لها إلا من الخدمات الموجودة على internal_net، أي من الـ Backend فقط.

استخدام المتغيرات البيئية (Environment Variables)

المتغيرات البيئية عنصر أساسي في أي مشروع يستخدم Docker Compose متعدد الخدمات لأنها:

  • تسمح لك بتغيير الإعدادات بدون تعديل الكود.
  • تفصل بين إعدادات التطوير والإنتاج.
  • تسهل التعامل مع كلمات المرور وبيانات الاتصال.

استخدام ملف .env مع Docker Compose

بدلًا من كتابة القيم مباشرة في ملف docker-compose.yml، يمكن تخزينها في ملف .env في نفس مسار المشروع:

# ملف .env
POSTGRES_USER=app_user
POSTGRES_PASSWORD=app_password
POSTGRES_DB=app_db
BACKEND_PORT=8000
FRONTEND_PORT=3000

ثم في docker-compose.yml:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: ${POSTGRES_DB}
    ports:
      - "5432:5432"

  backend:
    build: ./backend
    ports:
      - "${BACKEND_PORT}:8000"

  frontend:
    build: ./frontend
    ports:
      - "${FRONTEND_PORT}:3000"

بهذا الشكل يمكن تغيير المنافذ أو بيانات قاعدة البيانات بسهولة دون تغيير ملف Compose نفسه.

Volumes: حفظ البيانات وتسهيل التطوير

في سيناريو Docker Compose متعدد الخدمات ستحتاج غالبًا إلى نوعين من الـ Volumes:

  1. Volumes لقاعدة البيانات: لحفظ البيانات بعد إعادة التشغيل.
  2. Bind Mounts للكود: لربط كود المشروع الجاري تطويره بحاوية الـ Backend أو الـ Frontend.

حفظ بيانات قاعدة البيانات

في المثال السابق استخدمنا:

volumes:
  db_data:

services:
  db:
    ...
    volumes:
      - db_data:/var/lib/postgresql/data

هذا يضمن عدم فقدان البيانات في كل مرة تقوم فيها بتشغيل أو إيقاف الحاوية.

ربط كود المشروع أثناء التطوير

أثناء التطوير، لا تريد إعادة بناء الصورة في كل تغيير للكود. لذلك نستخدم:

services:
  backend:
    build: ./backend
    volumes:
      - ./backend:/app

بهذا الشكل، كل تغيير في ملفات مجلد backend/ على جهازك ينعكس مباشرة داخل الحاوية في المسار /app، مما يجعل التجربة أقرب ما تكون لتشغيل المشروع مباشرة على جهازك، مع الاستفادة من عزلة Docker.

تشغيل وإدارة مشروع Docker Compose متعدد الخدمات

تشغيل الخدمات

من داخل مجلد المشروع الذي يحتوي على docker-compose.yml:

docker-compose up

هذا الأمر يقوم ببناء الصور (إن لزم) ثم تشغيل كل الخدمات. إذا أردت التشغيل في الخلفية (Detached Mode):

docker-compose up -d

إيقاف الخدمات

لإيقاف كل الخدمات:

docker-compose down

إذا أردت أيضًا حذف الـ Volumes (مثل بيانات قاعدة البيانات):

docker-compose down -v

إعادة بناء الصور بعد تعديل Dockerfile

إذا قمت بتعديل Dockerfile أو إعداد بناء الخدمة:

docker-compose up -d --build

عرض السجلات (Logs) لكل خدمة

لمتابعة ما يحدث داخل الخدمات، يمكنك عرض السجلات:

docker-compose logs -f

أو لسervice معينة فقط:

docker-compose logs -f backend

نصائح لتجهيز بيئة تطوير محلية مريحة

حتى تستفيد بالكامل من Docker Compose متعدد الخدمات في بيئة التطوير المحلية، إليك بعض الممارسات المفيدة:

  • استخدم Bind Mounts للكود:
    • كما ذكرنا، ربط كود المشروع داخل الحاوية يسمح بتطوير سريع بدون إعادة بناء.
  • استخدم أوامر تشغيل مناسبة داخل الحاوية:
    • في Python مثلًا، يمكنك استخدام uvicorn --reload مع FastAPI لإعادة تشغيل الخادم عند تغيير الملفات. يمكنك الرجوع لمقال بناء RESTful APIs باستخدام FastAPI لمعرفة كيفية تشغيل واجهات برمجية في بيئة تطوير.
  • فصل إعدادات التطوير عن الإنتاج:
    • يمكنك إنشاء ملفين:
      • docker-compose.dev.yml للإعدادات الخاصة بالتطوير.
      • docker-compose.prod.yml للإعدادات الخاصة بالإنتاج.
    • تشغيل ملف معين باستخدام:
      docker-compose -f docker-compose.dev.yml up
  • استخدم Healthchecks:
    • للتأكد من أن الخدمات الأساسية مثل قاعدة البيانات متاحة قبل أن تبدأ الخدمات المعتمدة عليها.

حالة استخدام واقعية: مشروع Python Backend مع Docker Compose

إذا كنت تعمل بمشروعات Python، وخاصة مع FastAPI أو Django، فإن الجمع بين البرمجة غير المتزامنة وDocker يناسب المشروعات ذات الحمل العالي. يمكنك قراءة مقال البرمجة غير المتزامنة في بايثون: تحسين الأداء باستخدام async و await ثم تطبيق هذا التصور داخل حاوية الـ Backend في Docker.

مثال بسيط لخدمة Backend FastAPI داخل Dockerfile:

# backend/Dockerfile
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]

ثم تشغيلها مع قاعدة بيانات وFrontend كما رأينا في ملف docker-compose.yml السابق.

أفضل الممارسات عند استخدام Docker Compose متعدد الخدمات

  • حافظ على بساطة الملف:
    • إذا زاد حجم docker-compose.yml كثيرًا، فكر في تقسيمه إلى أكثر من ملف (dev, prod, test).
  • لا تضع كلمات مرور حساسة في المستودع:
    • استخدم ملفات .env، ولا تقم برفعها إلى Git.
  • استخدم أسماء واضحة للخدمات:
    • مثل api_backend بدلاً من app لتسهيل الصيانة.
  • استخدم إصدارات صور محددة:
    • مثل postgres:16 بدلاً من postgres:latest لتجنب المفاجآت عند التحديث.

خلاصة

استخدام Docker Compose متعدد الخدمات يسهّل بشكل كبير إدارة مشروعات تحتوي على:

  • خدمات خلفية (Backends) متعددة.
  • واجهات أمامية (Frontends).
  • قواعد بيانات وخدمات داعمة مثل Redis، Nginx، RabbitMQ وغيرها.

من خلال ملف واحد docker-compose.yml يمكنك:

  • تشغيل كل الخدمات بأمر واحد.
  • إدارة الشبكات الداخلية بين الحاويات.
  • التحكم في المتغيرات البيئية ومنافذ الاتصال.
  • تجهيز بيئة تطوير محلية مريحة بدون تثبيت كل خدمة يدويًا على جهازك.

مع القليل من الممارسة ستتمكن من تصميم ملفات Docker Compose متعدد الخدمات تناسب أي مشروع تقريبًا، من تطبيقات ويب بسيطة إلى منظومات معقدة تضم عشرات الخدمات.

حول المحتوى:

شرح عملي لإنشاء ملف docker-compose لتشغيل قواعد بيانات، واجهات، وخدمات خلفية معًا، إدارة الشبكات، المتغيرات البيئية، وطرق التطوير المحلية السهلة.

هل كان هذا مفيدًا لك؟

أضف تعليقك