使用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 Deploy → Run 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_PATH 对 SSH_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)或保留多版本回滚,请联系维护者扩展工作流(增加 environments 与 releases 目录策略)。

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

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
相关推荐
洋就在江州5 小时前
gitlab-cicd 离线集成——springboot-cicd (非docker形式,shell形式)
java·spring boot·后端·ci/cd·gitlab·gitlab-runner
oicola5 小时前
【运维】CI/CD是什么,合格的规范应该怎么设置
运维·ci/cd
tianyuanwo8 小时前
【软考高级系统架构设计师】05 - DevOps & CI/CD专题备考
ci/cd·系统架构·devops
白帽攻防录3 天前
SRC 挖洞:Claude Code Action 供应链 RCE 深度复盘,CVE-2026-47751 一个 PR 怎么偷走你所有 CI/CD 密钥
网络安全·ci/cd·供应链安全
hasty4 天前
安全扫描器也要被隔离:Trivy Terraform 文件读取漏洞与 CI 输出边界
安全·ci/cd·terraform
事圆则缓5 天前
Android 使用 Jenkins 实现 CI/CD,并用 SonarQube 建立质量门禁
android·ci/cd·jenkins
行者-全栈开发8 天前
【码动四季·秋】从 commit 到发版全自动:Conventional Commits + semantic-release 发布流水线实战
git·ci/cd·自动化发布·atomgit·semantic-releas·语义化版本自动化·git 提交规范落地
Zhou1411369 天前
CICD_01_持续集成与Jenkins入门
运维·ci/cd·jenkins
大貔貅喝啤酒9 天前
Gitea+Jenkins+Docker 搭建 Node 项目 CI/CD 自动部署完整教程
ci/cd·docker·jenkins·js·gitea
smartpi_ai10 天前
通用脱机烧录器为什么烧不进 CI-03?下载协议的门槛、免唤醒 10 条的建议值属性
服务器·网络·ci/cd