نشر المشروع
الآن بعد أن تم تطوير MegaShop، يحتاج Charlie إلى نشره في بيئة الإنتاج. هذا ليس مجرد npm run build بسيط—بل يتطلب تنسيق Docker لمجموعة الخدمات الكاملة، وخط أنابيب CI/CD مؤتمت، ومراقبة وقت التشغيل وتتبع الأخطاء، وقائمة فحص نشر شاملة. انشر مرة واحدة، وشغّل باستقرار على المدى الطويل.
1. ما ستتعلمه
- تحسين بناء الإنتاج: تحليل nuxt build + تحسين الحزمة + أمان متغيرات البيئة
- تنسيق Docker Compose: Nuxt + PostgreSQL + Redis + Nginx
- خط أنابيب CI/CD: اختبار مؤتمت مع GitHub Actions + نشر عبر بيئات متعددة
- المراقبة والتسجيل: تتبع أخطاء Sentry + تسجيل API + مقاييس الأداء
- قائمة فحص النشر: SSL/CDN/الترحيل/تسخين التخزين المؤقت/خطة التراجع
2. قصة حقيقية لمهندس معماري
(1) نقطة الألم: يتحطم فور الإطلاق
أول نشر لـ MegaShop الخاص بـ Charlie—نسي تشغيل ترحيلات قاعدة البيانات، ولم يكوّن متغيرات البيئة، ولم يشغل Redis، وشهادة SSL منتهية الصلاحية. بعد خمس دقائق من الإطلاق، لاحظت Alice خطأ 500، واستغرق Bob ساعة للتراجع يدويًا.
(2) حلول لأنظمة النشر بمستوى الإنتاج
نظام نشر شامل: CI/CD مؤتمت + Docker Compose + مراقبة + قائمة فحص نشر، مع التحقق في كل خطوة وتراجع تلقائي في حالة الأخطاء.
(3) الفوائد: أتمتة + قابلية للرصد
بمجرد دفع الكود، يُنشر تلقائيًا؛ Sentry يلتقط الأخطاء في الوقت الفعلي؛ Prometheus يراقب مقاييس الأداء؛ ولن تواجه Alice خطأ 500 غير مكتشف مرة أخرى.
3. تحسين بناء الإنتاج
(1) تحليل منتجات البناء
(1) ▶ مثال: تحليل البناء والتحسين
# بناء للإنتاج
npm run build
# تحليل الحزمة
npx nuxi analyze
الناتج:
# تم تنفيذ الأمر بنجاح
(2) ▶ مثال: فحص أمان متغيرات البيئة
// 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'
}
})
الناتج:
// التنفيذ ناجح
(2) قائمة تحسين التطوير
| عنصر الفحص | المتطلب | طريقة التحقق |
|---|---|---|
| حزمة JS gzip | < 200KB | nuxi analyze |
| استخراج CSS | ✅ ملف منفصل | فحص مخرجات البناء |
| صور WebP | ✅ تحويل تلقائي | التحقق من NuxtImg |
| متغيرات البيئة | بدون مفاتيح مشفرة | فحص grep |
| Tree Shaking | بدون كود غير مستخدم | تحليل الحزمة |
| Source Map | معطل في الإنتاج | nuxt.config.ts |
4. تنسيق Docker Compose
(1) عملية نشر الإنتاج
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 بمستوى الإنتاج
# 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:
الناتج:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
5. خط أنابيب CI/CD
(1) ▶ مثال: سير عمل نشر الإنتاج الكامل
# .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)"
الناتج:
CONTAINER ID IMAGE STATUS PORTS
abc123 nginx:latest Up 2 hours 0.0.0.0:80->80/tcp
6. المراقبة والتسجيل
(1) ▶ مثال: تتبع أخطاء Sentry
// 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
}
}
})
الناتج:
// التنفيذ ناجح
(2) ▶ مثال: سجلات جانب الخادم
// 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))
}
})
})
الناتج:
// التنفيذ ناجح
(3) ▶ مثال: واجهة API لفحص الصحة
// 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()
}
})
الناتج:
// التنفيذ ناجح
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
# ============================================
# نشر 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 pull ثم docker-compose up -d --no-build. استخدم النشر الأزرق-الأخضر لعدم وجود توقف.docker compose logs. لبيئات الإنتاج، نوصي بـ ELK (Elasticsearch + Logstash + Kibana) أو Loki + Grafana. في مراحلها المبكرة، استخدم MegaShop إدخالات console.log منظمة مع Sentry.prisma migrate deploy يطبق فقط ملفات الترحيل المُتحقق منها. إذا فشل ترحيل، أصلح ملف الترحيل وأعد نشره. لا تستخدم أبدًا prisma migrate reset في الإنتاج (سيؤدي إلى فقدان البيانات).📖 ملخص
- بناء الإنتاج: تحليل الحزمة + أمان متغيرات البيئة + Source Maps معطلة
- تنسيق Docker Compose: مكدس كامل من خدمات Nuxt + PostgreSQL + Redis + Nginx
- CI/CD: GitHub Actions—اختبار وبناء ونشر وفحص صحة وتراجع تلقائي مؤتمت
- المراقبة: تتبع أخطاء Sentry + تسجيل منظم + واجهة API لفحص الصحة
- قائمة فحص نشر من 10 عناصر: SSL، متغيرات البيئة، الترحيل، Redis، CDN، تسخين التخزين المؤقت، خطة التراجع
📝 تمارين
- تمرين أساسي (الصعوبة: ⭐): نشر MegaShop إلى بيئة إنتاج (Docker Compose) والتحقق من أن /api/health يرجع "ok"
- تمرين متقدم (الصعوبة: ⭐⭐): إعداد GitHub Actions CI/CD للنشر التلقائي عند الدفع إلى فرع
main، بما في ذلك فحوصات الصحة والتراجع التلقائي - تحدي (الصعوبة: ⭐⭐⭐): بناء نظام تشغيل كامل—تتبع أخطاء Sentry + تسجيل منظم + مقاييس Prometheus + تحقق مؤتمت من قوائم فحص النشر
---|



