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

写在前面:在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 常用命令

bash 复制代码
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. 本地生成密钥:
bash 复制代码
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 目录策略)。

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

yaml 复制代码
# 工作流名称,会显示在 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
相关推荐
marsh020611 小时前
39 openclaw持续集成实践:自动化构建与部署流程
运维·ci/cd·ai·自动化·编程·技术
byoass11 小时前
企业云盘API集成指南:如何与CI/CD流水线打通
网络·安全·ci/cd·云计算
qq_4523962313 小时前
第十四篇:《持续集成中的UI自动化:Jenkins/GitHub Actions集成》
ui·ci/cd·自动化
开开心心就好13 小时前
支持批量添加水印的实用工具推荐
人工智能·游戏·ci/cd·docker·音视频·语音识别·媒体
魏波.13 小时前
Harness工程与传统CI/CD流水线的区别?
ci/cd·harness
测试那点事儿13 小时前
零基础接口自动化到 Jenkins 持续集成(导读)
ci/cd·自动化·jenkins
ℳ₯㎕ddzོꦿ࿐1 天前
告别手工发版:用 GitLab CI/CD 打通前后端自动化部署的“任督二脉”
ci/cd·自动化·gitlab
mascon1 天前
CI/CD 标准化(自动流水线)
ci/cd
ℳ₯㎕ddzོꦿ࿐2 天前
实战篇:结合 GitLab CI/CD 实现 Spring Cloud 微服务自动化部署与防坑指南
spring cloud·ci/cd·gitlab
运维全栈笔记2 天前
零基础掌握Jenkins CI/CD:Java项目自动构建与部署全流程指南
git·servlet·ci/cd·gitee·自动化·jenkins·devops