概述CI/CD（持续集成/持续部署）是现代 Web 开发的标准实践。对于 Nuxt 应用而言，由于涉及服务端渲染、预渲染、环境变量等多个环节，部署流程相对复杂。本文记录了 Nuxt 应用的 CI/CD 部署实践。部署架构流程概览开发者推送代码到 GitHub │ ▼ ┌─────────────────────────────┐ │ GitHub Actions 触发 │ │ ┌─────────────────────────┐│ │ │ 1. Checkout 代码 ││ │ │ 2. Setup Node.js ││ │ │ 3. npm ci ││ │ │ 4. 构建项目 ││ │ │ 5. 打包构建产物 ││ │ └─────────────────────────┘│ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ SCP 上传到服务器 │ └─────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ SSH 远程执行部署 │ │ ┌─────────────────────────┐│ │ │ 1. 清理旧构建产物 ││ │ │ 2. 解压新构建产物 ││ │ │ 3. PM2 重启服务 ││ │ └─────────────────────────┘│ └─────────────────────────────┘ │ ▼ 部署完成服务器架构 ┌─────────────┐ 用户请求 ──────►│ Nginx │ │ (端口 80) │ └──────┬──────┘ │ 反向代理 ▼ ┌─────────────┐ │ Nuxt │ │ (端口 3000)│ │ PM2 集群 │ └─────────────┘GitHub Actions 配置官方文档：https://docs.github.com/en/actions工作流文件创建 .github/workflows/deploy.yml：name: Deploy on: push: branches: - main workflow_dispatch: # 支持手动触发 jobs: build-and-deploy: runs-on: ubuntu-latest steps: # 1. 检出代码 - name: Checkout uses: actions/checkout@v4 # 2. 设置 Node.js 环境 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' cache: 'npm' # 3. 安装依赖 - name: Install dependencies run: | npm ci npm install --platform=linux --arch=x64 sharp # 4. 安装 jemalloc（可选，解决 sharp 内存问题） - name: Install jemalloc run: | sudo apt-get update -qq sudo apt-get install -y -qq libjemalloc2 > /dev/null 2>&1 # 5. 构建项目 - name: Build Project run: npm run build env: NODE_ENV: production NODE_OPTIONS: '--max-old-space-size=6144 --no-node-snapshot' VIPS_WARNING: '0' LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so.2 # 6. 打包构建产物 - name: Zip Output run: tar -czf release.tar.gz .output ecosystem.config.cjs # 7. 上传到服务器 - name: Upload to Server uses: appleboy/scp-action@v0.1.4 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} source: 'release.tar.gz' target: '/tmp' # 8. 执行部署 - name: Execute Deploy uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} script: | cd /opt/app rm -rf .output tar -xzf /tmp/release.tar.gz -C /opt/app rm /tmp/release.tar.gz pm2 delete app 2>/dev/null || true if [ -f /opt/app/.env ]; then set -a; source /opt/app/.env; set +a fi pm2 start ecosystem.config.cjs pm2 save关键配置说明跨平台依赖安装- name: Install dependencies run: | npm ci npm install --platform=linux --arch=x64 sharpCI 环境是 Linux，但开发环境可能是 Windows/macOS。npm ci 会根据当前平台安装依赖，需要额外安装 Linux 平台的 sharp 原生二进制。内存优化NODE_OPTIONS: '--max-old-space-size=6144 --no-node-snapshot'--max-old-space-size=6144：将 Node.js 堆内存上限设为 6GB--no-node-snapshot：禁用 V8 快照，减少内存占用jemallocsharp 依赖的 libvips 在某些环境下可能与 glibc 默认的内存分配器冲突，导致 munmap_chunk(): invalid pointer 崩溃。使用 jemalloc 可以解决这个问题：LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so.2GitHub Secrets 配置必需的 Secrets在 GitHub 仓库的 Settings → Secrets and variables → Actions 中配置：Secret 名称说明SERVER_HOST服务器 IP 地址SERVER_USERSSH 登录用户名SERVER_SSH_KEYSSH 私钥完整内容STUDIO_GITHUB_CLIENT_IDGitHub OAuth Client ID（如使用 Nuxt Studio）STUDIO_GITHUB_CLIENT_SECRETGitHub OAuth Client SecretSSH 密钥配置生成 SSH 密钥对：ssh-keygen -t ed25519 -C "github-actions" -f github-actions将公钥添加到服务器：ssh-copy-id -i github-actions.pub user@your-server将私钥内容添加到 GitHub Secrets 的 SERVER_SSH_KEY。GitHub OAuth App 配置如需使用 Nuxt Studio，需要配置 GitHub OAuth：前往 GitHub Developer Settings → OAuth Apps → New OAuth App填写信息：Homepage URL: https://your-domain.comAuthorization callback URL: https://your-domain.com/__nuxt_studio/auth/github创建后获取 Client ID 和 Client SecretPM2 配置官方文档：https://pm2.keymetrics.ioecosystem.config.cjsmodule.exports = { apps: [ { name: 'app', port: '3000', exec_mode: 'cluster', instances: 'max', script: './.output/server/index.mjs', max_memory_restart: '512M', env: { NODE_ENV: 'production', NITRO_PRESET: 'node-server', NUXT_PUBLIC_SITE_URL: 'https://example.com' } } ] }配置项说明配置项值说明exec_modecluster集群模式，利用多核 CPUinstancesmax启动与 CPU 核心数相同的进程max_memory_restart512M单进程内存超限自动重启script.output/server/index.mjsNuxt 服务端入口环境变量注意事项运行时不要设置 NUXT_STUDIO 环境变量。Nuxt 会将所有 NUXT_ 前缀的环境变量映射到 runtimeConfig。如果设置 NUXT_STUDIO='true'，会把 runtimeConfig.studio 从对象覆盖为字符串，导致运行时报错。NUXT_STUDIO 仅在构建时用于条件判断是否加载 nuxt-studio 模块。服务器环境配置目录结构/opt/app/ ├── .output/ # Nuxt 构建产物（CI 部署时覆盖） │ └── server/ │ └── index.mjs # 服务端入口 ├── ecosystem.config.cjs # PM2 配置（CI 部署时覆盖） └── .env # 运行时凭据（手动创建，不被覆盖）服务器 .env 文件# /opt/app/.env # Nuxt Studio GitHub OAuth（如使用） STUDIO_GITHUB_CLIENT_ID=your_client_id STUDIO_GITHUB_CLIENT_SECRET=your_client_secret STUDIO_GITHUB_REDIRECT_URL=https://example.com/__nuxt_studio/auth/github.env 文件不会被 CI 部署覆盖，首次部署时需要手动创建。Node.js 环境安装推荐使用 nvm 管理 Node.js 版本：# 安装 nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash source ~/.bashrc # 安装 Node.js 22 nvm install 22 nvm use 22 nvm alias default 22 # 安装 PM2 npm install -g pm2PM2 常用命令# 启动服务 pm2 start ecosystem.config.cjs # 查看状态 pm2 status # 查看日志 pm2 logs app # 实时监控 pm2 monit # 重启服务 pm2 restart app # 停止服务 pm2 stop app # 保存当前进程列表（开机自启） pm2 save # 生成开机自启脚本 pm2 startupNginx 配置反向代理配置# /etc/nginx/sites-available/app server { listen 80; server_name example.com www.example.com; # 重定向到 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name example.com www.example.com; # SSL 证书 ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # SSL 配置 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256; ssl_prefer_server_ciphers off; # 反向代理到 Nuxt location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } # 静态资源缓存 location /_nuxt/ { proxy_pass http://localhost:3000; proxy_cache static_cache; proxy_cache_valid 200 365d; proxy_cache_key $uri; add_header Cache-Control "public, max-age=31536000, immutable"; } # IPX 图片缓存 location /_ipx/ { proxy_pass http://localhost:3000; proxy_cache image_cache; proxy_cache_valid 200 7d; add_header Cache-Control "public, max-age=604800"; } }启用配置# 创建软链接 sudo ln -s /etc/nginx/sites-available/app /etc/nginx/sites-enabled/ # 测试配置 sudo nginx -t # 重载 Nginx sudo systemctl reload nginxNginx 官方文档：https://nginx.org/en/docs环境变量管理构建时 vs 运行时阶段变量来源用途构建时GitHub Actions env 块条件加载模块、注入凭据运行时服务器 .env 文件OAuth 认证、API 密钥环境变量前缀约定前缀处理方式示例NUXT_自动映射到 runtimeConfigNUXT_PUBLIC_SITE_URLSTUDIO_nuxt-studio 模块直接读取STUDIO_GITHUB_CLIENT_IDNITRO_Nitro 引擎配置NITRO_PRESET安全建议敏感信息不入库：OAuth 凭据、API 密钥等通过 Secrets 或 .env 管理最小权限原则：OAuth App 只请求必要的 scope定期轮换：定期更新密钥和令牌审计日志：监控异常登录和操作故障排查构建失败：OOM症状：JavaScript heap out of memory解决：增加 Node.js 堆内存NODE_OPTIONS: '--max-old-space-size=6144'构建失败：munmap_chunk症状：munmap_chunk(): invalid pointer解决：安装并使用 jemalloc- name: Install jemalloc run: sudo apt-get install -y libjemalloc2 - name: Build Project env: LD_PRELOAD: /usr/lib/x86_64-linux-gnu/libjemalloc.so.2PM2 进程异常重启排查命令：# 查看进程状态 pm2 status # 查看日志 pm2 logs app --lines 100 # 查看内存使用 pm2 monit如果频繁因内存超限重启，调整 max_memory_restart 值。部署后页面 404排查步骤：确认 .output/ 目录已正确解压确认 PM2 进程正在运行检查 Nginx 配置是否正确高级配置多环境部署# .github/workflows/deploy.yml jobs: deploy-staging: runs-on: ubuntu-latest if: github.ref == 'refs/heads/develop' environment: staging steps: # ... 部署到测试环境 deploy-production: runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' environment: production steps: # ... 部署到生产环境部署通知- name: Notify on Success if: success() uses: 8398a7/action-slack@v3 with: status: success webhook_url: ${{ secrets.SLACK_WEBHOOK }} - name: Notify on Failure if: failure() uses: 8398a7/action-slack@v3 with: status: failure webhook_url: ${{ secrets.SLACK_WEBHOOK }}部署检查清单首次部署服务器安装 Node.js 22+服务器安装 PM2配置 GitHub Secrets创建服务器 .env 文件配置 Nginx 反向代理配置 SSL 证书设置 PM2 开机自启每次部署检查 GitHub Actions 日志验证网站可访问检查 PM2 进程状态查看应用日志定期维护更新依赖版本轮换 OAuth 凭据检查 SSL 证书有效期清理旧日志和备份小结Nuxt 应用的 CI/CD 部署涉及多个环节。GitHub Actions 实现自动化构建和部署流程，PM2 提供进程管理和集群模式，Nginx 处理反向代理和静态资源缓存。环境变量需要在构建时和运行时分别管理，确保敏感信息安全。遇到问题时，可以通过日志和监控工具进行排查。参考资料：GitHub Actions 文档PM2 文档Nginx 文档Nuxt 部署文档