Docker: التنسيق المتقدم مع Compose

آخر تحديث: 2026-08-26

التطوير والاختبار والإنتاج — ملف Compose واحد يدير البيئات الثلاث.

1. ما ستتعلمه



2. قصة حقيقية لقائد تقني

(1) نقطة الألم: ثلاث بيئات، ثلاثة ملفات تكوين

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

(2) حل Profiles + Override

استخدم تشارلي profiles وملفات التجاوز لتنفيذ حل قائم على "تكوين أساسي واحد + تجاوزات خاصة بالبيئة."

BASH
# التطوير: تفعيل ملف dev + تجاوزات dev
docker compose --profile dev -f docker-compose.yml -f docker-compose.dev.yml up -d

# الإنتاج: تجاوزات prod فقط
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

(3) النتيجة: 3 ملفات ← 1 + 2 ملف

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



3. Profiles: تكوين متعدد البيئات

(1) آلية Profile

100%
graph TB
    BASE["docker-compose.yml<br/>الخدمات الأساسية"] --> DEV["--profile dev<br/>+ أدوات dev (hot-reload, adminer)"]
    BASE --> TEST["--profile test<br/>+ منفذي الاختبار (selenium)"]
    BASE --> PROD["--profile prod<br/>+ المراقبة (prometheus, grafana)"]

▶ مثال: --profile dev مفعّل (الصعوبة: ⭐⭐)

YAML
# docker-compose.yml مع profiles
services:
  api:
    build: .
    ports:
      - "5000:5000"
    environment:
      - FLASK_ENV=development

  db:
    image: postgres:15-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data

  # للتطوير فقط: واجهة إدارة قاعدة البيانات
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    depends_on: [db]

  # للتطوير فقط: إعادة تحميل تلقائية مع تصحيح Flask
  api-dev:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/app
    ports:
      - "5000:5000"
      - "5678:5678"   # منفذ التصحيح
    environment:
      - FLASK_DEBUG=1
    profiles: ["dev"]

  # للإنتاج فقط: مراقبة Prometheus
  prometheus:
    image: prom/prometheus
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    profiles: ["prod"]

volumes:
  pg-data:
BASH
# التطوير: الخدمات الأساسية + ملف dev
docker compose --profile dev up -d

# الإنتاج: الخدمات الأساسية + ملف prod
docker compose --profile prod up -d

# الخدمات الأساسية فقط (بدون ملف)
docker compose up -d

(1) مقارنة Profiles مقابل Override

البُعد Profiles ملفات Override
عدد الملفات ملف واحد 2–3 ملفات
طريقة التفعيل --profile xxx -f base.yml -f override.yml
السيناريوهات المناسبة توسيع الخدمات اختلافات كبيرة في التكوين
قابلية التركيب يمكن دمج ملفات متعددة تجاوز بالترتيب


4. دمج ملفات Override

▶ مثال: تجاوز تكوين الملف (الصعوبة: ⭐⭐⭐)

YAML
# docker-compose.yml (التكوين الأساسي)
services:
  api:
    build: .
    environment:
      FLASK_ENV: production
    restart: unless-stopped

  db:
    image: postgres:15-alpine
    volumes:
      - pg-data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  pg-data:
YAML
# docker-compose.dev.yml (تجاوزات dev)
services:
  api:
    environment:
      FLASK_ENV: development
      FLASK_DEBUG: "1"
    volumes:
      - ./src:/app     # إعادة تحميل تلقائية: تركيب الكود المصدري
    ports:
      - "5678:5678"    # منفذ التصحيح

  # خدمة للتطوير فقط
  adminer:
    image: adminer
    ports:
      - "8081:8080"
YAML
# docker-compose.prod.yml (تجاوزات prod)
services:
  api:
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  # للإنتاج فقط: وكيل Nginx العكسي
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - api
BASH
# التطوير
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d

# الإنتاج
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# معاينة التكوين المدمج (dry-run)
docker compose -f docker-compose.yml -f docker-compose.prod.yml config


5. التوسع الأفقي

▶ مثال: توسيع --scale (الصعوبة: ⭐⭐)

BASH
# توسيع خدمة API إلى 3 نسخ
docker compose up -d --scale api=3

# التوسع الديناميكي على مجموعة قيد التشغيل
docker compose up -d --scale api=5

(1) اعتبارات التوسع

المشكلة السبب الحل
تعارض المنافذ نسخ متعددة معينة لنفس منفذ المضيف نقطة دخول واحدة فقط مكشوفة (Nginx)؛ API لا تعين لمنفذ مضيف
اتساق البيانات الكتابة إلى نسخ متعددة في نفس التخزين فقط الخدمات عديمة الحالة مناسبة للتوسع؛ قواعد بيانات مشتركة
موازنة الحمل كيفية توزيع الطلبات عبر نسخ متعددة Nginx upstream أو DNS round-robin المدمج في Docker

▶ مثال: التوسع التعريفي بـ deploy.replicas (الصعوبة: ⭐⭐)

YAML
services:
  api:
    build: .
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 256M
        reservations:
          cpus: "0.25"
          memory: 128M
    # لا تعين منافذ للخدمات الموسعة
    # ports: ["5000:5000"]  ← هذا يتعطل مع replicas > 1

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
    # Nginx upstream يستخدم DNS round-robin إلى api:5000


6. حدود الموارد

(1) إعدادات deploy.resources

YAML
services:
  api:
    deploy:
      resources:
        limits:        # حدود صارمة (تُقتل إذا تجاوزت)
          cpus: "1.0"
          memory: 512M
        reservations:  # ضمانات مرنة (الحد الأدنى)
          cpus: "0.5"
          memory: 256M
الحقل الغرض التأثير
limits.cpus حد CPU تجاوز: تقييد
limits.memory حد الذاكرة تجاوز: OOM Kill
reservations.cpus ضمان أدنى CPU ضمان الجدولة
reservations.memory ضمان أدنى ذاكرة ضمان الجدولة


7. مثال كامل: مشروع Compose لثلاث بيئات

YAML
# ============================================
# docker-compose.yml - التكوين الأساسي
# ============================================
services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://postgres:${DB_PASSWORD:-secret}@db:5432/${DB_NAME:-myapp}
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
      POSTGRES_DB: ${DB_NAME:-myapp}
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  # للتطوير فقط: واجهة Adminer DB
  adminer:
    image: adminer
    ports:
      - "8081:8080"
    profiles: ["dev"]
    networks:
      - backend

volumes:
  pg-data:

networks:
  backend:
YAML
# ============================================
# docker-compose.prod.yml - تجاوزات الإنتاج
# ============================================
services:
  api:
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 5s
      retries: 3

  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
BASH
# التطوير
docker compose --profile dev up -d

# الإنتاج (3 نسخ + Nginx + حدود الموارد)
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# التحقق من التوسع
docker compose -f docker-compose.yml -f docker-compose.prod.yml ps

❓ أسئلة شائعة

س هل يمكن استخدام profiles وملفات override معًا؟
ج نعم. docker compose --profile dev -f docker-compose.yml -f docker-compose.dev.yml up -d استخدم profiles وملفات override معًا. Profiles تدير إضافة وإزالة الخدمات، بينما ملفات override تدير اختلافات التكوين؛ الاثنان يكملان بعضهما.
س أيهما له الأسبقية — --scale أم deploy.replicas؟
ج وسيط سطر الأوامر --scale له الأسبقية على إعلان YAML deploy.replicas. إذا تم تعيين كلاهما، فإن قيمة --scale تتجاوز القيمة في YAML. في بيئة الإنتاج، نوصي باستخدام إعلان YAML (الذي يدعم التحكم في الإصدار)؛ استخدم --scale للتعديلات السريعة أثناء التصحيح.
س كيف أتحكم في ترتيب بدء الخدمات؟
ج depends_on + condition. ثلاث شروط: ① service_started (افتراضي؛ ينتظر البدء فقط)؛ ② service_healthy (ينتظر اجتياز فحص الصحة)؛ ③ service_completed_successfully (ينتظر اكتمال مهام التهيئة). استخدم دائمًا service_healthy في بيئات الإنتاج.
س كيف أبدل بين المتغيرات في ملفات Compose عبر البيئات المختلفة؟
ج ${VAR:-default} + ملفات .env. ضع ملفات .env مختلفة في كل بيئة: .env.dev / .env.prod. حدد --env-file عند البدء: docker compose --env-file .env.prod up -d.
س كيف تُعالج المنافذ عند وجود نسخ متعددة؟
ج الخدمة الموسعة لا يمكنها تعيين منافذ المضيف (النسخ المتعددة ستسبب تعارضات). اسمح فقط لـ Nginx/HAProxy بتعيين المنافذ؛ خدمة API تتواصل داخليًا عبر الشبكة. Nginx upstream يستخدم DNS round-robin من Docker لتوزيع حركة المرور تلقائيًا عبر النسخ المتعددة.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): اكتب مجموعتين من ملفات التجاوز (واحدة لـ dev وواحدة لـ prod) للتطبيق من الدرس 12؛ أضف إعادة تحميل تلقائية لـ dev وحدود موارد لـ prod.
  2. تمرين متقدم (الصعوبة: ⭐⭐): استخدم --scale api=3 لتوسيع خدمة API إلى 3 نسخ، واستخدم docker compose ps للتحقق.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أعد وكيل Nginx عكسيًا لـ API مع 3 نسخ، وتحقق من أن الطلبات موزعة على نسخ مختلفة (بفحص سجلات الحاويات المختلفة).
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%