Docker + Next.js 部署实战教程
浏览量:加载中...
Next.js + Docker + PM2 + Nginx

一、环境准备与服务器搭建
1. 服务器基础环境
1操作系统:Linux (CentOS、Alpine、Ubuntu 等均可,示例用的是 Alpine 基础镜像) 2 3Node.js:使用官方 Node 20 Alpine 版本镜像 4 5包管理工具:pnpm 6 7进程管理:PM2 8 9容器管理:Docker 和 Docker Compose 10 11反向代理:Nginx 12
2.特点与需求
1使用 Next.js(含 SSR 和静态资源) 2 3使用 pnpm 管理依赖 4 5构建产物包含 standalone 文件夹,方便容器运行时无需额外依赖 6 7Docker 容器化部署,要求镜像尽量轻量且高效缓存 8 9nginx 反向代理,支持 HTTPS,使用 Certbot 自动申请和续期证书 10 11自动重载 nginx 配置,保障证书续期后服务不中断 12 13静态资源做长缓存,页面和 API请求走代理转发 14
二、项目结构与代码准备
1/root/cheche-deploy/ 2├── Dockerfile 3├── docker-compose.yml 4├── nginx/ 5│ └── cheche-blog.conf 6├── nginx-out/ # 本地存放 Nginx 配置供脚本使用 7├── package.json 8├── pm2.config.js 9├── scripts/ 10│ └── deploy.sh 11├── src/ 12├── public/ 13└── ...
nginx/cheche-blog.conf 是 Nginx 配置文件的原始存放目录
nginx-out/ 是本地用于同步和部署时复制的目录
scripts/deploy.sh 是自动构建、启动和同步 Nginx 配置的脚本
三、Dockerfile 编写
1 使用官方 Node 运行环境(本地已导入 node:20-alpine) 2FROM node:20-alpine 3 4 安装全局工具 pnpm 和 pm2 5RUN npm install -g pnpm pm2 6 7 设置工作目录 8WORKDIR /app 9 10 只先拷贝锁文件和 package.json,用于依赖安装缓存 11COPY pnpm-lock.yaml package.json ./ 12 13 安装依赖,锁定版本,防止自动更新依赖 14 network-concurrency 降低网络压力,避免卡死 15RUN pnpm install --frozen-lockfile --network-concurrency=1 16 17 后续复制完整源码,包括构建脚本和 .next 文件夹 18COPY . . 19 20 限制 Node 最大内存,防止构建时内存溢出卡死 21ENV NODE_OPTIONS="--max-old-space-size=1024" 22 23 运行构建命令,生成生产环境产物(含 .next/standalone) 24RUN pnpm run build 25 26 拷贝构建产物,确保 .next 全部拷贝,静态资源和 public 目录完整 27 ✅ 拷贝构建产物,确保 .next 所有关键文件完整 28RUN mkdir -p /app/deploy/standalone/.next \ 29 && cp -r .next/standalone/* /app/deploy/standalone/ \ 30 && cp -r .next/* /app/deploy/standalone/.next/ \ 31 && cp -r public /app/deploy/standalone/public \ 32 && cp pm2.config.js /app/deploy/standalone/pm2.config.js 33 34 35 设置运行时工作目录为部署产物目录 36WORKDIR /app/deploy/standalone 37 38 容器监听端口 39EXPOSE 3000 40 41CMD ["pm2-runtime", "pm2.config.js"]
四、docker-compose.yml 示例
1version: '3.9' 2 3services: 4 cheche-blog: 5 build: 6 context: . 7 dockerfile: Dockerfile 8 args: 9 REPO: https://github.com/user/cheche-blog.git 10 BRANCH: main 11 container_name: cheche-blog 12 ports: 13 - '3000:3000' 14 # volumes: 15 # - ./nginx-out:/nginx-out # 复制 nginx 配置出容器 16 restart: always
五、scripts/deploy.sh 自动化部署脚本
自动申请证书 续期证书
1#!/bin/bash 2set -euo pipefail 3 4DOMAIN="thomasche.top" 5EMAIL="thomaschefowshu@gmail.com" 6NGINX_CONF_SRC="./nginx/cheche-blog.conf" 7NGINX_CONF_DST="/etc/nginx/conf.d/cheche-blog.conf" 8CERTBOT_RENEW_HOOK="/usr/local/bin/certbot-renew-hook.sh" 9 10echo -e "🚀 构建并启动 Docker 容器..." 11docker compose up -d --build 12 13if [ -f "$NGINX_CONF_SRC" ]; then 14 echo -e "📄 复制 nginx 配置文件到系统路径..." 15 sudo cp "$NGINX_CONF_SRC" "$NGINX_CONF_DST" 16else 17 echo "❌ 未找到 nginx 配置文件: $NGINX_CONF_SRC" 18 exit 1 19fi 20 21echo -e "🔧 创建 certbot 自动续期钩子..." 22sudo tee "$CERTBOT_RENEW_HOOK" > /dev/null <<EOF 23#!/bin/bash 24set -e 25echo "[\$(date)] 证书续期成功,正在重载 nginx..." 26sudo nginx -s reload 27EOF 28sudo chmod +x "$CERTBOT_RENEW_HOOK" 29 30echo -e "📜 申请或续期证书(自动配置 nginx)..." 31sudo certbot --nginx -d "$DOMAIN" \ 32 --agree-tos --non-interactive -m "$EMAIL" \ 33 --deploy-hook "$CERTBOT_RENEW_HOOK" 34 35echo -e "🔍 测试 nginx 配置..." 36sudo nginx -t 37 38echo -e "🔁 重载 nginx..." 39sudo nginx -s reload 40 41echo -e "\n✅ 部署完成!访问地址:https://$DOMAIN" 42
六、.dockerignore 优化(减小构建上下文体积)
1node_modules 2.git 3.gitignore
七、nginx 配置
单文件,Certbot 管理证书
一些通用的放在通用的 nginx 上做处理
1 2server { 3 listen 443 ssl; 4 server_name thomasche.top; 5 6 ssl_certificate /etc/letsencrypt/live/thomasche.top/fullchain.pem; 7 ssl_certificate_key /etc/letsencrypt/live/thomasche.top/privkey.pem; 8 include /etc/letsencrypt/options-ssl-nginx.conf; 9 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; 10 11 client_max_body_size 50m; 12 13 # 安全头部 14 add_header X-Frame-Options "SAMEORIGIN"; 15 add_header X-XSS-Protection "1; mode=block"; 16 add_header X-Content-Type-Options "nosniff"; 17 18 # # 🔐 静态资源缓存策略(如 js/css/img 等) 19 # location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|ttf|svg|eot|otf|mp4|webm)$ { 20 # expires 7d; 21 # access_log off; 22 # add_header Cache-Control "public, max-age=604800, immutable"; 23 # } 24 25 # # 🔐 Next.js 静态资源路径(如 /_next/static) 26 # location ~* ^/_next/static/ { 27 # expires 7d; 28 # access_log off; 29 # add_header Cache-Control "public, max-age=604800, immutable"; 30 # } 31 32 # ⬅️ 应用入口(默认反向代理) 33 location / { 34 proxy_pass http://127.0.0.1:3000; 35 proxy_http_version 1.1; 36 proxy_set_header Host $host; 37 proxy_set_header X-Real-IP $remote_addr; 38 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 39 proxy_set_header Upgrade $http_upgrade; 40 proxy_set_header Connection "upgrade"; 41 42 # 缓存 43 proxy_cache html_cache; 44 proxy_cache_valid 200 302 1m; 45 proxy_cache_valid 404 1m; 46 proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; 47 add_header X-Cache-Status $upstream_cache_status; 48 } 49} 50 51server { 52 listen 80; 53 server_name thomasche.top; 54 return 301 https://$host$request_uri; 55}
八、pm2.config.js
1module.exports = { 2 apps: [ 3 { 4 name: 'cheche-blog', 5 script: 'server.js', 6 cwd: '/app/deploy/standalone', // 运行时目录,和 Dockerfile WORKDIR 保持一致 7 instances: 1, 8 exec_mode: 'fork', // 单进程模式,Next.js 通常用这个 9 watch: false, // 生产环境关闭文件监听 10 env: { 11 NODE_ENV: 'production', 12 NEXT_PUBLIC_SUPABASE_URL: 'https://fusrmsbzfmnicfsbyjzd.supabase.co', 13 NEXT_PUBLIC_SUPABASE_ANON_KEY: 14 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3Jtc2J6Zm1uaWNmc2J5anpkIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI2NTgwNDgsImV4cCI6MjA2ODIzNDA0OH0.xtmEdoPwsT_O8WqNnpvg-eHNst33O-ncc3B4rJ8MqUA', 15 SUPABASE_SERVICE_ROLE_KEY: 16 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ1c3Jtc2J6Zm1uaWNmc2J5anpkIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc1MjY1ODA0OCwiZXhwIjoyMDY4MjM0MDQ4fQ.GivYH9R0W7sElFgt-9TL1_KQ7TAhaDbRMr9Xj-2nuFg', 17 }, 18 }, 19 ], 20}; 21
九、常见问题汇总与解决方案
十、构建与部署流程总结
本地或服务器拉取最新代码
执行 ./deploy.sh,自动构建 Docker 镜像并启动容器
1 2[root@VM-12-6-opencloudos cheche-deploy]# chmod +x ./scripts/deploy.sh 3[root@VM-12-6-opencloudos cheche-deploy]# ./scripts/deploy.sh 4
自动复制 nginx 配置到 /etc/nginx/conf.d/
自动申请或续期 HTTPS 证书,并自动创建续期后重载 nginx 的钩子
检查 nginx 配置并重载,使证书生效
访问 https://thomasche.top,支持 HTTPS,静态资源带缓存,API 代理转发到 Node 容器
十一、构建产物说明
/app/deploy/standalone:Next.js 15 版本的独立运行包,包含服务端启动文件 server.js 和静态资源
相关静态资源复制到对应目录确保独立运行
PM2 负责管理 Node 服务,使用 pm2.config.js 配置启动参数
十二、后续优化
构建缓存提升:可使用 Docker BuildKit 的缓存机制或本地缓存服务加速依赖安装
日志管理:nginx 和 pm2 日志分离,定期收集和清理
安全防护:nginx 增加安全 headers(如 HSTS、X-Frame-Options 等)
性能监控:集成 pm2 监控面板或使用 Prometheus 监控服务健康
备份机制:定期备份 nginx 配置、证书文件及重要数据
多环境支持:增加 staging 环境配置,部署测试更安全