404 Not Found

404 Not Found


nginx

Project Deployment

Now that MegaShop has been developed, Charlie needs to deploy it to the production environment. This isn't just a simple npm run build—it requires Docker orchestration for the entire suite of services, a CI/CD automation pipeline, runtime monitoring and error tracing, and a comprehensive deployment checklist. Deploy once, run stably for the long term.

1. What You'll Learn


2. A True Story of an Architect

(1) Pain Point: Crashes Immediately Upon Launch

Charlie's first deployment of MegaShop—he forgot to run the database migrations, missed configuring the environment variables, didn't start Redis, and the SSL certificate had expired. Five minutes after going live, Alice noticed a 500 error, and it took Bob an hour to manually roll back the changes.

(2) Solutions for Production-Level Deployment Systems

A comprehensive deployment system: automated CI/CD + Docker Compose + monitoring + deployment checklist, with validation at every step and automatic rollback in case of errors.

(3) Benefits: Automation + Observability

Once the code is pushed, it deploys automatically; Sentry captures errors in real time; Prometheus monitors performance metrics; and Alice will never encounter an undetected 500 error again.


3. Production Build Optimization

(1) Analysis of Construction Products

(1) ▶ Example: Build Analysis and Optimization

BASH
# Build for production
npm run build

# Analyze bundle
npx nuxi analyze

Output:

TEXT
# Command executed successfully

(2) ▶ Example: Environment Variable Security Check

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'
  }
})

Output:

TEXT
// Execution Successful

(2) Develop an Optimization Checklist

Check Item Requirement Verification Method
JS Bundle gzip < 200KB nuxi analyze
CSS Extraction ✅ Separate File Build Output Check
WebP Images ✅ Automatic Conversion NuxtImg Validation
Environment Variables No Hard-Coded Keys grep Check
Tree Shaking No Unused Code Bundle Analysis
Source Map Production Disabled nuxt.config.ts

4. Docker Compose Orchestration

(1) Production Deployment Process

100%
flowchart TB
    A[Git Push] --> B[GitHub Actions CI]
    B --> C{Tests Pass?}
    C -->|No| D[Notify Team + Block]
    C -->|Yes| E[Build Docker Image]
    E --> F[Push to Registry]
    F --> G[Deploy to Staging]
    G --> H{Smoke Test?}
    H -->|No| I[Rollback Staging]
    H -->|Yes| J[Deploy to Production]
    J --> K{Health Check?}
    K -->|No| L[Auto Rollback]
    K -->|Yes| M[Done ✅]

(1) ▶ Example: Production-Grade 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:

Output:

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

5. CI/CD Pipeline

(1) ▶ Example: Complete Production Deployment Workflow

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: Deploy via 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: Health check
        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: Rollback on failure
        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)"

Output:

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

6. Monitoring and Logging

(1) ▶ Example: Sentry Error Tracking

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
    }
  }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Server-side logs

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

    // Structured log
    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))
    }
  })
})

Output:

TEXT
// Execution Successful

(3) ▶ Example: Health Check API

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

  // Check database
  try {
    await prisma.$queryRaw`SELECT 1`
    checks.database = 'ok'
  } catch {
    checks.database = 'error'
  }

  // Check 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()
  }
})

Output:

TEXT
// Execution Successful

7. Deployment Checklist

(1) Production Launch Inspection

# Check Item Action Verification Method
1 SSL Certificate Configure Let's Encrypt curl -v https://megashop.com
2 Environment Variables All keys have been configured /api/health check
3 Database Migration prisma migrate deploy Product Query Validation
4 Redis Connection Start and Connect Check /api/health
5 CDN Configuration Cloudflare/CloudFront Static Resource Cache Hits
6 Cache Preheating First Visit to Popular Products Page curl Home Page + List Page
7 Health Check /api/health returns "ok" Automated Verification
8 Sentry Initialization Error Tracking Enabled Trigger Test Error
9 Sitemap Generation /sitemap.xml is accessible Google Search Console
10 Rollback Plan Old Image Available Document Rollback Steps

(2) Rollback Plan

Step Action Command
1 Stop the current service docker compose down
2 Restore Old Image docker tag xxx:previous xxx:latest
3 Launch an older version docker compose up -d --no-build
4 Health Check curl /api/health
5 Notify the Team Slack/Email Notifications

8. Comprehensive Example: MegaShop Deployment Commands

BASH
# ============================================
# MegaShop Production Deployment
# Complete deployment + monitoring setup
# ============================================

# 1. Set up server (first time only)
ssh prod-server
mkdir -p /opt/megashop/ssl
# Copy SSL certificates
# Copy .env with production secrets

# 2. Initial deployment
cd /opt/megashop
git clone https://github.com/megashop/megashop.git .
docker compose -f docker-compose.prod.yml up -d

# 3. Database setup
docker compose exec web npx prisma migrate deploy
docker compose exec web npx prisma db seed

# 4. Verify
curl -f https://megashop.com/api/health
curl -f https://megashop.com/sitemap.xml

# 5. Cache warmup
curl https://megashop.com/
curl https://megashop.com/products
curl https://megashop.com/products/1

# 6. Monitor
docker compose logs -f web
# Check Sentry dashboard
# Check health endpoint periodically

❓ FAQ

Q How do I update a Docker image?
A CI/CD automatically builds and pushes new images to GHCR; on the server, run docker pull followed by docker-compose up -d --no-build. Use blue-green deployment for zero downtime.
Q Is the free quota in Sentry sufficient?
A The free plan includes 5,000 error events per month. This is sufficient for MegaShop in the early stages; you can upgrade as needed later on. Development errors are not counted (only production errors are reported).
Q Does the health check endpoint require authentication?
A No. /api/health must be publicly accessible for use by load balancers, K8s, and Docker health checks. However, do not expose any sensitive information.
Q How do I warm up the cache?
A After deployment, use a script to access popular pages (homepage, list pages, Top 100 product details) to trigger ISR cache generation. This can also be automated after seeding.
Q How are logs collected?
A For Docker logs, use docker compose logs. For production environments, we recommend ELK (Elasticsearch + Logstash + Kibana) or Loki + Grafana. In its early stages, MegaShop used structured console.log entries combined with Sentry.
Q What should I do if a database migration fails?
A prisma migrate deploy only applies validated migration files. If a migration fails, fix the migration file and redeploy it. Never use prisma migrate reset in production (it will result in data loss).

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Deploy MegaShop to a production environment (Docker Compose) and verify that /api/health returns "ok"
  2. Advanced Exercise (Difficulty: ⭐⭐): Set up GitHub Actions CI/CD to automatically deploy upon a push to the main branch, including health checks and automatic rollbacks
  3. Challenge (Difficulty: ⭐⭐⭐): Build a complete operations system—Sentry error tracking + structured logging + Prometheus metrics + automated validation of deployment checklists

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏