انتقل إلى المحتوى الرئيسي

Docker Compose للتطوير: البنية التحتية المحلية

Docker Compose للتطوير المحلي: PostgreSQL، وRedis، وAuthentik، والشبكات، والفحوصات الصحية، وإدارة الحجم، والتكوينات الخاصة بالبيئة لـ TypeScript monorepos.

E
ECOSIRE Research and Development Team
|19 مارس 202610 دقائق قراءة1.9k كلمات|

الفرق بين تجربة الإعداد الممتعة ("استنساخ وتشغيل pnpm dev:infra") والتجربة المؤلمة ("قم أولاً بإعداد PostgreSQL، ثم قم بتكوين Redis، ثم...") يعود إلى مدى نجاح إعداد Docker Compose في التقاط متطلبات البنية التحتية الخاصة بك.

الفرق بين تجربة الإعداد الممتعة ("استنساخ وتشغيل pnpm dev:infra") والتجربة المؤلمة ("قم أولاً بإعداد PostgreSQL، ثم قم بتكوين Redis، ثم...") يعود إلى مدى نجاح إعداد Docker Compose في التقاط متطلبات البنية التحتية الخاصة بك. يتيح docker-compose.dev.yml المصمم جيدًا لأي مطور على أي جهاز الحصول على نفس البنية الأساسية التي تعمل في دقائق.

يغطي هذا الدليل أنماط حزمة التطوير المحلية ذات جودة الإنتاج: تكوين الخدمة، والفحوصات الصحية، والشبكات، وإدارة الحجم، والتكامل مع تسلسل بدء تشغيل التطبيق الخاص بك.

الوجبات الرئيسية

  • استخدم منفذًا غير افتراضي لـ PostgreSQL محليًا (5433) لتجنب التعارضات مع عمليات تثبيت النظام
  • تمنع عمليات التحقق من سلامة تبعيات الخدمة حدوث أخطاء عند بدء التشغيل "رفض الاتصال".
  • تحافظ وحدات التخزين المسماة على بيانات قاعدة البيانات بين عمليات إعادة تشغيل الحاوية - لا تعمل عمليات ربط الربط بشكل موثوق على نظام التشغيل Windows
  • استخدم env_file لتحميل متغيرات البيئة من ملف .env.local الخاص بك إلى الحاويات
  • افصل docker-compose.dev.yml عن docker-compose.prod.yml — فهي تخدم أغراضًا مختلفة
  • ينتظر النمط depends_on.condition: service_healthy الاستعداد الفعلي، وليس فقط بدء الحاوية
  • استخدم profiles للاشتراك في الخدمات الاختيارية (البريد الإلكتروني والمراقبة).
  • قم بتشغيل docker compose (v2) وليس docker-compose (v1) — صيغة البرنامج الإضافي حالية

حزمة التطوير الكاملة

# infrastructure/docker-compose.dev.yml
name: ecosire-dev

services:
  # ─── PostgreSQL ─────────────────────────────────────────────────
  postgres:
    image: postgres:17-alpine
    container_name: ecosire-postgres
    environment:
      POSTGRES_DB: ecosire_dev
      POSTGRES_USER: ecosire
      POSTGRES_PASSWORD: dev_password_change_in_prod
    ports:
      - "5433:5432"  # 5433 externally — avoids conflicts with system PostgreSQL
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init-scripts:/docker-entrypoint-initdb.d  # Run SQL on first start
    command: >
      postgres
        -c shared_buffers=256MB
        -c effective_cache_size=1GB
        -c work_mem=16MB
        -c maintenance_work_mem=128MB
        -c checkpoint_completion_target=0.9
        -c wal_buffers=16MB
        -c max_connections=100
        -c log_min_duration_statement=100
        -c log_statement=ddl
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ecosire -d ecosire_dev"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped

  # ─── Redis ──────────────────────────────────────────────────────
  redis:
    image: redis:7-alpine
    container_name: ecosire-redis
    ports:
      - "6379:6379"
    volumes:
      - redis_data:/data
    command: >
      redis-server
        --maxmemory 512mb
        --maxmemory-policy allkeys-lru
        --appendonly yes
        --appendfsync everysec
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

  # ─── Authentik (Identity Provider) ──────────────────────────────
  authentik-server:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: ecosire-authentik
    command: server
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: ecosire
      AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
      AUTHENTIK_ERROR_REPORTING__ENABLED: "false"
      AUTHENTIK_DISABLE_STARTUP_ANALYTICS: "true"
    volumes:
      - authentik_media:/media
      - authentik_certs:/certs
    ports:
      - "9000:9000"  # HTTP
      - "9443:9443"  # HTTPS
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "ak healthcheck"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s  # Authentik takes time to initialize
    restart: unless-stopped

  authentik-worker:
    image: ghcr.io/goauthentik/server:2024.12
    container_name: ecosire-authentik-worker
    command: worker
    environment:
      AUTHENTIK_REDIS__HOST: redis
      AUTHENTIK_POSTGRESQL__HOST: postgres
      AUTHENTIK_POSTGRESQL__USER: ecosire
      AUTHENTIK_POSTGRESQL__PASSWORD: dev_password_change_in_prod
      AUTHENTIK_POSTGRESQL__NAME: authentik
      AUTHENTIK_SECRET_KEY: dev-secret-key-change-in-production-32chars
    volumes:
      - authentik_media:/media
      - authentik_certs:/certs
      - /var/run/docker.sock:/var/run/docker.sock  # For Authentik's proxy
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped

  # ─── Mailpit (Email Testing) ─────────────────────────────────────
  mailpit:
    image: axllent/mailpit:latest
    container_name: ecosire-mailpit
    ports:
      - "1025:1025"  # SMTP
      - "8025:8025"  # Web UI
    environment:
      MP_MAX_MESSAGES: 200
      MP_SMTP_AUTH_ACCEPT_ANY: true
      MP_SMTP_AUTH_ALLOW_INSECURE: true
    restart: unless-stopped
    profiles:
      - email  # Optional — use `docker compose --profile email up`

  # ─── pgAdmin (Database GUI) ─────────────────────────────────────
  pgadmin:
    image: dpage/pgadmin4:latest
    container_name: ecosire-pgadmin
    environment:
      PGADMIN_DEFAULT_EMAIL: admin@local.dev
      PGADMIN_DEFAULT_PASSWORD: admin
      PGADMIN_CONFIG_SERVER_MODE: "False"
      PGADMIN_CONFIG_MASTER_PASSWORD_REQUIRED: "False"
    ports:
      - "5050:80"
    volumes:
      - pgadmin_data:/var/lib/pgadmin
    depends_on:
      postgres:
        condition: service_healthy
    restart: unless-stopped
    profiles:
      - tools  # Optional

networks:
  default:
    name: ecosire-dev-network

volumes:
  postgres_data:
    name: ecosire-postgres-data
  redis_data:
    name: ecosire-redis-data
  authentik_media:
    name: ecosire-authentik-media
  authentik_certs:
    name: ecosire-authentik-certs
  pgadmin_data:
    name: ecosire-pgadmin-data

البرامج النصية Package.json

قم بتوصيل أوامر Docker Compose إلى البرامج النصية الخاصة بـ monorepo:

{
  "scripts": {
    "dev:infra": "docker compose -f infrastructure/docker-compose.dev.yml up -d",
    "dev:infra:down": "docker compose -f infrastructure/docker-compose.dev.yml down",
    "dev:infra:logs": "docker compose -f infrastructure/docker-compose.dev.yml logs -f",
    "dev:infra:reset": "docker compose -f infrastructure/docker-compose.dev.yml down -v && pnpm dev:infra",
    "dev:infra:email": "docker compose -f infrastructure/docker-compose.dev.yml --profile email up -d",
    "dev:infra:tools": "docker compose -f infrastructure/docker-compose.dev.yml --profile tools up -d"
  }
}

تتيح العلامة --profile للخدمات الاختيارية (اختبار البريد الإلكتروني باستخدام Mailpit، وواجهة المستخدم الرسومية لقاعدة البيانات مع pgAdmin) البقاء خاملة حتى يتم طلبها بشكل صريح.


البرامج النصية لتهيئة قاعدة البيانات

ضع ملفات SQL في infrastructure/init-scripts/ - حيث يتم تشغيلها عند بداية الحاوية الأولى:

-- infrastructure/init-scripts/01-create-databases.sql
-- Create all databases Authentik needs separately from the app DB
CREATE DATABASE authentik;
GRANT ALL PRIVILEGES ON DATABASE authentik TO ecosire;

-- Create test database for CI
CREATE DATABASE ecosire_test;
GRANT ALL PRIVILEGES ON DATABASE ecosire_test TO ecosire;
-- infrastructure/init-scripts/02-extensions.sql
-- Enable PostgreSQL extensions
\c ecosire_dev;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm";  -- Trigram search
CREATE EXTENSION IF NOT EXISTS "btree_gin"; -- Composite GIN indexes

\c ecosire_test;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

يتم تشغيل البرامج النصية للتهيئة بالترتيب الأبجدي. يقوم \c database_name psql metacommand بتبديل قاعدة البيانات النشطة.


تكامل متغيرات البيئة

يقرأ تطبيقك من .env.local في جذر monorepo. تحتاج خدمات Docker إلى معرفة كيفية الاتصال ببعضها البعض باستخدام أسماء الخدمات (وليس localhost):

# .env.local (monorepo root)

# PostgreSQL — use 5433 externally (host) or 5432 internally (container network)
DATABASE_URL=postgresql://ecosire:dev_password_change_in_prod@localhost:5433/ecosire_dev

# Redis
REDIS_URL=redis://localhost:6379

# Authentik — use 9000 for external calls from your dev machine
AUTHENTIK_URL=http://localhost:9000
# Use service name for server-to-server calls within Docker network
AUTHENTIK_INTERNAL_URL=http://authentik-server:9000

# Email (Mailpit SMTP)
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_SECURE=false

# Application
NODE_ENV=development

بالنسبة للتطبيقات التي تعمل داخل Docker والتي تحتاج إلى التواصل مع خدمات أخرى، استخدم أسماء الخدمات. بالنسبة للتطبيقات التي تعمل على جهازك المضيف (NestJS وNext.js في وضع التطوير)، استخدم localhost مع المنافذ المعينة للمضيف.


الغوص العميق في الفحوصات الصحية

تعمل عمليات التحقق من السلامة على منع حالات فشل بدء التشغيل المتتالية. ينتظر depends_on.condition: service_healthy الاستعداد الفعلي، وليس فقط بدء تشغيل الحاوية:

# Without health checks — can fail because PostgreSQL isn't ready
depends_on:
  - postgres

# With health checks — waits for PostgreSQL to accept connections
depends_on:
  postgres:
    condition: service_healthy

فحوصات صحية مخصصة لخدماتك الخاصة:

// apps/api/src/health/health.controller.ts
@Get()
@Public()
@HealthCheck()
async check() {
  return this.health.check([
    () => this.db.isHealthy('database'),
    () => this.redis.isHealthy('redis'),
  ]);
}
# If your API is also dockerized
api:
  image: ecosire-api:latest
  healthcheck:
    test: ["CMD", "curl", "-f", "http://localhost:3001/api/health"]
    interval: 30s
    timeout: 10s
    retries: 3
    start_period: 30s

إدارة الحجم

تحافظ وحدات التخزين المسماة على البيانات بين عمليات إعادة التشغيل. افهم متى تستخدم كل نوع من أنواع المجلدات:

اكتبالثباتالأداءاستخدم لـ
المجلد المسمىنعمممتازبيانات قاعدة البيانات
ربط جبلنعمجيد (لينكس)، ضعيف (ماك)كود المصدر الساخن إعادة التحميل
تمبفسلاممتازملفات مؤقتة أسرار
# Use bind mounts for source code (enables hot reload)
volumes:
  - ./apps/api/src:/app/src  # Code changes reflected immediately

# Use named volumes for data
volumes:
  - postgres_data:/var/lib/postgresql/data

# Use tmpfs for ephemeral data
volumes:
  - type: tmpfs
    target: /tmp

في نظام التشغيل macOS المزود بـ Docker Desktop، تستخدم عمليات الربط gRPC FUSE وهو أبطأ بشكل ملحوظ من Linux. بالنسبة لخوادم NestJS وNext.js dev، قم بتشغيلها مباشرة على جهازك المضيف (وليس في Docker) للحصول على أداء نظام الملفات الأصلي.


إنتاج عامل الميناء يؤلف

يختلف ملف إنشاء الإنتاج من الناحية الهيكلية — لا توجد منافذ محلية، ولا سياسات إعادة التشغيل، وحدود موارد الإنتاج:

# infrastructure/docker-compose.prod.yml
name: ecosire-prod

services:
  postgres:
    image: postgres:17-alpine
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    # No port mapping — only accessible within Docker network
    restart: always
    deploy:
      resources:
        limits:
          memory: 2G
        reservations:
          memory: 512M

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 1gb
    volumes:
      - redis_data:/data
    restart: always
    # No port mapping — internal only

volumes:
  postgres_data:
  redis_data:

لا يعرض الإنتاج المنافذ خارجيًا - تتصل التطبيقات عبر شبكة Docker الداخلية. يتعامل Nginx مع حركة المرور الخارجية.


المخاطر والحلول الشائعة

المأزق 1: يتعارض المنفذ مع خدمات النظام

غالبًا ما تعمل خدمات PostgreSQL وRedis وغيرها كخدمات نظام. قم دائمًا بالتخطيط للمنافذ غير القياسية قيد التطوير:

  • PostgreSQL: 5433:5432 (وليس 5432:5432)
  • Redis: احتفظ بـ 6379:6379 (نادرًا ما يتعارض)
  • قم بتشغيل lsof -i :5432 للتحقق مما يستخدم المنفذ الافتراضي

المأزق 2: مشكلات أذونات وحدة التخزين على Linux

تستخدم وحدات تخزين Docker على Linux ملكية الجذر افتراضيًا. إذا كان مستخدم الحاوية الخاص بك ليس جذرًا، فقم بتعيين الملكية الصحيحة:

postgres:
  image: postgres:17-alpine
  user: "999:999"  # postgres user's UID:GID
  # Or use init container to fix permissions

المأزق 3: تستغرق عملية تهيئة Authentik أكثر من 60 ثانية

يقوم Authentik بتشغيل عمليات ترحيل قاعدة البيانات عند البداية الأولى. start_period: 60s في الفحص الصحي يمنحه الوقت. إذا بدأت الخدمات التابعة قبل أن يصبح Authentik جاهزًا، فسوف تفشل. استخدم الشرط service_healthy واعطه ما يكفي من start_period.

المأزق 4: حدود موارد Docker Desktop على نظام Mac

يخصص Docker Desktop وحدتي معالجة مركزية وذاكرة وصول عشوائي (RAM) سعة 2 جيجابايت - وهو ما لا يكفي لتشغيل PostgreSQL + Redis + Authentik في وقت واحد. قم بزيادة إعدادات Docker Desktop > الموارد إلى 4 وحدات معالجة مركزية (CPU) وذاكرة وصول عشوائي (RAM) سعة 6 جيجابايت على الأقل.

المأزق 5: docker-compose مقابل docker compose

تم إهمال docker-compose القديم (الإصدار 1، المكتوب بلغة Python). استخدم docker compose (الإصدار 2، البرنامج الإضافي). تحقق من الإصدار الخاص بك: docker compose version. إذا رأيت Docker Compose version v2.x.x، فأنت تستخدم الإصدار 2.


الأسئلة المتداولة

هل يجب أن أقوم بتشغيل خدمات التطبيق الخاصة بي (NestJS، Next.js) في Docker أثناء التطوير؟

بشكل عام لا - للتطوير النشط، قم بتشغيل خدمات التطبيقات الخاصة بك على جهازك المضيف من أجل إعادة التحميل السريع بشكل أسرع وتصحيح الأخطاء بشكل أسهل. استخدم Docker فقط لخدمات البنية التحتية (قواعد البيانات، وذاكرات التخزين المؤقت، وموفري الهوية) التي تكون مستقرة ولا تحتاج إلى إعادة تشغيل متكررة. الاستثناء هو إذا كان التطبيق الخاص بك يحتوي على تبعيات أصلية تختلف بين نظام التشغيل الخاص بالتطوير وبيئة الإنتاج.

كيف أتعامل مع عمليات ترحيل قاعدة البيانات في سير عمل Docker Compose؟

قم بتشغيل عمليات الترحيل من جهازك المضيف بعد بدء البنية الأساسية: pnpm dev:infra && pnpm db:migrate. لا تقم بتشغيل عمليات الترحيل داخل حاوية Docker أثناء التطوير — ستفقد التحقق من النوع وتكامل IDE الذي يجعل عمليات ترحيل Drizzle آمنة. لإنشاء قاعدة البيانات الأولية، استخدم البرامج النصية initdb.d الخاصة بـ Docker.

كيف يمكنني عمل نسخة احتياطية واستعادة وحدات تخزين Docker المحلية الخاصة بي؟

استخدم docker run --rm -v postgres_data:/data -v $(pwd):/backup alpine tar czf /backup/postgres-backup.tar.gz /data لإجراء النسخ الاحتياطي. قم بالاستعادة بنفس الطريقة باستخدام tar xzf. للتطوير، يمكنك أيضًا التفريغ باستخدام pg_dump والاستعادة باستخدام psql نظرًا لأن المنفذ مكشوف لديك.

كيف يمكنني مشاركة حالة Docker Compose مع أعضاء الفريق الآخرين؟

تتم مشاركة ملف Docker Compose عبر git، لكن البيانات الموجودة في المجلدات محلية. يبدأ كل مطور بقاعدة بيانات فارغة ويقوم بتشغيل عمليات الترحيل/البذور لملءها. استخدم البرامج النصية الأولية (المخصصة للريبو) لإنشاء بيانات اختبار متسقة. يضمن docker-compose.dev.yml المشترك أن يستخدم الجميع نفس إصدارات الخدمة وتكوينها.

لماذا نستخدم Mailpit بدلاً من البريد الإلكتروني الحقيقي في التطوير؟

Mailpit هو خادم SMTP محلي يلتقط جميع رسائل البريد الإلكتروني الصادرة ويوفر واجهة مستخدم ويب لعرضها. فهو يمنع إرسال رسائل بريد إلكتروني حقيقية عن طريق الخطأ إلى مستخدمين حقيقيين أثناء التطوير، ولا يتطلب بيانات اعتماد SMTP، ويتيح لك التحقق من قوالب البريد الإلكتروني دون التحقق من صندوق الوارد الخاص بك. قم بتكوين تطبيقك لاستخدام SMTP_HOST=localhost SMTP_PORT=1025 وقم بزيارة http://localhost:8025 لرؤية رسائل البريد الإلكتروني التي تم التقاطها.


الخطوات التالية

يُعد إعداد Docker Compose المصمم جيدًا للتطوير المحلي استثمارًا يؤتي ثماره في كل مرة ينضم فيها مطور جديد أو تقوم بتشغيل جهاز جديد. يقوم ECOSIRE بتشغيل PostgreSQL 17 وRedis 7 وAuthentik في Docker Compose للتطوير المحلي عبر الفريق بأكمله.

هل تحتاج إلى مساعدة في تصميم البنية التحتية للتطوير المحلي لديك أو نقل تطبيقك إلى حاوية للإنتاج؟ استكشف خدمات DevOps لمعرفة كيف يمكننا مساعدتك.

E

بقلم

ECOSIRE Research and Development Team

الكتابة التقنية

يغطي فريق الكتابة التقنية في ECOSIRE موضوعات Odoo ERP والتجارة الإلكترونية عبر Shopify ووكلاء الذكاء الاصطناعي وتحليلات Power BI والأتمتة عبر GoHighLevel وأفضل ممارسات برمجيات المؤسسات. تساعد أدلتنا الشركات على اتخاذ قرارات تقنية مدروسة.

ECOSIRE

قم بتنمية أعمالك مع ECOSIRE

حلول المؤسسات عبر تخطيط موارد المؤسسات (ERP) والتجارة الإلكترونية والذكاء الاصطناعي والتحليلات والأتمتة.