404 Not Found

404 Not Found


nginx

نشر المشروع

الآن بعد أن تم تطوير MegaShop، يحتاج Charlie إلى نشره في بيئة الإنتاج. هذا ليس مجرد npm run build بسيط—بل يتطلب تنسيق Docker لمجموعة الخدمات الكاملة، وخط أنابيب CI/CD مؤتمت، ومراقبة وقت التشغيل وتتبع الأخطاء، وقائمة فحص نشر شاملة. انشر مرة واحدة، وشغّل باستقرار على المدى الطويل.

1. ما ستتعلمه


2. قصة حقيقية لمهندس معماري

(1) نقطة الألم: يتحطم فور الإطلاق

أول نشر لـ MegaShop الخاص بـ Charlie—نسي تشغيل ترحيلات قاعدة البيانات، ولم يكوّن متغيرات البيئة، ولم يشغل Redis، وشهادة SSL منتهية الصلاحية. بعد خمس دقائق من الإطلاق، لاحظت Alice خطأ 500، واستغرق Bob ساعة للتراجع يدويًا.

(2) حلول لأنظمة النشر بمستوى الإنتاج

نظام نشر شامل: CI/CD مؤتمت + Docker Compose + مراقبة + قائمة فحص نشر، مع التحقق في كل خطوة وتراجع تلقائي في حالة الأخطاء.

(3) الفوائد: أتمتة + قابلية للرصد

بمجرد دفع الكود، يُنشر تلقائيًا؛ Sentry يلتقط الأخطاء في الوقت الفعلي؛ Prometheus يراقب مقاييس الأداء؛ ولن تواجه Alice خطأ 500 غير مكتشف مرة أخرى.


3. تحسين بناء الإنتاج

(1) تحليل منتجات البناء

(1) ▶ مثال: تحليل البناء والتحسين

BASH
# بناء للإنتاج
npm run build

# تحليل الحزمة
npx nuxi analyze

الناتج:

TEXT
# تم تنفيذ الأمر بنجاح

(2) ▶ مثال: فحص أمان متغيرات البيئة

TYPESCRIPT
// server/utils/env-check.ts
export default defineEventHandler((event) => {
  if (getRequestURL(event).pathname !== '/api/health') return

  const config = useRuntimeConfig()
  const required = ['databaseUrl', 'jwtAccessSecret', 'jwtRefreshSecret']
  const missing = required.filter(key => !config[key as keyof typeof config])

  if (missing.length > 0 && process.env.NODE_ENV === 'production') {
    console.error(`Missing required env vars: ${missing.join(', ')}`)
  }

  return {
    status: missing.length === 0 ? 'ok' : 'degraded',
    timestamp: Date.now(),
    version: process.env.APP_VERSION || 'unknown'
  }
})

الناتج:

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

(2) قائمة تحسين التطوير

عنصر الفحص المتطلب طريقة التحقق
حزمة JS gzip < 200KB nuxi analyze
استخراج CSS ✅ ملف منفصل فحص مخرجات البناء
صور WebP ✅ تحويل تلقائي التحقق من NuxtImg
متغيرات البيئة بدون مفاتيح مشفرة فحص grep
Tree Shaking بدون كود غير مستخدم تحليل الحزمة
Source Map معطل في الإنتاج nuxt.config.ts

4. تنسيق Docker Compose

(1) عملية نشر الإنتاج

100%
flowchart TB
    A[Git Push] --> B[GitHub Actions CI]
    B --> C{الاختبارات ناجحة؟}
    C -->|لا| D[إبلاغ الفريق + حظر]
    C -->|نعم| E[بناء صورة Docker]
    E --> F[دفع إلى السجل]
    F --> G[نشر إلى Staging]
    G --> H{اختبار الدخان؟}
    H -->|لا| I[تراجع Staging]
    H -->|نعم| J[نشر إلى الإنتاج]
    J --> K{فحص الصحة؟}
    K -->|لا| L[تراجع تلقائي]
    K -->|نعم| M[تم ✅]

(1) ▶ مثال: Docker Compose بمستوى الإنتاج

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

services:
  web:
    image: ghcr.io/megashop/megashop:latest
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://megashop:${DB_PASSWORD}@db:5432/megashop
      - REDIS_URL=redis://redis:6379
      - JWT_ACCESS_SECRET=${JWT_ACCESS_SECRET}
      - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET}
      - DEPLOY_TARGET=node-server
    depends_on:
      db: { condition: service_healthy }
      redis: { condition: service_started }
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    deploy:
      resources:
        limits: { memory: 1G, cpus: '1.0' }

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

  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD} --maxmemory 256mb --maxmemory-policy allkeys-lru
    volumes:
      - redis_data:/data
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.prod.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/nginx/ssl:ro
    depends_on:
      - web
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

الناتج:

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

5. خط أنابيب CI/CD

(1) ▶ مثال: سير عمل نشر الإنتاج الكامل

YAML
# .github/workflows/deploy-production.yml
name: Deploy Production

on:
  release:
    types: [published]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: megashop/megashop

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:16-alpine
        env: { POSTGRES_USER: test, POSTGRES_PASSWORD: test, POSTGRES_DB: megashop_test }
        ports: ['5432:5432']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci
      - run: npx prisma migrate deploy
        env: { DATABASE_URL: postgresql://test:test@localhost:5432/megashop_test }
      - run: npm run lint
      - run: npm run typecheck
      - run: npm run test:coverage

  build-push:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GHCR_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

  deploy:
    needs: build-push
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: نشر عبر SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            cd /opt/megashop
            docker pull ghcr.io/megashop/megashop:latest
            docker compose -f docker-compose.prod.yml up -d --no-build
            docker compose exec web npx prisma migrate deploy
            echo "Deploy completed at $(date)"

      - name: فحص الصحة
        run: |
          for i in {1..15}; do
            if curl -sf https://megashop.com/api/health; then
              echo "Health check passed"
              exit 0
            fi
            echo "Waiting for health check... ($i/15)"
            sleep 5
          done
          echo "Health check failed!"
          exit 1

      - name: تراجع عند الفشل
        if: failure()
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.PROD_HOST }}
          username: ${{ secrets.PROD_USER }}
          key: ${{ secrets.PROD_SSH_KEY }}
          script: |
            cd /opt/megashop
            docker compose down
            docker tag ghcr.io/megashop/megashop:previous ghcr.io/megashop/megashop:latest
            docker compose -f docker-compose.prod.yml up -d --no-build
            echo "Rollback completed at $(date)"

الناتج:

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

6. المراقبة والتسجيل

(1) ▶ مثال: تتبع أخطاء Sentry

TYPESCRIPT
// plugins/sentry.client.ts
import * as Sentry from '@sentry/vue'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  Sentry.init({
    dsn: config.public.sentryDsn,
    environment: config.public.environment,
    release: config.public.version,
    integrations: [
      new Sentry.BrowserTracing({
        routingInstrumentation: Sentry.vueRouterInstrumentation(nuxtApp.$router)
      })
    ],
    tracesSampleRate: 0.1,
    replaysSessionSampleRate: 0.1,
    replaysOnErrorSampleRate: 1.0
  })

  return {
    provide: {
      sentry: Sentry
    }
  }
})

الناتج:

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

(2) ▶ مثال: سجلات جانب الخادم

TYPESCRIPT
// server/middleware/logging.ts
export default defineEventHandler((event) => {
  const start = Date.now()
  const method = getMethod(event)
  const url = getRequestURL(event)

  event.node.res.on('finish', () => {
    const duration = Date.now() - start
    const status = event.node.res.statusCode

    // سجل منظم
    const logEntry = {
      method, url: url.pathname,
      status, duration,
      userId: event.context.user?.id || null,
      timestamp: new Date().toISOString()
    }

    if (status >= 500) {
      console.error(JSON.stringify(logEntry))
    } else if (duration > 1000) {
      console.warn(JSON.stringify({ ...logEntry, alert: 'slow_request' }))
    } else {
      console.log(JSON.stringify(logEntry))
    }
  })
})

الناتج:

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

(3) ▶ مثال: واجهة API لفحص الصحة

TYPESCRIPT
// server/api/health.get.ts
export default defineEventHandler(async () => {
  const checks: Record<string, string> = {}

  // فحص قاعدة البيانات
  try {
    await prisma.$queryRaw`SELECT 1`
    checks.database = 'ok'
  } catch {
    checks.database = 'error'
  }

  // فحص Redis
  try {
    const storage = useStorage('products')
    await storage.setItem('health-check', 'ok', { ttl: 10 })
    checks.redis = 'ok'
  } catch {
    checks.redis = 'error'
  }

  const allOk = Object.values(checks).every(v => v === 'ok')

  setHeader(useEvent(), 'cache-control', 'no-store')

  return {
    status: allOk ? 'ok' : 'degraded',
    checks,
    version: process.env.APP_VERSION || 'unknown',
    uptime: process.uptime(),
    timestamp: new Date().toISOString()
  }
})

الناتج:

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

7. قائمة فحص النشر

(1) فحص إطلاق الإنتاج

# عنصر الفحص الإجراء طريقة التحقق
1 شهادة SSL تكوين Let's Encrypt curl -v https://megashop.com
2 متغيرات البيئة جميع المفاتيح مكوّنة فحص /api/health
3 ترحيل قاعدة البيانات prisma migrate deploy التحقق من استعلام المنتجات
4 اتصال Redis بدء واتصال فحص /api/health
5 تكوين CDN Cloudflare/CloudFront إصابات تخزين مؤقت للموارد الثابتة
6 تسخين التخزين المؤقت أول زيارة لصفحات المنتجات الشائعة curl الصفحة الرئيسية + صفحة القائمة
7 فحص الصحة /api/health يرجع "ok" تحقق مؤتمت
8 تهيئة Sentry تتبع الأخطاء مفعّل تفعيل خطأ اختبار
9 توليد Sitemap /sitemap.xml متاح Google Search Console
10 خطة التراجع الصورة القديمة متاحة توثيق خطوات التراجع

(2) خطة التراجع

الخطوة الإجراء الأمر
1 إيقاف الخدمة الحالية docker compose down
2 استعادة الصورة القديمة docker tag xxx:previous xxx:latest
3 إطلاق نسخة أقدم docker compose up -d --no-build
4 فحص الصحة curl /api/health
5 إبلاغ الفريق إشعارات Slack/بريد إلكتروني

8. مثال شامل: أوامر نشر MegaShop

BASH
# ============================================
# نشر MegaShop للإنتاج
# نشر كامل + إعداد المراقبة
# ============================================

# 1. إعداد الخادم (المرة الأولى فقط)
ssh prod-server
mkdir -p /opt/megashop/ssl
# نسخ شهادات SSL
# نسخ .env بأسرار الإنتاج

# 2. النشر الأولي
cd /opt/megashop
git clone https://github.com/megashop/megashop.git .
docker compose -f docker-compose.prod.yml up -d

# 3. إعداد قاعدة البيانات
docker compose exec web npx prisma migrate deploy
docker compose exec web npx prisma db seed

# 4. التحقق
curl -f https://megashop.com/api/health
curl -f https://megashop.com/sitemap.xml

# 5. تسخين التخزين المؤقت
curl https://megashop.com/
curl https://megashop.com/products
curl https://megashop.com/products/1

# 6. المراقبة
docker compose logs -f web
# فحص لوحة تحكم Sentry
# فحص نقطة نهاية الصحة بشكل دوري

❓ أسئلة شائعة

س كيف أحدّث صورة Docker؟
ج CI/CD يبني ويدفع صورًا جديدة تلقائيًا إلى GHCR؛ على الخادم، شغّل docker pull ثم docker-compose up -d --no-build. استخدم النشر الأزرق-الأخضر لعدم وجود توقف.
س هل الحصة المجانية في Sentry كافية؟
ج الخطة المجانية تتضمن 5000 حدث خطأ شهريًا. هذا كافٍ لـ MegaShop في المراحل المبكرة؛ يمكنك الترقية حسب الحاجة لاحقًا. أخطاء التطوير لا تُحسب (يُبلّغ فقط عن أخطاء الإنتاج).
س هل تتطلب نقطة نهاية فحص الصحة مصادقة؟
ج لا. /api/health يجب أن يكون متاحًا publicly للاستخدام من قبل موزعات الحمل وK8s وفحوصات صحة Docker. لكن لا تكشف أي معلومات حساسة.
س كيف أسخّن التخزين المؤقت؟
ج بعد النشر، استخدم نصًا برمجيًا للوصول إلى الصفحات الشائعة (الرئيسية، صفحات القوائم، أفضل 100 تفاصيل منتج) لتفعيل توليد تخزين ISR المؤقت. يمكن أيضًا أتمتة ذلك بعد البذر.
س كيف تُجمع السجلات؟
ج لسجلات Docker، استخدم docker compose logs. لبيئات الإنتاج، نوصي بـ ELK (Elasticsearch + Logstash + Kibana) أو Loki + Grafana. في مراحلها المبكرة، استخدم MegaShop إدخالات console.log منظمة مع Sentry.
س ماذا أفعل إذا فشلت ترحيلات قاعدة البيانات؟
ج prisma migrate deploy يطبق فقط ملفات الترحيل المُتحقق منها. إذا فشل ترحيل، أصلح ملف الترحيل وأعد نشره. لا تستخدم أبدًا prisma migrate reset في الإنتاج (سيؤدي إلى فقدان البيانات).

📖 ملخص


📝 تمارين

  1. تمرين أساسي (الصعوبة: ⭐): نشر MegaShop إلى بيئة إنتاج (Docker Compose) والتحقق من أن /api/health يرجع "ok"
  2. تمرين متقدم (الصعوبة: ⭐⭐): إعداد GitHub Actions CI/CD للنشر التلقائي عند الدفع إلى فرع main، بما في ذلك فحوصات الصحة والتراجع التلقائي
  3. تحدي (الصعوبة: ⭐⭐⭐): بناء نظام تشغيل كامل—تتبع أخطاء Sentry + تسجيل منظم + مقاييس Prometheus + تحقق مؤتمت من قوائم فحص النشر

---|

Web-Tutorial.com

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

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

100%