Docker: التنسيق المتقدم مع Compose
آخر تحديث: 2026-08-26
التطوير والاختبار والإنتاج — ملف Compose واحد يدير البيئات الثلاث.
1. ما ستتعلمه
- Profiles: تكوين متعدد البيئات
- إعادة استخدام وتوريث التكوين (extends / override)
- التوسع الأفقي
- إعلان حدود الموارد
- التحكم في التبعيات مع فحص الصحة
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
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 لتوزيع حركة المرور تلقائيًا عبر النسخ المتعددة.
📖 ملخص
- Profiles: إضافة أو إزالة الخدمات في نفس الملف بناءً على البيئة؛
--profile devللتفعيل - ملف Override:
-f base.yml -f override.ymlتكوين مدمج، مناسب لعدد كبير من الاختلافات - التوسع:
--scale api=3أوdeploy.replicas: 3توسع أفقي للخدمات عديمة الحالة - الخدمة الموسعة لا يمكنها التعيين لمنفذ مضيف — استخدم نقطة دخول Nginx + DNS round-robin داخلي
deploy.resourcesإعلان حدود واحتياطيات CPU/ذاكرةdepends_on: { condition: service_healthy }يضمن أن التبعيات جاهزة فعليًا
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): اكتب مجموعتين من ملفات التجاوز (واحدة لـ dev وواحدة لـ prod) للتطبيق من الدرس 12؛ أضف إعادة تحميل تلقائية لـ dev وحدود موارد لـ prod.
- تمرين متقدم (الصعوبة: ⭐⭐): استخدم
--scale api=3لتوسيع خدمة API إلى 3 نسخ، واستخدمdocker compose psللتحقق. - تحدٍ (الصعوبة: ⭐⭐⭐): أعد وكيل Nginx عكسيًا لـ API مع 3 نسخ، وتحقق من أن الطلبات موزعة على نسخ مختلفة (بفحص سجلات الحاويات المختلفة).