使用CI/CD部署项目(前端Nextjs)

发布于:2025-09-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

写在前面:在github上使用CI/CD部署Nextjs项目,具体配置可以按照自己的实际的修改

这是我的项目配置,仅供参考
在这里插入图片描述
后端项目可以参考使用CI/CD部署后端项目

正文开始

项目名(PROJECT_NAME)- CI/CD 部署指南(GitHub Actions + SSH + PM2)

本项目已内置基于 GitHub Actions 的 CI/CD。它会在 main 分支有变更或手动触发时:

  • 安装依赖并构建 Next.js 产物
  • 通过 SCP 将构建产物上传至服务器指定目录
  • 通过 SSH 调用 PM2 平滑重载运行中的服务

1. 文件位置

  • 工作流文件(部署文件示例已经放到文末):.github/workflows/deploy.yml

2. 前置条件

  • 服务器已安装 Node.js 18.x 与 npm(与工作流一致)
  • 服务器已安装 PM2:npm i -g pm2
  • 服务器部署目录存在且对 SSH 用户可写,例如:/var/www/PROJECT_NAME

3. 仓库 Secrets 配置

在 GitHub → 仓库 → Settings → Secrets and variables → Actions 中添加:

必填

  • SSH_HOST:服务器 IP/域名
  • SSH_USER:SSH 用户名
  • SSH_PORT:SSH 端口(如 22)
  • SSH_PASSWORD:SSH 登录密码(如改用密钥见文末)
  • REMOTE_PATH:服务器部署根目录(例如 /var/www/PROJECT_NAME

可选

  • ENV_FILE_CONTENTS:用于生成 .env.production 的完整文本。例如:
    NEXT_PUBLIC_API_URL=https://api.your-domain.com
    NEXT_PUBLIC_LANGUAGE=en
    NEXT_PUBLIC_WALLETCONNECT_ID=xxxxxxx
    

说明

  • 工作流会在“构建前”把 ENV_FILE_CONTENTS 写为 .env.production,确保 NEXT_PUBLIC_* 变量参与 Next.js 打包。

4. 服务器目录结构(默认)

工作流会将 release.tar.gz 上传至 REMOTE_PATH 并解压到 REMOTE_PATH/current 下:

REMOTE_PATH/
  └── current/
      ├── .next/
      ├── public/
      ├── ecosystem.config.js
      ├── package.json
      ├── package-lock.json
      ├── .env.production (可选)
      └── ...

5. 触发部署

  • 自动:向 main 分支推送代码会自动触发
  • 手动:GitHub → Actions → 选择 CI/CD DeployRun workflow → 选择 main

6. 运行流程概览

  1. Checkout 代码
  2. 使用 Node 18 安装依赖(包含 devDependencies)并构建
  3. 压缩构建产物与必要文件为 release.tar.gz
  4. 通过 SCP 上传到服务器 REMOTE_PATH
  5. 通过 SSH:
    • 解压到 REMOTE_PATH/current
    • 写入 .env.production(如提供)
    • npm ci --omit=dev
    • pm2 startOrReload ecosystem.config.js --env production

7. PM2 常用命令

pm2 ls                       # 查看进程
pm2 logs --lines 100         # 查看日志
pm2 restart <name|id>        # 重启
pm2 stop <name|id>           # 停止
pm2 delete <name|id>         # 删除

8. 回滚思路(简易)

当前流程将产物解压到 current/。若需要回滚,推荐:

  • 在服务器保留历史版本目录(可扩展工作流增加 releases/ 与符号链接),或
  • 临时将上一份稳定包重新上传并覆盖 current/pm2 reload

9. 常见问题与排查

  • 构建期报 Cannot find module 'xxx':确保安装步骤包含 devDependencies(本工作流已处理)。
  • SCP/SSH 失败:检查 SSH_HOST/USER/PORT/PASSWORD 是否正确,服务器防火墙、安全组、端口开放情况。
  • 权限问题:确保 REMOTE_PATHSSH_USER 可写,如需:sudo chown -R <user>:<user> /var/www/PROJECT_NAME
  • 环境变量不生效:确认 ENV_FILE_CONTENTS 已填写,变量名与代码中一致(例如 NEXT_PUBLIC_API_URL)。

10. 切换为 SSH 密钥登录(可选,更安全)

  1. 本地生成密钥:
ssh-keygen -t ed25519 -C "deploy" -N "" -f ~/.ssh/PROJECT_NAME_deploy
  1. ~/.ssh/PROJECT_NAME_deploy.pub 追加到服务器 ~/.ssh/authorized_keys
  2. 在仓库 Secrets 新增:SSH_KEY(粘贴私钥全文),并把工作流中 password: ${{ secrets.SSH_PASSWORD }} 改为 key: ${{ secrets.SSH_KEY }}(scp/ssh 两处)

11. 调整 Node 版本

  • 服务器与工作流默认使用 Node 18。如需升级:同时升级服务器 Node 与工作流的 actions/setup-node 版本号,保持一致。

如需灰度、分环境(staging/prod)或保留多版本回滚,请联系维护者扩展工作流(增加 environmentsreleases 目录策略)。

附:示例工作流(脱敏,含注释与可改项)

# 工作流名称,会显示在 Actions 列表中
name: CI/CD Deploy

on:
  # 推送到 main 分支时自动触发(如需改分支,请改这里)
  push:
    branches:
      - main
  # 允许在 Actions 页面手动触发
  workflow_dispatch:

permissions:
  contents: read

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    # Job 级别环境变量:默认生产。构建阶段会临时切到 development 以安装 dev 依赖
    env:
      NODE_ENV: production
    steps:
      # 1) 拉取代码
      - name: Checkout repository
        uses: actions/checkout@v4

      # 2) 选择 Node 版本(与服务器一致;可改为 20 等)
      - name: Use Node.js 18
        uses: actions/setup-node@v4
        with:
          node-version: 18
          cache: npm

      # 3) 写入 .env.production(可选)
      #    值来自仓库 Secret: ENV_FILE_CONTENTS(整段文本,包含多行 KEY=VALUE)
      - name: Create .env.production from secrets (if provided)
        env:
          ENV_FILE_CONTENTS: ${{ secrets.ENV_FILE_CONTENTS }}
        run: |
          if [ -n "$ENV_FILE_CONTENTS" ]; then
            printf "%s" "$ENV_FILE_CONTENTS" > .env.production
          fi

      # 4) 安装依赖(包含 devDependencies,避免构建缺包)
      - name: Install dependencies (with fallback, include dev deps)
        env:
          NPM_CONFIG_PRODUCTION: "false"
          NODE_ENV: development
        run: |
          npm ci || npm install --legacy-peer-deps

      # 5) 构建(生产环境)
      - name: Build
        env:
          NODE_ENV: production
        run: npm run build

      # 6) 仅打包需要的文件(如需额外文件,按需在此补充)
      - name: Prepare artifact (ship only what is needed)
        run: |
          tar -czf release.tar.gz \
            .next \
            public \
            package.json \
            package-lock.json \
            next.config.js \
            ecosystem.config.js \
            tsconfig.json \
            postcss.config.js \
            tailwind.config.js

      # 7) 上传产物到服务器(以下 5 个值均来自仓库 Secrets)
      #    - SSH_HOST:服务器 IP/域名(需改为你的)
      #    - SSH_USER:SSH 用户名(需改为你的)
      #    - SSH_PASSWORD:SSH 密码(如改用密钥见文档)
      #    - SSH_PORT:SSH 端口(默认 22,可按需修改)
      #    - REMOTE_PATH:部署目录(需改为你的,例如 /var/www/your-app)
      - name: Upload artifact to server
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          password: ${{ secrets.SSH_PASSWORD }}
          port: ${{ secrets.SSH_PORT }}
          source: "release.tar.gz"
          target: ${{ secrets.REMOTE_PATH }}

      # 8) 服务器上解压、装产线依赖并用 PM2 启动/热重载
      - name: Deploy on server (extract, install prod deps, reload pm2)
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          password: ${{ secrets.SSH_PASSWORD }}
          port: ${{ secrets.SSH_PORT }}
          script_stop: true
          script: |
            set -e
            cd ${{ secrets.REMOTE_PATH }}
            mkdir -p current
            mv release.tar.gz current/
            cd current
            tar -xzf release.tar.gz
            rm -f release.tar.gz
            # 二次兜底:如提供了 ENV_FILE_CONTENTS,这里也会写入(与构建前一致)
            if [ ! -z "${{ secrets.ENV_FILE_CONTENTS }}" ]; then
              echo "${{ secrets.ENV_FILE_CONTENTS }}" > .env.production
            fi
            # 服务器仅安装生产依赖,减小体积
            npm ci --omit=dev
            # 使用 PM2 平滑重载;若无进程则创建
            if command -v pm2 >/dev/null 2>&1; then
              pm2 startOrReload ecosystem.config.js --env production || pm2 start ecosystem.config.js --env production
              pm2 save
            else
              npm i -g pm2
              pm2 start ecosystem.config.js --env production
              pm2 save
            fi

网站公告

今日签到

点亮在社区的每一天
去签到