Docker لنشر تخطيط موارد المؤسسات للإنتاج: دليل العمليات الكامل

نشر أنظمة تخطيط موارد المؤسسات (ERP) مع Docker في الإنتاج. يغطي عمليات الإنشاء متعددة المراحل وتنسيق Docker Compose وإدارة الحجم والشبكات واستراتيجيات التوسع.

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

Docker لنشر تخطيط موارد المؤسسات (ERP) للإنتاج: دليل العمليات الكامل

**تعلن المؤسسات التي تستخدم أنظمة ERP في حاويات Docker عن دورات نشر أسرع بنسبة 73% وحوادث أقل تتعلق بالبيئة بنسبة 45% مقارنة بعمليات النشر التقليدية. ** تعمل Docker على تحويل نشر ERP من عملية متعددة الأيام وعرضة للأخطاء إلى عملية قابلة للتكرار يتم التحكم فيها بالإصدار ويمكن لأي عضو في الفريق تنفيذها.

يغطي هذا الدليل دورة الحياة الكاملة لتشغيل أنظمة ERP الخاصة بالمؤسسات --- بما في ذلك Odoo، والواجهات الخلفية لـ NestJS المخصصة، والواجهات الأمامية لـ Next.js --- في بيئات Docker للإنتاج.

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

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

بنية مكدس ERP Dockerized

يتضمن نشر تخطيط موارد المؤسسات (ERP) الإنتاجي عادةً خمس خدمات مترابطة أو أكثر. يحدد Docker Compose هذه الخدمات بشكل صريح، مما يضمن النشر المتسق عبر البيئات.

طبولوجيا الخدمة

مكدس Dockerized ERP القياسي:

  1. خادم التطبيقات: وقت تشغيل ERP (Odoo، أو NestJS، أو ما شابه ذلك)
  2. قاعدة البيانات: PostgreSQL مع وحدة تخزين ثابتة
  3. الوكيل العكسي: يتعامل Nginx مع إنهاء SSL والملفات الثابتة وتوجيه الطلب
  4. طبقة ذاكرة التخزين المؤقت: Redis لتخزين الجلسة وقوائم انتظار المهام والتخزين المؤقت للتطبيق
  5. العاملون في الخلفية: معالجات المهام غير المتزامنة لرسائل البريد الإلكتروني والتقارير وعمليات التكامل

تتضمن الخدمات الاختيارية حاويات النسخ الاحتياطي (pg_dump on cron)، ومراقبة السيارات الجانبية (مصدرو Prometheus)، وشركات شحن السجلات (Fluent Bit).


تصميمات متعددة المراحل لتطبيقات تخطيط موارد المؤسسات (ERP).

تعد عمليات البناء متعددة المراحل ضرورية لإنتاج صور Docker. فهي تفصل تبعيات وقت البناء عن وقت التشغيل، وتنتج صورًا بسيطة وآمنة.

بناء الواجهة الخلفية لـ NestJS

# Stage 1: Install dependencies and build
FROM node:20-alpine AS builder
WORKDIR /app

# Install pnpm
RUN corepack enable

# Copy workspace configuration
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/api/package.json ./apps/api/

# Install dependencies
RUN pnpm install --frozen-lockfile

# Copy source and build
COPY apps/api/ ./apps/api/
RUN pnpm --filter @ecosire/db build
RUN pnpm --filter @ecosire/types build
RUN pnpm --filter @ecosire/validators build
RUN pnpm --filter @ecosire/api build

# Stage 2: Production runtime
FROM node:20-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

COPY --from=builder --chown=appuser:appgroup /app/apps/api/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/apps/api/package.json ./

USER appuser
EXPOSE 3001

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1

CMD ["node", "dist/main.js"]

بناء الواجهة الأمامية Next.js

FROM node:20-alpine AS builder
WORKDIR /app
RUN corepack enable

COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/ ./packages/
COPY apps/web/package.json ./apps/web/

RUN pnpm install --frozen-lockfile

COPY apps/web/ ./apps/web/
RUN pnpm --filter @ecosire/web build

FROM node:20-alpine AS runner
WORKDIR /app

RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/standalone ./
COPY --from=builder --chown=appuser:appgroup /app/apps/web/.next/static ./.next/static
COPY --from=builder --chown=appuser:appgroup /app/apps/web/public ./public

USER appuser
EXPOSE 3000
ENV NODE_ENV=production
CMD ["node", "server.js"]

مقارنة حجم الصورة

نوع البناءحجم الصورةوقت البناء
مرحلة واحدة (صورة العقدة الكاملة)1.8 جيجا4 دقائق
مرحلة واحدة (جبال الألب)650 ميجا3.5 دقيقة
متعدد المراحل (جبال الألب)180 ميجا5 دقائق
متعدد المراحل + قطع مشذبة120 ميجا5.5 دقيقة

يعد وقت البناء الذي يبلغ 5.5 دقيقة مقبولًا لأنه يحدث في CI، وليس على أجهزة المطورين.


Docker Compose للإنتاج

version: "3.8"

services:
  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    environment:
      - DATABASE_URL=postgresql://app:${DB_PASSWORD}@db:5432/ecosire
      - REDIS_URL=redis://redis:6379
      - NODE_ENV=production
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - backend
      - frontend

  web:
    build:
      context: .
      dockerfile: apps/web/Dockerfile
    environment:
      - API_URL=http://api:3001
      - NODE_ENV=production
    depends_on:
      - api
    restart: unless-stopped
    networks:
      - frontend

  db:
    image: postgres:17-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ecosire
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U app -d ecosire"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis-data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - backend

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./infrastructure/nginx/production.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certbot/conf:/etc/letsencrypt:ro
      - ./certbot/www:/var/www/certbot:ro
    depends_on:
      - web
      - api
    restart: unless-stopped
    networks:
      - frontend

volumes:
  postgres-data:
  redis-data:

networks:
  frontend:
  backend:

عزل الشبكة

يستخدم التكوين أعلاه شبكتين:

  • الواجهة الأمامية: Nginx، والويب، وAPI (وكلاء Nginx لكليهما)
  • الواجهة الخلفية: واجهة برمجة التطبيقات (API)، وقاعدة البيانات، وRedis

لا يمكن الوصول إلى قاعدة البيانات وRedis من حاوية Nginx أو الشبكة الخارجية. يعد تجزئة الشبكة ممارسة أمنية مهمة.


إدارة الحجم واستمرارية البيانات

تعد وحدات التخزين الجزء الأكثر أهمية في نشر Dockerized ERP. تفقد وحدات التخزين الخاصة بك وتفقد البيانات الخاصة بك.

أنواع الحجم

اكتبحالة الاستخدامالثباتالأداء
مجلدات مسماةقاعدة بيانات ريديسينجو من إزالة الحاوياتسرعة نظام الملفات الأصلية
ربط يتصاعدملفات التكوين والسجلاتمرتبطة بنظام الملفات المضيفسرعة نظام الملفات الأصلية
يتصاعد tmpfsملفات مؤقتة، أسرارالذاكرة فقط، فقدت عند إعادة التشغيلسرعة الذاكرة

استراتيجية النسخ الاحتياطي لوحدات تخزين Docker

#!/bin/bash
# backup-volumes.sh - Run via cron every 6 hours

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/opt/backups"

# Stop the application briefly for consistent backup
docker compose stop api web

# Backup PostgreSQL
docker compose exec -T db pg_dump -U app ecosire | gzip > "$BACKUP_DIR/db_$TIMESTAMP.sql.gz"

# Backup Redis
docker compose exec -T redis redis-cli -a "$REDIS_PASSWORD" BGSAVE
sleep 5
docker cp $(docker compose ps -q redis):/data/dump.rdb "$BACKUP_DIR/redis_$TIMESTAMP.rdb"

# Restart services
docker compose start api web

# Upload to S3
aws s3 sync "$BACKUP_DIR" "s3://company-backups/docker-volumes/" --exclude "*.tmp"

# Retain 30 days locally
find "$BACKUP_DIR" -name "*.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "*.rdb" -mtime +30 -delete

عمليات التحقق من الصحة وسياسات إعادة التشغيل

يجب على حاويات الإنتاج الإبلاغ عن حالتها الصحية والتعافي من الأعطال تلقائيًا.

نقطة نهاية التحقق من صحة التطبيق

// health.controller.ts
@Controller('health')
export class HealthController {
  constructor(
    private readonly db: DatabaseService,
    private readonly redis: RedisService,
  ) {}

  @Get()
  @Public()
  async check() {
    const checks = {
      database: await this.checkDatabase(),
      redis: await this.checkRedis(),
      uptime: process.uptime(),
      memory: process.memoryUsage(),
    };

    const healthy = checks.database && checks.redis;
    return { status: healthy ? 'ok' : 'degraded', checks };
  }

  private async checkDatabase(): Promise<boolean> {
    try {
      await this.db.execute('SELECT 1');
      return true;
    } catch {
      return false;
    }
  }

  private async checkRedis(): Promise<boolean> {
    try {
      await this.redis.ping();
      return true;
    } catch {
      return false;
    }
  }
}

إعادة تشغيل اختيار السياسة

سياسةالسلوكحالة الاستخدام
noلا تقم بإعادة التشغيل أبدًاالتطوير، مهام لمرة واحدة
on-failureأعد التشغيل فقط عند الخروج غير الصفريعمال وظائف دفعة
alwaysأعد التشغيل دائمًا (بما في ذلك إعادة تشغيل برنامج Docker Daemon)خدمات الإنتاج
unless-stoppedمثل always ولكنه يحترم التوقفات اليدويةمعظم خدمات الإنتاج

استخدم unless-stopped لخدمات الإنتاج. يضمن ذلك إعادة تشغيل الحاويات بعد إعادة تشغيل الخادم أو إعادة تشغيل برنامج Docker الخفي، لكنه يحترم أوامر docker compose stop اليدوية أثناء الصيانة.


سير عمل النشر

التحديثات المستمرة باستخدام Docker Compose

#!/bin/bash
# deploy.sh - Zero-downtime deployment

set -e

echo "Pulling latest code..."
git pull origin main

echo "Building new images..."
docker compose build --no-cache api web

echo "Rolling update - API first..."
docker compose up -d --no-deps api
sleep 10

# Verify API health
if ! curl -sf http://localhost:3001/health > /dev/null; then
  echo "API health check failed, rolling back..."
  docker compose up -d --no-deps api
  exit 1
fi

echo "Rolling update - Web..."
docker compose up -d --no-deps web
sleep 5

# Verify Web health
if ! curl -sf http://localhost:3000 > /dev/null; then
  echo "Web health check failed, rolling back..."
  docker compose up -d --no-deps web
  exit 1
fi

echo "Deployment complete!"
docker compose ps

سلامة ترحيل قاعدة البيانات

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

# Run migrations before deploying new containers
docker compose run --rm api npx drizzle-kit push

# Then deploy the new version
docker compose up -d

يضمن هذا النمط أنه في حالة فشل الترحيل، يستمر الإصدار القديم في العمل دون أن يتأثر.


التسجيل والتصحيح

التسجيل المركزي

# Add to docker-compose.yml
services:
  api:
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
        labels: "service"
    labels:
      service: "ecosire-api"

أوامر التصحيح الشائعة

# View logs for a specific service
docker compose logs -f api --tail 100

# Execute a shell inside a running container
docker compose exec api sh

# View resource usage
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}"

# Inspect container networking
docker compose exec api ping db

# View container environment variables
docker compose exec api env | sort

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

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

قم بتشغيل عمليات الترحيل كخطوة منفصلة قبل نشر حاويات التطبيقات الجديدة. استخدم docker compose run --rm api npx drizzle-kit push (أو أمر ترحيل ORM الخاص بك) كخطوة ما قبل النشر. لا تقم مطلقًا بتضمين تنفيذ الترحيل في أمر بدء تشغيل الحاوية --- يجب ألا يمنع الترحيل الفاشل الإصدار الحالي من الاستمرار في التشغيل.

ما هو عبء أداء Docker؟

في نظام التشغيل Linux، يكون الحمل العام لأداء Docker ضئيلًا --- عادةً أقل من 2% لأحمال العمل المرتبطة بوحدة المعالجة المركزية ولا يوجد فرق قابل للقياس لأحمال العمل المرتبطة بالإدخال/الإخراج. في نظامي التشغيل macOS وWindows، يعمل Docker داخل جهاز افتراضي، مما يضيف 5-15% من النفقات العامة. بالنسبة للإنتاج (الذي يجب أن يكون Linux)، فإن Docker ليس مصدر قلق ذي معنى للأداء.

كيف ندير الأسرار في Docker؟

لا تضع أسرارًا أبدًا في ملفات Dockerfiles أو ملفات docker-compose.yml. استخدم ملفات متغيرات البيئة (.env) المستبعدة من التحكم في الإصدار، أو أسرار Docker (لوضع Swarm)، أو مديري السرية الخارجيين (AWS Secrets Manager، HashiCorp Vault). بالنسبة إلى Docker Compose، يعد الملف .env في جذر المشروع هو الطريقة الأبسط.

هل يجب أن نستخدم Docker Swarm أم Kubernetes؟

بالنسبة لمعظم عمليات نشر SMB ERP، يكون Docker Compose كافيًا. يضيف Docker Swarm تنسيقًا متعدد المضيفين مع الحد الأدنى من التعقيد. يعد Kubernetes مناسبًا عندما تحتاج إلى التوسع التلقائي أو سياسات الشبكات المعقدة أو إمكانات شبكة الخدمة. راجع دليل قياس Kubernetes ودليل بنية الخدمات الصغيرة للتعرف على أطر اتخاذ القرار.

كيف نتعامل مع وحدات Odoo المخصصة في Docker؟

قم بتثبيت الوحدات المخصصة كوحدة تخزين ربط تشير إلى دليل الوظائف الإضافية الخاص بك. في ملف Dockerfile، تأكد من تكوين مسار الوظائف الإضافية في odoo.conf. بالنسبة إلى CI/CD، أنشئ صورة Docker مخصصة يتم دمجها في الوحدات النمطية الخاصة بك، مما يضمن اتساق الإصدار. راجع دليل نشر Docker Odoo الموجود لدينا للتعرف على التكوين الخاص بـ Odoo.


ما يأتي بعد ذلك

Docker هو الأساس لنشر ERP الحديث. بمجرد استقرار المكدس الموجود في الحاوية، استكشف إستراتيجيات النشر بدون توقف، ومراقبة الإنتاج، والبنية الأساسية كرمز لإنشاء مسار عمليات مؤتمت بالكامل.

اتصل بـ ECOSIRE للحصول على استشارات نشر Docker، أو استكشف خدمات تنفيذ Odoo لنشر ERP المُدار بالكامل في حاويات.


تم النشر بواسطة ECOSIRE - لمساعدة الشركات على نشر برامج المؤسسة بثقة.

E

بقلم

ECOSIRE Research and Development Team

بناء منتجات رقمية بمستوى المؤسسات في ECOSIRE. مشاركة رؤى حول تكاملات Odoo وأتمتة التجارة الإلكترونية وحلول الأعمال المدعومة بالذكاء الاصطناعي.

الدردشة على الواتساب