Next.js: Docker للنشر الذاتي والاستضافة

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

يمنحك النشر الذاتي تحكما كاملا في بيئة تشغيل تطبيقك — عندما تمنعك متطلبات الامتثال أو التكلفة أو الشبكة من استخدام منصة سحابية، يكون Docker هو شريكك الأكثر موثوقية.

1. ما ستتعلمه



2. قصة حقيقية لمهندس DevOps

(1) نقطة الألم: العميل يطلب عدم نقل البيانات خارج البلاد

يعمل تشارلي في شركة SaaS تخدم المؤسسات المالية في الشرق الأوسط. يحتاج منتجهم TaskFlow إلى النشر في مركز بيانات محلي في المملكة العربية السعودية — يطلب العميل تخزين جميع بيانات المستخدمين فعليا داخل المملكة العربية السعودية.

لكن Vercel ليس لديها مركز بيانات في المملكة العربية السعودية. المشكلة التي يواجهها تشارلي:

المشكلة التأثير
الامتثال لسيادة البيانات المتطلبات التنظيمية المالية السعودية تمنع نقل البيانات للخارج
زمن انتقال الشبكة زمن الوصول من الخوادم الأوروبية > 200 مللي ثانية
الارتباط بالمزود (Vendor Lock-in) فاتورة Vercel الشهرية تتجاوز 2,000$
متطلبات الشبكة الداخلية يرغب العميل في نشر الحل على الشبكة الداخلية للمؤسسة

(2) حلول النشر الذاتي باستخدام Docker

بنى تشارلي حزم نشر محمولة باستخدام Docker:

BASH
# ابني مرة واحدة، وشغل في كل مكان
docker build -t taskflow:latest .
docker run -p 3000:3000 \
  -e DATABASE_URL="postgresql://..." \
  -e AUTH_SECRET="..." \
  taskflow:latest

(3) النتائج

البعد Vercel النشر الذاتي مع Docker
موقع البيانات مناطق Vercel فقط أي مركز بيانات
التكاليف الشهرية 2,000$+ 300$ (الخادم)
زمن انتقال النشر عالمي ~100 مللي ثانية محلي < 20 مللي ثانية
الارتباط بالمزود مرتفع منخفض (قابل للترحيل)


3. إعداد output: 'standalone'

ينشئ وضع output: 'standalone' في Next.js 16 خادم Node.js مستقلا يحتوي على جميع الملفات اللازمة لتشغيل التطبيق.

100%
graph TB
    A[next.config.js] --> B[output: 'standalone']
    B --> C[عملية البناء]
    C --> D[مجلد .next/standalone/]
    D --> E[server.js — خادم HTTP مستقل]
    D --> F[.next/static — موارد ثابتة]
    D --> G[node_modules — تبعيات مصغرة]
    D --> H[package.json — إعدادات الإدخال]
    
    style A fill:#cce5ff
    style D fill:#d4edda

(1) إعداد next.config.js

JS
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  
  // التبعيات التي تتطلب معالجة خارجية
  serverExternalPackages: ['@prisma/client'],
  
  // تحسين بيئة الإنتاج
  productionBrowserSourceMaps: false,
  swcMinify: true,
  
  // الاحتفاظ بتحسين الصور
  images: {
    unoptimized: false
  }
}

module.exports = nextConfig

(2) هيكل مجلد المخرجات المستقلة

TEXT 📖 للعرض فقط
.next/standalone/
├── server.js              # خادم HTTP مستقل (المدخل)
├── package.json           # تعريفات تبعيات وقت التشغيل
├── node_modules/          # تبعيات البناء فقط
├── .next/
│   ├── server/            # كود جانب الخادم
│   ├── static/            # موارد ثابتة
│   ├── build-manifest.json
│   └── ...
├── public/                # موارد ثابتة عامة
└── trace                  # تتبع البناء

▶ مثال: التحقق من البناء المستقل

BASH
# بناء المشروع
npm run build

# عرض حجم مجلد standalone
du -sh .next/standalone/

# تشغيل خادم مخصص
node .next/standalone/server.js

# التحقق في طرفية أخرى
curl http://localhost:3000
💻 المخرجات:

TEXT 📖 للعرض فقط
.next/standalone/    358M    # الحجم الإجمالي
.next/standalone/server.js   # ملف الإدخال (مولد تلقائيا)

المخرجات:

TEXT 📖 للعرض فقط
.next/standalone/    358M
node .next/standalone/server.js
  ▲ Next.js 16.0.0
  - Local: http://localhost:3000
  ✓ Ready in 1.2s


4. بناء Docker متعدد المراحل

يقسم البناء متعدد المراحل الصورة إلى ثلاث مراحل: تثبيت التبعيات ← بناء التطبيق ← بيئة تشغيل مصغرة.

DOCKERFILE
# ============================================
# Dockerfile — Next.js 16 بناء متعدد المراحل
# ============================================

# --- المرحلة 1: تثبيت التبعيات ---
FROM node:20-alpine AS deps
LABEL stage=deps

RUN apk add --no-cache libc6-compat

WORKDIR /app

COPY package.json package-lock.json pnpm-lock.yaml ./

RUN npm ci --only=production && \
    npm cache clean --force

# --- المرحلة 2: البناء ---
FROM node:20-alpine AS build
LABEL stage=build

WORKDIR /app

COPY --from=deps /app/node_modules ./node_modules
COPY . .

ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production

RUN npm run build

# --- المرحلة 3: التشغيل ---
FROM node:20-alpine AS runner
LABEL stage=runner

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

WORKDIR /app

# نسخ مخرجات البناء
COPY --from=build --chown=nextjs:nodejs \
    /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs \
    /app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs \
    /app/public ./public

# فحص الصحة
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV NODE_ENV=production

CMD ["node", "server.js"]

(1) البناء والتشغيل

BASH
# بناء الصورة
docker build -t taskflow:latest .

# عرض حجم الصورة
docker images taskflow:latest

# تشغيل الحاوية
docker run -d \
  --name taskflow-app \
  -p 3000:3000 \
  -e DATABASE_URL="postgresql://user:pass@host:5432/taskflow" \
  -e AUTH_SECRET="your-secret-key" \
  -e NEXT_PUBLIC_API_URL="https://api.taskflow.local" \
  --restart unless-stopped \
  taskflow:latest

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

المرحلة الصورة الأساسية الحجم المحتويات
deps node:20-alpine ~150 MB node_modules + تبعيات النظام
build node:20-alpine ~450 MB الكود المصدري + node_modules + مخرجات البناء
runner node:20-alpine ~358 MB standalone + تبعيات الإنتاج
node:20-alpine bare ~126 MB النظام الأساسي

▶ مثال: حقن متغيرات البيئة في Docker

المخرجات:

TEXT 📖 للعرض فقط
Sending build context to Docker daemon  4.096kB
Step 1/8 : FROM node:20-alpine AS deps
 ---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
Docker command completed successfully.
d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4
BASH
# استخدام ملف .env لحقن متغيرات البيئة
cat > .env.production << EOF
DATABASE_URL=postgresql://user:pass@db:5432/taskflow
AUTH_SECRET=super-secret-key
NEXT_PUBLIC_API_URL=https://api.taskflow.local
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxx
REDIS_URL=redis://redis:6379
EOF

docker run -d \
  --name taskflow-app \
  --env-file .env.production \
  -p 3000:3000 \
  --network taskflow-net \
  taskflow:latest

المخرجات:

TEXT 📖 للعرض فقط
Docker operation completed.


5. Nginx كوكيل عكسي

يتولى Nginx إنهاء SSL والتخزين المؤقت للموارد الثابتة وموازنة التحميل، مما يجعله مكونا أساسيا في بيئات الإنتاج.

100%
graph LR
    A[متصفح المستخدم] --> B[Nginx :443]
    B --> C{مطابقة المسار}
    C -->|/_next/static/*| D[خدمة Nginx المباشرة<br/>تخزين مؤقت سنة]
    C -->|/api/health| E[Next.js :3000]
    C -->|/*| E
    B --> F[إنهاء SSL<br/>Let's Encrypt]
    
    style B fill:#cce5ff
    style D fill:#d4edda

إعدادات Nginx

NGINX
# nginx/nginx.conf
upstream nextjs_upstream {
    server app:3000;
    keepalive 64;
}

server {
    listen 80;
    server_name taskflow.local;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name taskflow.local;

    # شهادة SSL
    ssl_certificate /etc/nginx/ssl/taskflow.crt;
    ssl_certificate_key /etc/nginx/ssl/taskflow.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # رؤوس الأمان
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;

    # السجلات
    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # تخزين مؤقت للموارد الثابتة (معالجة بواسطة Next.js)
    location /_next/static/ {
        proxy_pass http://nextjs_upstream;
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

    location /static/ {
        proxy_pass http://nextjs_upstream;
        expires 30d;
        add_header Cache-Control "public";
    }

    # نقاط نهاية فحص الصحة
    location /api/health {
        proxy_pass http://nextjs_upstream;
        access_log off;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # جميع الطلبات الأخرى توجه إلى Next.js
    location / {
        proxy_pass http://nextjs_upstream;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 60s;
        proxy_send_timeout 60s;
    }
}


6. مدير العمليات PM2

يضمن PM2 إعادة تشغيل عمليات Node.js تلقائيا بعد الانهيار ويوفر إدارة السجلات ووضع الكتلة (cluster mode).

(1) إعداد PM2

JS
// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'taskflow',
    script: 'server.js',
    cwd: '/app',
    
    // وضع الكتلة (استخدام جميع أنوية CPU)
    exec_mode: 'cluster',
    instances: 'max',
    
    // متغيرات البيئة
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
      HOSTNAME: '0.0.0.0'
    },
    
    // إعدادات السجلات
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    error_file: '/var/log/pm2/taskflow-error.log',
    out_file: '/var/log/pm2/taskflow-out.log',
    merge_logs: true,
    
    // إعادة التشغيل التلقائي
    max_restarts: 10,
    restart_delay: 1000,
    min_uptime: 5000,
    
    // مراقبة الذاكرة
    max_memory_restart: '500M',
    
    // فحص الصحة
    listen_timeout: 3000,
    kill_timeout: 5000
  }]
}

(2) دمج PM2 مع Docker

DOCKERFILE
# تثبيت PM2 في مرحلة runner
FROM node:20-alpine AS runner

RUN npm install -g pm2 && \
    addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

WORKDIR /app

COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=build --chown=nextjs:nodejs /app/public ./public
COPY --chown=nextjs:nodejs ecosystem.config.js ./

USER nextjs

EXPOSE 3000

# استخدام PM2 لتفعيل وضع الكتلة
CMD ["pm2-runtime", "start", "ecosystem.config.js"]

▶ مثال: أوامر PM2 الشائعة

المخرجات:

TEXT 📖 للعرض فقط
Docker image built and container started successfully.
BASH
# عرض جميع العمليات
pm2 list

# عرض السجلات
pm2 logs taskflow
pm2 logs taskflow --lines 100

# مراقبة الموارد
pm2 monit

# إعادة التحميل (بدون توقف)
pm2 reload taskflow

# إيقاف/إعادة تشغيل
pm2 stop taskflow
pm2 restart taskflow

# حفظ قائمة العمليات الحالية
pm2 save
pm2 startup

المخرجات:

TEXT 📖 للعرض فقط
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name     │ status  │ cpu     │
├────┼──────────┼─────────┼─────────┤
│ 0  │ shophub  │ online  │ 0%      │
└────┴──────────┴─────────┴─────────┘
PM2 command executed successfully.
┌────┬──────────┬─────────┬─────────┐
│ id │ name     │ status  │ cpu     │
├────┼──────────┼─────────┼─────────┤
│ 0  │ shophub  │ online  │ 0%      │
└────┴──────────┴─────────┴─────────┘


7. Docker Compose: تنسيق ثلاث حاويات

ينسق Docker Compose ثلاث حاويات — App و Nginx و PostgreSQL — لتشغيل البيئة بأكملها بنقرة واحدة.

YAML
# docker-compose.yml
version: '3.8'

networks:
  taskflow-net:
    driver: bridge

volumes:
  postgres-data:
    driver: local
  nginx-logs:
    driver: local

services:
  # === 1. قاعدة بيانات PostgreSQL ===
  db:
    image: postgres:16-alpine
    container_name: taskflow-db
    restart: unless-stopped
    networks:
      - taskflow-net
    volumes:
      - postgres-data:/var/lib/postgresql/data
      - ./db/init:/docker-entrypoint-initdb.d
    environment:
      POSTGRES_USER: taskflow
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: taskflow
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U taskflow"]
      interval: 10s
      timeout: 5s
      retries: 5
    ports:
      - "5432:5432"

  # === 2. تطبيق Next.js ===
  app:
    build:
      context: .
      dockerfile: Dockerfile
      target: runner
    image: taskflow:latest
    container_name: taskflow-app
    restart: unless-stopped
    networks:
      - taskflow-net
    depends_on:
      db:
        condition: service_healthy
    environment:
      NODE_ENV: production
      PORT: 3000
      HOSTNAME: "0.0.0.0"
      DATABASE_URL: postgresql://taskflow:${DB_PASSWORD}@db:5432/taskflow
      AUTH_SECRET: ${AUTH_SECRET}
      AUTH_URL: ${AUTH_URL}
      NEXT_PUBLIC_API_URL: ${PUBLIC_API_URL}
      NEXT_PUBLIC_POSTHOG_KEY: ${POSTHOG_KEY:-}
    env_file:
      - .env.production
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 3s
      retries: 3

  # === 3. Nginx كوكيل عكسي ===
  nginx:
    image: nginx:alpine
    container_name: taskflow-nginx
    restart: unless-stopped
    networks:
      - taskflow-net
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - nginx-logs:/var/log/nginx
    depends_on:
      app:
        condition: service_healthy

(1) ملف متغيرات البيئة

BASH
# .env.production (لا ترفعه إلى Git)
DB_PASSWORD=StrongPassword123!
AUTH_SECRET=your-auth-secret-key-min-32-chars
AUTH_URL=https://auth.taskflow.local
PUBLIC_API_URL=https://api.taskflow.local
POSTHOG_KEY=phc_exampleKey123

(2) التشغيل والإدارة

BASH
# التشغيل الأول
docker compose up -d

# عرض السجلات
docker compose logs -f app
docker compose logs -f nginx

# إعادة بناء التطبيق
docker compose build app
docker compose up -d app

# تحديث ترحيل قاعدة البيانات
docker compose exec app npx prisma migrate deploy

# عرض حالة التشغيل
docker compose ps

# إيقاف جميع الخدمات
docker compose down

# تنظيف كامل (بما في ذلك وحدات التخزين)
docker compose down -v

▶ مثال: docker-compose.override.yml (بيئة التطوير)

المخرجات:

TEXT 📖 للعرض فقط
احفظ إعدادات YAML أعلاه في مسار الملف المحدد. ستصبح الإعدادات سارية عند إعادة تشغيل الخادم التالية.
YAML
# docker-compose.override.yml
version: '3.8'

services:
  app:
    build:
      target: build  # استخدام build بدلا من runner في مرحلة التطوير
    environment:
      NODE_ENV: development
    volumes:
      - ./src:/app/src:ro
      - ./public:/app/public:ro
    command: npm run dev  # استخدام خادم التطوير

  db:
    ports:
      - "5432:5432"  # كشف منفذ قاعدة البيانات أثناء التطوير

  nginx:
    ports:
      - "3000:80"  # تبسيط تعيين المنافذ أثناء التطوير

المخرجات:

TEXT 📖 للعرض فقط
احفظ إعدادات YAML أعلاه في مسار الملف المحدد. ستصبح الإعدادات سارية عند إعادة تشغيل الخادم التالية.


8. حقن متغيرات البيئة في وقت التشغيل

يتم حقن متغيرات البيئة لحاويات Docker في وقت التشغيل، وليس أثناء عملية البناء — وهذا يسمح بنشر صورة واحدة إلى بيئات متعددة.

100%
graph LR
    A[Docker Build] --> B[الصورة<br/>(بدون متغيرات بيئة)]
    B --> C[الحقن في وقت التشغيل]
    C --> D[بيئة التطوير .env.dev]
    C --> E[بيئة الاختبار .env.test]
    C --> F[بيئة الإنتاج .env.prod]
    D --> G[بدء تشغيل الحاوية]
    E --> G
    F --> G
    G --> H[server.js يقرأ process.env]
    
    style B fill:#cce5ff
    style G fill:#d4edda

(1) متغيرات وقت البناء مقابل متغيرات وقت التشغيل

نوع المتغير توقيت الحقن مثال موقع التخزين
وقت البناء docker build NEXT_PUBLIC_*، رقم الإصدار ARG في Dockerfile
وقت التشغيل docker run DATABASE_URL، AUTH_SECRET env_file في docker compose
مختلط مطلوب في كليهما NEXT_PUBLIC_API_URL يحقن في الواجهة الأمامية وقت البناء، وفي الخلفية وقت التشغيل

(2) حقن المتغيرات أثناء البناء

DOCKERFILE
# استخدام ARG في Dockerfile لتمرير متغيرات وقت البناء
FROM node:20-alpine AS build

ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL

ARG SENTRY_DSN
ENV SENTRY_DSN=$SENTRY_DSN

RUN npm run build
BASH
# تمرير المتغيرات أثناء البناء
docker build \
  --build-arg NEXT_PUBLIC_API_URL=https://api.taskflow.com \
  --build-arg SENTRY_DSN=https://xxx@sentry.io/123 \
  -t taskflow:latest .

▶ مثال: سكريبت التحقق من متغيرات البيئة في وقت التشغيل

المخرجات:

TEXT 📖 للعرض فقط
Sending build context to Docker daemon  4.096kB
Step 1/8 : FROM node:20-alpine AS deps
 ---> 1dd67de0f936
...
Successfully built a7b3c2d1e0f9
Successfully tagged shophub:latest
TS
// src/lib/env.ts
// التحقق من متغيرات البيئة في وقت التشغيل
function getRequiredEnvVar(name: string): string {
  const value = process.env[name]
  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`)
  }
  return value
}

export const env = {
  databaseUrl: getRequiredEnvVar('DATABASE_URL'),
  authSecret: getRequiredEnvVar('AUTH_SECRET'),
  authUrl: process.env.AUTH_URL || 'http://localhost:3000',
  publicApiUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000',
  posthogKey: process.env.NEXT_PUBLIC_POSTHOG_KEY,
  nodeEnv: process.env.NODE_ENV || 'development',
  isProduction: process.env.NODE_ENV === 'production',
  port: parseInt(process.env.PORT || '3000', 10)
}

المخرجات:

TEXT 📖 للعرض فقط
Exports: env.


9. مثال كامل: نشر TaskFlow باستخدام Docker

BASH
# ============================================
# سكريبت النشر للإنتاج: deploy.sh
# الميزات: بناء ← ترحيل ← تشغيل ← فحص الصحة
# ============================================

#!/bin/bash
set -euo pipefail

echo "=== نشر TaskFlow للإنتاج ==="

# 1. تحميل متغيرات البيئة
if [ ! -f .env.production ]; then
    echo "خطأ: .env.production غير موجود"
    exit 1
fi
source .env.production

# 2. بناء صورة Docker
echo "جاري بناء صورة Docker..."
docker compose build app

# 3. تشغيل قاعدة البيانات (إذا لم تكن قيد التشغيل)
echo "جاري تشغيل قاعدة البيانات..."
docker compose up -d db
echo "في انتظار جاهزية قاعدة البيانات..."
sleep 5

# 4. تشغيل ترحيل قاعدة البيانات
echo "جاري تشغيل ترحيل قاعدة البيانات..."
docker compose run --rm app npx prisma migrate deploy

# 5. تشغيل التطبيق و Nginx
echo "جاري تشغيل التطبيق و Nginx..."
docker compose up -d app nginx

# 6. فحص الصحة
echo "جاري تشغيل فحص الصحة..."
for i in {1..10}; do
    if curl -s -o /dev/null -w "%{http_code}" http://localhost:80/api/health | grep -q 200; then
        echo "تم اجتياز فحص الصحة!"
        break
    fi
    echo "في الانتظار... ($i/10)"
    sleep 3
done

# 7. تنظيف الصور القديمة
echo "جاري تنظيف الصور القديمة..."
docker image prune -f

# 8. معلومات النشر
echo ""
echo "=== اكتمل النشر ==="
echo "التطبيق:      http://localhost:80"
echo "API:          http://localhost:80/api/health"
echo "قاعدة البيانات:       postgresql://taskflow@localhost:5432/taskflow"
echo "السجلات:     docker compose logs -f app"
echo "إعادة التشغيل:  docker compose restart app"
TS
// src/app/api/health/route.ts
// ============================================
// API فحص الصحة: يستدعى من Docker HEALTHCHECK
// ============================================
import { NextResponse } from 'next/server'
import { prisma } from '@/lib/prisma'

export async function GET() {
  const checks = {
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    checks: {} as Record<string, boolean>
  }

  try {
    // التحقق من اتصال قاعدة البيانات
    await prisma.$queryRaw`SELECT 1`
    checks.checks.database = true
  } catch {
    checks.checks.database = false
    checks.status = 'degraded'
  }

  try {
    // التحقق من Redis (إذا تم إعداده)
    // await redis.ping()
    checks.checks.redis = true
  } catch {
    checks.checks.redis = false
    if (!checks.checks.database) {
      checks.status = 'unhealthy'
    }
  }

  const statusCode = checks.status === 'healthy' ? 200 : 503

  return NextResponse.json(checks, { status: statusCode })
}

❓ أسئلة شائعة

س ما الفرق بين output: 'standalone' و output: 'export'؟
ج standalone ينتج حزمة مستقلة تتضمن خادم Node.js وتدعم جميع ميزات Next.js، مثل SSR و ISR و API Routes. أما export فينتج HTML ثابتا خالصا (مع تعطيل SSR)، وهو مناسب للاستضافة على CDN. للنشر الذاتي مع Docker، يجب استخدام وضع standalone.
س لماذا البناء متعدد المراحل أفضل من البناء أحادي المرحلة؟
ج (1) صور أصغر — تحتوي مرحلة التشغيل على الحد الأدنى من الملفات المطلوبة للتشغيل فقط (358 MB مقابل 1.2 GB)؛ (2) أكثر أمانا — لا يتم تضمين أدوات البناء والكود المصدري في الصورة النهائية؛ (3) تخزين مؤقت أكثر كفاءة للبناء — نظرا لأن طبقات التبعيات نادرا ما تتغير، يمكن إعادة استخدام طبقات التخزين المؤقت لـ Docker.
س هل Nginx ضروري أم اختياري؟
ج يوصى بشدة باستخدام Nginx في بيئات الإنتاج: (1) إنهاء SSL — معالجة شهادات HTTPS؛ (2) التخزين المؤقت للموارد الثابتة — تقليل الحمل على Node.js؛ (3) حقن رؤوس الأمان — XSS و CSP و HSTS وغيرها؛ (4) موازنة التحميل — توزيع الطلبات عبر نسخ متعددة. في بيئات الشبكة الداخلية البسيطة، يمكنك تخطي هذه الخطوة وكشف منفذ Next.js مباشرة.
س هل أحتاج إلى استخدام كل من PM2 وسياسة إعادة تشغيل Docker؟
ج نوصي باستخدام كليهما. restart: unless-stopped في Docker يعالج انهيارات مستوى الحاوية (مثل OOM)، بينما يعالج PM2 انهيارات مستوى عملية Node.js (مثل الاستثناءات غير الملتقطة). كما يوفر PM2 ميزات لا يوفرها Docker نفسه، مثل تدوير السجلات ووضع الكتلة وإعادة التشغيل بدون توقف.
س كيف تضمن بيئة وقت التشغيل حقن متغيرات NEXT_PUBLIC_* أثناء البناء؟
ج المتغيرات التي تبدأ بالبادئة NEXT_PUBLIC_* تضمن في حزمة JS أثناء البناء ولا يمكن تعديلها في وقت التشغيل. للنشر الذاتي، الحل هو: (1) تمرير قيمة NEXT_PUBLIC_* أثناء البناء؛ (2) أو وضع المتغيرات التي تتطلب إعدادا في وقت التشغيل في استجابة API route (مثل /api/config)، والتي تسترجعها الواجهة الأمامية عبر fetch.
س أيهما أختار، Docker Compose أم Kubernetes؟
ج للنشر على خادم واحد، اختر Docker Compose (إعداد بسيط، منحنى تعلم منخفض)؛ للتجمعات متعددة الخوادم والتحجيم التلقائي واكتشاف الخدمات، اختر Kubernetes. للفرق الصغيرة والمتوسطة (1-5 خوادم)، Docker Compose في وضع Swarm كاف لمعظم السيناريوهات.

📖 ملخص


📝 تمارين

  1. مسألة أساسية (⭐): أنشئ next.config.js يحتوي على output: 'standalone'، واكتب Dockerfile متعدد المراحل، وابنه وشغله بنجاح باستخدام docker run، ثم تحقق منه باستخدام curl localhost:3000.

  2. تمرين متقدم (⭐⭐): أضف حاوية Nginx كوكيل عكسي إلى Docker Compose: (1) أعد شهادة SSL موقعة ذاتيا؛ (2) أضف قواعد التخزين المؤقت للموارد الثابتة؛ (3) أعد /_next/static ليتم تخزينها مؤقتا لمدة 365 يوما؛ (4) تحقق من أن الوصول عبر HTTPS يعمل بشكل صحيح.

  3. تحد (⭐⭐⭐): ابن خط تجميع CI/CD + Docker كامل للنشر الذاتي: (1) استخدم GitHub Actions لبناء صور Docker تلقائيا ودفعها إلى GHCR؛ (2) اسحب الصور الجديدة إلى الخادم الهدف عبر SSH؛ (3) استخدم Docker Compose للتحديث بدون توقف (docker compose up -d --no-deps --build app)؛ (4) أعد وضع الكتلة في PM2 وتدوير السجلات.

Web-Tutorial.com

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

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

100%