Docker: الممارسة المتكاملة للمرحلة الثالثة

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

تجميع مفاهيم المرحلة الثالثة — بناء خلفية تجارة إلكترونية قائمة على الخدمات المصغرة.

1. ما ستتعلمه



2. القصة وراء خلفية منصة تجارة إلكترونية

(1) نقطة الألم: 6 خدمات يجب بدؤها يدويًا

يحتاج تشارلي إلى نشر ست خدمات لخلفية منصة التجارة الإلكترونية: واجهة React أمامية، وكيل Nginx عكسي، Go API، PostgreSQL، ذاكرة تخزين Redis المؤقتة، وطابور رسائل RabbitMQ. استخدام docker run يدويًا يتطلب تذكر أكثر من 30 معاملًا، وبما أن ترتيب البدء صارم، فإن معدل الخطأ مرتفع للغاية.

(2) حل للتكوين بنقرة واحدة

عالج تشارلي كل شيء بملف docker-compose.yml واحد.

BASH
docker compose up -d

(3) الفوائد: بدء 6 خدمات بنقرة واحدة

اختُزل من 30 دقيقة يدويًا إلى docker compose up -d في 30 ثانية. يمكن للموظفين الجدد تشغيل الخلفية بالكامل في 5 دقائق.



3. تصميم بنية الخدمات المصغرة

(1) نظرة عامة على البنية

100%
graph TB
    USER["المتصفح"] --> NGX["Nginx<br/>:80 وكيل عكسي"]
    NGX -->|"api/"| API1["Go API #1<br/>:8080"]
    NGX -->|"api/"| API2["Go API #2<br/>:8080"]
    NGX -->|"api/"| API3["Go API #3<br/>:8080"]
    NGX -->|"/"| REACT["React SPA<br/>ملفات ثابتة"]
    API1 --> PG["PostgreSQL<br/>:5432"]
    API2 --> PG
    API3 --> PG
    API1 --> REDIS["Redis<br/>:6379"]
    API2 --> REDIS
    API3 --> MQ["RabbitMQ<br/>:5672"]

(2) قائمة الخدمات

الخدمة الصورة المنفذ الشبكة التبعيات
Nginx nginx:1.25-alpine 80→80 frontend, backend api (healthy)
React مستضافة ذاتيًا (متعددة المراحل) - frontend -
Go API بناء ذاتي (متعدد المراحل) 8080 backend, db-net, cache-net postgres (healthy), redis
PostgreSQL postgres:15-alpine 5432 db-net -
Redis redis:7-alpine 6379 cache-net -
RabbitMQ rabbitmq:3-management 5672, 15672 backend -


4. كتابة ملفات Compose

▶ مثال: كتابة ملف compose كامل (الصعوبة: ⭐⭐⭐)

YAML
# ============================================
# docker-compose.yml - خدمات التجارة الإلكترونية المصغرة
# ============================================

services:
  # ---------- الواجهة الأمامية ----------
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - react-build:/usr/share/nginx/html:ro
    depends_on:
      api:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - frontend
      - backend
    healthcheck:
      test: ["CMD", "nginx", "-t"]
      interval: 30s
      timeout: 5s

  react:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    volumes:
      - react-build:/app/build
    networks:
      - frontend

  # ---------- الخلفية ----------
  api:
    build:
      context: ./api
      dockerfile: Dockerfile
    environment:
      DATABASE_URL: postgresql://appuser:${DB_PASSWORD:-secret}@postgres:5432/${DB_NAME:-ecommerce}
      REDIS_URL: redis://redis:6379
      RABBITMQ_URL: amqp://guest:guest@rabbitmq:5672
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    networks:
      - backend
      - db-net
      - cache-net
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
      interval: 15s
      timeout: 5s
      retries: 3
      start_period: 10s
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"

  # ---------- مخازن البيانات ----------
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${DB_PASSWORD:-secret}
      POSTGRES_DB: ${DB_NAME:-ecommerce}
    volumes:
      - pg-data:/var/lib/postgresql/data
    restart: unless-stopped
    networks:
      - db-net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser"]
      interval: 5s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis-data:/data
    restart: unless-stopped
    networks:
      - cache-net
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s

  rabbitmq:
    image: rabbitmq:3-management-alpine
    ports:
      - "15672:15672"   # واجهة الإدارة
    environment:
      RABBITMQ_DEFAULT_USER: guest
      RABBITMQ_DEFAULT_PASS: guest
    volumes:
      - mq-data:/var/lib/rabbitmq
    restart: unless-stopped
    networks:
      - backend

# ---------- وحدات التخزين ----------
volumes:
  pg-data:
  redis-data:
  mq-data:
  react-build:

# ---------- الشبكات ----------
networks:
  frontend:
  backend:
  db-net:
    internal: true   # لا وصول خارجي
  cache-net:
    internal: true

(1) إعدادات Nginx (موازنة الحمل)

TEXT 📖 للعرض فقط
# nginx.conf
upstream api_backend {
    server api:8080;   # DNS round-robin من Docker لـ 3 نسخ
}

server {
    listen 80;
    server_name localhost;

    # الملفات الثابتة (React SPA)
    location / {
        root /usr/share/nginx/html;
        try_files $uri $uri/ /index.html;
    }

    # وكيل API
    location /api/ {
        proxy_pass http://api_backend/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}


5. تكوين متعدد البيئات

▶ مثال: التجاوز في بيئة التطوير (الصعوبة: ⭐⭐)

YAML
# docker-compose.dev.yml
services:
  api:
    build:
      context: ./api
      dockerfile: Dockerfile.dev
    volumes:
      - ./api/src:/app/src   # إعادة تحميل تلقائية
    environment:
      GO_ENV: development
    deploy:
      replicas: 1            # نسخة واحدة للتصحيح

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


6. فحوصات الصحة والشفاء الذاتي

▶ مثال: إضافة فحوصات الصحة لجميع الخدمات (الصعوبة: ⭐⭐)

YAML
# تكوين فحص الصحة لكل خدمة
services:
  api:
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 20s

  postgres:
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U $$POSTGRES_USER"]
      interval: 5s
      timeout: 3s
      retries: 5

  redis:
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 3

المخرجات:

NAME                STATUS
ecommerce-api-1     Up 2 minutes (healthy)
ecommerce-postgres  Up 2 minutes (healthy)
ecommerce-redis-1   Up 2 minutes (healthy)


7. التحقق من النشر

(1) قائمة التحقق

عنصر الاختبار الأمر النتيجة المتوقعة
جميع الخدمات قيد التشغيل docker compose ps 6+ خدمات نشطة
Nginx سليم curl http://localhost 200 OK
صحة API curl http://localhost/api/health {"status":"ok"}
عدد نسخ API docker compose ps api 3 حاويات
اتصال Redis docker compose exec redis redis-cli ping PONG
اتصال PostgreSQL docker compose exec postgres pg_isready accepting connections
عزل قاعدة البيانات Ping إلى Postgres من حاوية Nginx فشل (شبكة داخلية)


8. مثال كامل: نشر منصة تجارة إلكترونية بنقرة واحدة

BASH
# ============================================
# شرح كامل: خدمات التجارة الإلكترونية المصغرة
# ============================================

# 1. بناء وبدء جميع الخدمات
docker compose up -d --build

# 2. التحقق من تشغيل جميع الخدمات
docker compose ps

# 3. التحقق من حالة الصحة
docker compose ps --format "table {{.Name}}\t{{.Status}}"

# 4. اختبار API
curl -s http://localhost/api/health | python3 -m json.tool

# 5. التحقق من موازنة الحمل (طلبات متعددة لنسخ مختلفة)
for i in $(seq 1 6); do
  curl -s http://localhost/api/health | jq -r '.hostname'
done

# 6. التحقق من اتصال قاعدة البيانات من API
docker compose exec api wget -qO- http://localhost:8080/health

# 7. الوصول إلى واجهة إدارة RabbitMQ
# افتح http://localhost:15672 (guest/guest)

# 8. عرض السجلات
docker compose logs -f api

# 9. توسيع API إلى 5 نسخ
docker compose up -d --scale api=5

# 10. التنظيف
docker compose down
docker compose down -v  # يزيل أيضًا وحدات تخزين البيانات

❓ أسئلة شائعة

س هل يجب استخدام أسماء الشبكة المستعارة أم أسماء الخدمات للتواصل بين الخدمات؟
ج في Compose، يوصى باستخدام أسماء الخدمات (مثل postgres و redis). يسجل Compose تلقائيًا سجلات DNS لكل خدمة، ويعمل اسم الخدمة كاسم مضيف. تُستخدم الأسماء المستعارة للشبكة في سيناريوهات تتطلب فيها نفس الخدمة أسماء مختلفة عبر شبكات مختلفة.
س كيف يتم ضمان ترتيب بدء التبعيات؟
ج depends_on + condition: service_healthy. لن تبدأ API إلا بعد اجتياز فحص الصحة لـ postgres. ملاحظة: depends_on: [db] (الشرط الافتراضي: service_started) ينتظر فقط بدء الحاوية؛ لا ينتظر حتى تكون الخدمة جاهزة.
س كيف تتعامل API متعددة النسخ مع موازنة الحمل؟
ج DNS round-robin المدمج في Docker + Nginx upstream. جميع حاويات API الثلاث مسجلة باسم DNS api، وسيقوم upstream api_backend { server api:8080; } في Nginx بتوزيع الطلبات بالدوران. لاستراتيجيات موازنة حمل أكثر تعقيدًا، يمكنك استخدام HAProxy.
س كيف أقوم بتكوين نسخ رئيسي-تابع لقاعدة البيانات؟
ج لمثيل Compose أحادي العقدة، يمكنك استخدام حاويات PostgreSQL متعددة مع تكوين نسخ رئيسي-تابع. ومع ذلك، للنسخ الرئيسي-التابع على مستوى الإنتاج، نوصي باستخدام قاعدة بيانات مُدارة (AWS RDS / مزود سحابي) أو أدوات مخصصة (Patroni/Stolon). مثيل PostgreSQL في هذا الدرس هو مثيل أحادي العقدة.
س كيف أنفذ إعادة التحميل التلقائية؟
ج في بيئة التطوير، قم بتركيب الكود المصدري باستخدام وحدة تخزين (مثال: -v ./api/src:/app/src) واستخدم أداة إعادة تحميل تلقائية (air لـ Go، nodemon لـ Node، flask --debug لـ Python). في بيئة الإنتاج، لا تقم بتركيب الكود المصدري؛ بدلاً من ذلك، طبق تغييرات التكوين عبر متغيرات البيئة وأعد التشغيل باستخدام docker compose up -d.

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): صمم بنية متعددة الخدمات لنظام مدونة (Nginx + API + DB + Cache)، واكتب ملف docker-compose.yml، وابدأ النظام.
  2. تمرين متقدم (الصعوبة: ⭐⭐): عدل API بنسخ متعددة (replicas: 3) وموازنة حمل Nginx، وتحقق من توزيع الطلبات عبر نسخ مختلفة.
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أضف مجموعتين من ملفات التجاوز لـ dev و prod (dev: أضف Adminer وإعادة تحميل تلقائية؛ prod: أضف حدود موارد وتدوير سجلات)، ثم ابدأ كل بيئة للتحقق من الاختلافات.
Web-Tutorial.com

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

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

100%