Next.js 15 全栈开发系列之Docker 构建与优化

文摘   2024-11-13 08:09   湖北  

一、项目配置文件

1. 项目结构

├── .dockerignore           # Docker 构建忽略文件
├── .env                    # 环境变量配置
├── .env.production         # 生产环境变量
├── Dockerfile             # Docker 构建文件
├── docker-compose.yml     # 开发环境配置
├── docker-compose.prod.yml # 生产环境配置
├── next.config.js         # Next.js 配置
└── deploy/
    ├── nginx.conf         # Nginx 配置
    └── prometheus.yml     # 监控配置

2. .dockerignore

# 开发文件
.git
.gitignore
.env*
.vscode
.idea
*.md

# 依赖文件
node_modules
**/node_modules
npm-debug.log
yarn-debug.log
.pnpm-debug.log

# 构建文件
.next
out
dist
build
coverage

# 测试文件
__tests__
test
tests
*.test.js

# 部署文件
.docker
docker-compose*
Dockerfile*

# 工具配置
.eslintrc
.prettierrc
.husky

3. 完整的 Dockerfile

# 基础镜像阶段
FROM node:20-alpine AS base

# 依赖安装阶段
FROM base AS deps
WORKDIR /app

# 安装构建工具
RUN apk add --no-cache \
    libc6-compat \
    python3 \
    make \
    g++


# 配置 pnpm
RUN corepack enable && corepack prepare pnpm@8.15.1 --activate

# 仅复制依赖相关文件
COPY package.json pnpm-lock.yaml ./
COPY .npmrc ./

# 安装依赖
RUN pnpm fetch --prod
RUN pnpm install --offline --prod --frozen-lockfile

# 构建阶段
FROM base AS builder
WORKDIR /app

# 复制依赖
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# 设置环境变量
ENV NEXT_TELEMETRY_DISABLED 1
ENV NODE_ENV production

# 构建应用
RUN pnpm build

# 运行阶段
FROM base AS runner
WORKDIR /app

# 安装生产环境工具
RUN apk add --no-cache \
    curl \
    tzdata \
    tini


# 创建非 root 用户
RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs


# 设置环境变量
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
ENV TZ=Asia/Shanghai
ENV PORT 3000
ENV NODE_OPTIONS='--max-old-space-size=4096'

# 设置时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
    echo $TZ > /etc/timezone


# 设置工作目录权限
RUN mkdir -p /app && chown -R nextjs:nodejs /app
WORKDIR /app

# 复制构建产物
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

# 切换用户
USER nextjs

# 暴露端口
EXPOSE 3000

# 使用 tini 作为初始化系统
ENTRYPOINT ["/sbin/tini""--"]
CMD ["node""server.js"]

4. docker-compose.yml

version: '3.8'

services:
  nextjs:
    container_name: nextjs-app
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
    restart: always
    ports:
      - '3000:3000'
    environment:
      - NODE_ENV=production
      - DATABASE_URL=${DATABASE_URL}
    networks:
      - app-network
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
        reservations:
          cpus: '0.5'
          memory: 512M
    healthcheck:
      test: ["CMD""curl""-f""http://localhost:3000/api/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./deploy/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./data/certbot/conf:/etc/letsencrypt:ro
    depends_on:
      - nextjs
    networks:
      - app-network

  postgres:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=${DB_NAME}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network
    deploy:
      resources:
        limits:
          memory: 1G
    healthcheck:
      test: ["CMD-SHELL""pg_isready -U ${DB_USER} -d ${DB_NAME}"]
      interval: 30s
      timeout: 5s
      retries: 3

networks:
  app-network:
    driver: bridge

volumes:
  postgres-data:

5. next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  output'standalone',
  compresstrue,
  poweredByHeaderfalse,
  
  experimental: {
    optimizeCsstrue,
  },

  // 静态资源优化
  images: {
    domains: ['example.com'],
    minimumCacheTTL60,
  },

  // HTTP 头部配置
  async headers() {
    return [
      {
        source'/:all*(svg|jpg|png)',
        headers: [
          {
            key'Cache-Control',
            value'public, max-age=31536000, immutable',
          },
        ],
      },
      {
        source'/_next/static/:all*',
        headers: [
          {
            key'Cache-Control',
            value'public, max-age=31536000, immutable',
          },
        ],
      },
    ];
  },
}

module.exports = nextConfig

二、部署流程

1. 开发环境部署

# 构建开发环境
docker compose build

# 启动服务
docker compose up -d

# 查看日志
docker compose logs -f nextjs

2. 生产环境部署

# 创建网络(如果不存在)
docker network create app-network

# 设置环境变量
cp .env.example .env.production
vim .env.production

# 构建生产镜像
docker compose -f docker-compose.yml -f docker-compose.prod.yml build

# 启动服务
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

3. 更新部署

# 拉取最新代码
git pull

# 重新构建并更新
docker compose -f docker-compose.yml -f docker-compose.prod.yml build
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

三、性能优化

1. Docker 构建优化

  • • 使用多阶段构建减小镜像体积

  • • 合理使用构建缓存

  • • 优化依赖安装策略

  • • 仅复制必要文件

2. 运行时优化

# docker-compose.prod.yml 中的优化配置
services:
  nextjs:
    deploy:
      resources:
        limits:
          cpus: '1'
          memory: 1G
      update_config:
        order: start-first
        failure_action: rollback
    environment:
      - NODE_OPTIONS="--max-old-space-size=4096"

3. Nginx 配置优化

# deploy/nginx.conf
http {
    # 基础配置
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 开启 gzip
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss text/javascript;

    # 缓存配置
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;

    server {
        listen 80;
        server_name example.com;

        # 静态文件缓存
        location /_next/static {
            proxy_cache STATIC;
            proxy_ignore_headers Cache-Control;
            proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
            proxy_cache_valid 60m;
            proxy_pass http://nextjs:3000;
        }

        # 反向代理
        location / {
            proxy_pass http://nextjs:3000;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection 'upgrade';
            proxy_set_header Host $host;
            proxy_cache_bypass $http_upgrade;
        }
    }
}

四、监控和维护

1. 健康检查接口

// pages/api/health.ts
import type { NextApiRequestNextApiResponse } from 'next'

export default function handler(reqNextApiRequestresNextApiResponse) {
  res.status(200).json({
    status'healthy',
    timestampnew Date().toISOString(),
    uptime: process.uptime()
  })
}

2. 容器监控

# docker-compose.prod.yml
services:
  prometheus:
    image: prom/prometheus
    volumes:
      - ./deploy/prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    networks:
      - app-network

  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - app-network

3. 备份策略

#!/bin/bash
# deploy/backup.sh

# 设置备份目录
BACKUP_DIR="/backups/$(date +%Y%m%d)"
mkdir -p $BACKUP_DIR

# 数据库备份
docker compose exec -T postgres pg_dump -U $DB_USER $DB_NAME > $BACKUP_DIR/database.sql

# 静态文件备份
docker compose cp nextjs-app:/app/public $BACKUP_DIR/public

# 压缩备份
tar -czf $BACKUP_DIR.tar.gz $BACKUP_DIR
rm -rf $BACKUP_DIR

# 保留最近 7 天的备份
find /backups -name "*.tar.gz" -mtime +7 -delete

五、安全加固

1. 容器安全配置

# docker-compose.prod.yml
services:
  nextjs:
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp

2. 安全更新

# 自动安全更新脚本
#!/bin/bash
# deploy/update-security.sh

# 更新基础镜像
docker pull node:20-alpine
docker pull nginx:alpine
docker pull postgres:15-alpine

# 重建容器
docker compose -f docker-compose.yml -f docker-compose.prod.yml build --no-cache
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

六、部署检查清单

1. 构建前检查

  • • 所有环境变量是否配置正确

  • • .dockerignore 是否包含所有不需要的文件

  • • 依赖是否都已经更新到最新的稳定版本

  • • 代码是否已经通过所有测试

2. 部署检查

  • • 数据库连接是否正常

  • • 静态资源是否正确加载

  • • API 接口是否正常响应

  • • 缓存是否正常工作

  • • SSL 证书是否有效

3. 性能检查

  • • 页面加载时间是否在可接受范围

  • • 内存使用是否正常

  • • CPU 使用是否正常

  • • 数据库查询性能是否正常

4. 安全检查

  • • 所有服务是否使用最新的安全补丁

  • • 敏感信息是否通过环境变量注入

  • • 容器是否使用非 root 用户运行

  • • 防火墙规则是否正确配置


  • 更多关于 Next.js的全栈内容请点击下面的合集



字节笔记本
专注于科技领域的分享,AIGC,全栈开发,产品运营
 最新文章