404 Not Found

404 Not Found


nginx

نشر Docker — الحاويات والبناء متعدد المراحل

Docker مثل حاوية شحن — فهو يُغلف التطبيقات وجميع تبعياتها في حاويات قياسية يمكن تفريغها وتشغيلها مباشرة في أي منفذ (خادم)، مما يُزيل مشكلة "يعمل على جهازي".

1. ما ستتعلمه


2. القصة الحقيقية لـ Alice

(1) نقطة الألم: فشل النشر بسبب اختلافات البيئة

عندما نشر Charlie تطبيق PriceTracker — الذي كان قد إعداده محليًا — على خادم الإنتاج، واجه مشاكل مثل عدم تطابق إصدار Python، وغياب مكتبة libpq، وعدم تثبيت UV، واختلاف إعدادات اتصال PostgreSQL — مما أجبره على قضاء ساعتين في استكشاف الأخطاء يدويًا في كل نشر. وازداد الأمر سوءًا أن الخدمات الأربع — FastAPI و PostgreSQL و Redis و Celery — كان يجب بدؤها وإدارتها بشكل منفصل، وكان ترتيب بدء التشغيل معقدًا ومترابطًا.

(2) حل Docker Compose

يُعرّف Dockerfile حاويات التطبيق، بينما يُنسق Docker Compose جميع الخدمات وتبعياتها. أمر واحد docker compose up يُطلق مكدس الخدمة بالكامل، وتضمن الصورة تناسق البيئة.

(3) العائد

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


3. Dockerfile متعدد المراحل

(1) عملية البناء

100%
flowchart LR
    A[مرحلة البناء] -->|نسخ التبعيات المثبتة| B[مرحلة التشغيل]
    
    subgraph Builder
        A1[تثبيت تبعيات UV] --> A2[تجميع الحزم]
    end
    
    subgraph Runtime
        B1[صورة Python خفيفة] --> B2[نسخ كود التطبيق]
        B2 --> B3[نسخ التبعيات من البناء]
        B3 --> B4[تشغيل uvicorn]
    end

(1) ▶مثال: Dockerfile متعدد المراحل

DOCKERFILE
# === المرحلة 1: البناء ===
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder

WORKDIR /app

# نسخ ملفات التبعيات أولاً (طبقة التخزين المؤقت)
COPY pyproject.toml uv.lock ./

# تثبيت التبعيات في البيئة الافتراضية
RUN uv sync --frozen --no-dev --no-install-project

# نسخ كود التطبيق
COPY app/ app/

# === المرحلة 2: التشغيل ===
FROM python:3.12-slim-bookworm AS runtime

WORKDIR /app

# تثبيت تبعيات النظام للتشغيل
RUN apt-get update && \
    apt-get install -y --no-install-recommends libpq5 && \
    rm -rf /var/lib/apt/lists/*

# نسخ البيئة الافتراضية من مرحلة البناء
COPY --from=builder /app/.venv /app/.venv

# نسخ كود التطبيق
COPY app/ app/

# تعيين متغيرات البيئة
ENV PATH="/app/.venv/bin:$PATH" \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# فحص الصحة
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
    CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

# تشغيل التطبيق
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]

الناتج:

TEXT
// التنفيذ ناجح

(2) مقارنة أحجام الصور

الطريقة حجم الصورة الوصف
مرحلة واحدة (Python 3.12) ~1.2 جيجابايت يتضمن أدوات البناء والتخزين المؤقت
مرحلتين (Python: 3.12-slim) ~200 ميجابايت تبعيات التشغيل فقط
Alpine (python:3.12-alpine) ~80 ميجابايت أصغر لكن بها مشاكل توافق

4. تنسيق Docker Compose

(1) بنية المكدس الكامل

100%
flowchart TD
    Nginx[Nginx Proxy عكسي] --> API1[FastAPI Worker 1]
    Nginx --> API2[FastAPI Worker 2]
    API1 --> PG[(PostgreSQL)]
    API2 --> PG
    API1 --> Redis[(Redis)]
    API2 --> Redis
    API1 --> Broker[Redis Broker]
    API2 --> Broker
    Broker --> CW1[Celery Worker 1]
    Broker --> CW2[Celery Worker 2]
    CW1 --> PG
    CW2 --> PG
    CW1 --> Redis
    CW2 --> Redis
    Flower[Flower Monitor] --> Broker

(1) ▶مثال: docker-compose.yml

YAML
version: "3.8"

services:
  api:
    build:
      context: .
      dockerfile: docker/Dockerfile
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql+asyncpg://pricetracker:${DB_PASSWORD}@postgres:5432/pricetracker
      - REDIS_URL=redis://redis:6379/0
      - CELERY_BROKER_URL=redis://redis:6379/1
      - SECRET_KEY=${SECRET_KEY}
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"]
      interval: 30s
      timeout: 10s
      retries: 3

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: pricetracker
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: pricetracker
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U pricetracker"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5

  celery-worker:
    build:
      context: .
      dockerfile: docker/Dockerfile
    command: celery -A app.core.celery_app worker --loglevel=info --concurrency=4
    environment:
      - DATABASE_URL=postgresql+asyncpg://pricetracker:${DB_PASSWORD}@postgres:5432/pricetracker
      - REDIS_URL=redis://redis:6379/0
      - CELERY_BROKER_URL=redis://redis:6379/1
    depends_on:
      - redis
      - postgres

  flower:
    build:
      context: .
      dockerfile: docker/Dockerfile
    command: celery -A app.core.celery_app flower --port=5555
    ports:
      - "5555:5555"
    depends_on:
      - redis

volumes:
  postgres_data:
  redis_data:

الناتج:

TEXT
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(2) ▶مثال: ملف .env

INI
# .env - يقرأه Docker Compose تلقائيًا
DB_PASSWORD=change-me-in-production
SECRET_KEY=your-very-long-random-secret-key-at-least-32-chars
CELERY_BROKER_URL=redis://redis:6379/1

الناتج:

TEXT
// التنفيذ ناجح

5. فحوصات الصحة وترتيب الإقلاع

(1) depends_on + healthcheck

يضمن depends_on + condition: service_healthy في Docker Compose ترتيب بدء تشغيل الخدمات الصحيح: تصبح PostgreSQL و Redis جاهزتين أولاً، ثم يبدأ FastAPI.

الخدمات ترتيب البدء فحوصات الصحة
PostgreSQL 1 (الأول) pg_isready -U pricetracker
Redis 1 (الأول) redis-cli ping
FastAPI 2 (بعد جاهزية PG+Redis) GET /health
Celery Worker 3 (بعد جاهزية Redis و PostgreSQL) نبض داخلي

(1) ▶مثال: نقطة نهاية فحص صحة FastAPI

PYTHON
@app.get("/health")
async def health_check(db: AsyncSession = Depends(get_db), redis: Redis = Depends(get_redis)):
    # فحص اتصال قاعدة البيانات
    try:
        await db.execute(select(1))
        db_status = "healthy"
    except Exception:
        db_status = "unhealthy"

    # فحص اتصال Redis
    try:
        await redis.ping()
        redis_status = "healthy"
    except Exception:
        redis_status = "unhealthy"

    overall = "healthy" if db_status == "healthy" and redis_status == "healthy" else "unhealthy"
    return {
        "status": overall,
        "database": db_status,
        "redis": redis_status,
    }

الناتج:

TEXT
# تم تعريف الدالة بنجاح

❓أسئلة شائعة

س لماذا نستخدم البناء متعدد المراحل؟
ج تُستخدم صورة Python كاملة (بما فيها المترجم) خلال مرحلة البناء، بينما تُستخدم صورة خفيفة في وقت التشغيل. الصورة النهائية لا تحتوي على أي أدوات بناء، وهي أصغر بـ 5 إلى 10 مرات، ولها سطح هجوم أصغر.
س هل Docker Compose مناسب للإنتاج؟
ج مناسب للمشاريع الصغيرة والمتوسطة. لبيئات الإنتاج الكبيرة، استخدم Kubernetes أو Docker Swarm. Compose مناسب للتطوير والاختبار والنشر على نطاق صغير.
س كيف أتعامل مع ترحيلات قاعدة البيانات؟
ج شغّل الترحيلات قبل بدء حاوية API. أضف حاوية init، أو في entrypoint.sh، شغّل alembic upgrade head أولاً قبل بدء uvicorn.
س كيف تُنقل الأسرار بأمان؟
ج للتطوير، استخدم ملفات .env (أضفها إلى .gitignore). للإنتاج، استخدم Docker secrets أو K8s Secrets؛ لا تُصلبها برمجيًا ولا تُرسلها إلى Git.
س كيف أحدد عدد Workers؟
ج المعادلة: (2 x أنوية المعالج) + 1. لخادم بـ 4 أنوية، عيّن 9 Workers. لكن يجب أيضًا مراعاة حدود الذاكرة؛ كل Worker يستخدم 50-100 ميجابايت.
س كيف أعرض سجلات الحاوية؟
ج docker compose logs api لعرض سجلات FastAPI، docker compose logs -f للمراقبة في الوقت الفعلي. في بيئات الإنتاج، استخدم ELK/Loki لتجميع السجلات.

📖ملخص


📝تمارين

  1. سؤال أساسي (الصعوبة ⭐): اكتب Dockerfile بمرحلة واحدة لـ PriceTracker، ابنِ الصورة، شغّل الحاوية، وتحقق من إمكانية الوصول إلى نقطة النهاية /health. تلميح: FROM python:3.12-slim + CMD ["uvicorn", ...]
  2. تمرين متقدم (الصعوبة ⭐⭐): عدّل Dockerfile لاستخدام بناء متعدد المراحل، واكتب ملف docker-compose.yml يتضمن ثلاث خدمات: API و PostgreSQL و Redis، بحيث يمكن بدؤها بأمر واحد: docker compose up. تلميح: AS builder + COPY --from=builder
  3. تحدٍ (الصعوبة ⭐⭐⭐): أكمل تنسيق Docker Compose — أضف خدمتي Celery Worker و Flower، أضف فحص صحة، عيّن كلمة مرور لملف .env، وأضف entrypoint.sh لتشغيل alembic upgrade head قبل البدء. تلميح: depends_on + condition: service_healthy + سكريبت entrypoint

---|

Web-Tutorial.com

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

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

100%