写在前面:在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 Deployon:# 推送到 main 分支时自动触发(如需改分支,请改这里)push:branches:- main# 允许在 Actions 页面手动触发workflow_dispatch:permissions:contents: readjobs:build-and-deploy:runs-on: ubuntu-latest# Job 级别环境变量:默认生产。构建阶段会临时切到 development 以安装 dev 依赖env:NODE_ENV: productionsteps:# 1) 拉取代码- name: Checkout repositoryuses: actions/checkout@v4# 2) 选择 Node 版本(与服务器一致;可改为 20 等)- name: Use Node.js 18uses: actions/setup-node@v4with:node-version: 18cache: 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" ]; thenprintf "%s" "$ENV_FILE_CONTENTS" > .env.productionfi# 4) 安装依赖(包含 devDependencies,避免构建缺包)- name: Install dependencies (with fallback, include dev deps)env:NPM_CONFIG_PRODUCTION: "false"NODE_ENV: developmentrun: |npm ci || npm install --legacy-peer-deps# 5) 构建(生产环境)- name: Buildenv:NODE_ENV: productionrun: 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 serveruses: appleboy/scp-action@v0.1.7with: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.3with:host: ${{ secrets.SSH_HOST }}username: ${{ secrets.SSH_USER }}password: ${{ secrets.SSH_PASSWORD }}port: ${{ secrets.SSH_PORT }}script_stop: truescript: |set -ecd ${{ secrets.REMOTE_PATH }}mkdir -p currentmv release.tar.gz current/cd currenttar -xzf release.tar.gzrm -f release.tar.gz# 二次兜底:如提供了 ENV_FILE_CONTENTS,这里也会写入(与构建前一致)if [ ! -z "${{ secrets.ENV_FILE_CONTENTS }}" ]; thenecho "${{ secrets.ENV_FILE_CONTENTS }}" > .env.productionfi# 服务器仅安装生产依赖,减小体积npm ci --omit=dev# 使用 PM2 平滑重载;若无进程则创建if command -v pm2 >/dev/null 2>&1; thenpm2 startOrReload ecosystem.config.js --env production || pm2 start ecosystem.config.js --env productionpm2 saveelsenpm i -g pm2pm2 start ecosystem.config.js --env productionpm2 savefi

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/921701.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/921701.shtml
英文地址,请注明出处:http://en.pswp.cn/news/921701.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java全栈工程师面试实录:从基础到实战的全面解析

Java全栈工程师面试实录&#xff1a;从基础到实战的全面解析 面试官&#xff1a;李明&#xff08;资深技术负责人&#xff09; 应聘者&#xff1a;张宇&#xff08;28岁&#xff0c;硕士学历&#xff0c;5年开发经验&#xff09; 第一轮&#xff1a;Java语言与JVM基础 李明&…

C#中解析XML时遇到注释节点报错

在C#中解析XML时遇到注释节点报错的问题&#xff0c;这是因为XML注释节点&#xff08;<!-- -->&#xff09;是特殊的节点类型。当遍历XML节点时&#xff0c;注释节点也会被包含在内&#xff0c;但它们不能像普通元素节点那样处理。 解决方案 方法1&#xff1a;跳过注释节…

9.3深度循环神经网络

目前为止&#xff0c;只讨论了具有一个单向隐藏层的循环神经网络&#xff0c;其中隐变量和观测值域具体的函数形式的交互方式是相当随意的。只要交互类型建模具有足够的灵活性&#xff0c;不是一个单问题。然而&#xff0c;对一个单层来说&#xff0c;可能具有相当的挑战性。之…

CSS in JS 的演进:Styled Components, Emotion 等的深度对比与技术选型指引

CSS in JS 的演进&#xff1a;Styled Components, Emotion 等的深度对比与技术选型指引在现代前端开发中&#xff0c;组件化思维已成为主流&#xff0c;而如何科学、高效地管理组件的样式&#xff0c;也随之成为了一个重要议题。CSS in JS&#xff08;JS中的CSS&#xff09;应运…

【正则表达式】 正则表达式的分组和引用

🌈 个人主页:(时光煮雨) 🔥 高质量专栏:vulnhub靶机渗透测试 👈 希望得到您的订阅和支持~ 💡 创作高质量博文(平均质量分95+),分享更多关于网络安全、Python领域的优质内容!(希望得到您的关注~) 🌵目录🌵 前言 🍱一、基本语法 🍘二、分组类型 🍙2.1.…

Grafana 导入仪表盘失败:从日志排查到解决 max\_allowed\_packet 问题

问题背景 近期在为项目搭建一套基于 Prometheus 和 Grafana 的可观测性体系。在完成基础部署后&#xff0c;我准备导入一个功能相对复杂的官方仪表盘模板&#xff0c;以便快速监控各项指标。然而&#xff0c;当上传仪表盘的 JSON 文件并点击保存时&#xff0c;Grafana 界面却反…

java对接物联网设备(一)——使用okhttp网络工具框架对接标准API接口

当前无论是在互联网领域&#xff0c;还是物联网项目下&#xff0c;亦或者各类应用类软件&#xff0c;基于http标准接口的对接是目前市面上最常见也是最简单的数据交互方式之一&#xff0c;甚至可以说是最流行的&#xff0c;因为它不依赖的各种插件或者服务。 开发者或者提供服…

版本管理系统与平台(权威资料核对、深入解析、行业选型与国产平台补充)

本文是一篇基于公开权威资料&#xff08;官方文档、产品页、厂商技术文章与技术社区讨论&#xff09;重新检索、核对后撰写的详尽博文。内容覆盖&#xff1a;版本控制基础、主流 VCS 工具深度比较、常见托管/协作平台&#xff08;含中国本土平台&#xff1a;Gitee / GitCode / …

计算机毕设选题:基于Python+Django的B站数据分析系统的设计与实现【源码+文档+调试】

精彩专栏推荐订阅&#xff1a;在 下方专栏&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f496;&#x1f525;作者主页&#xff1a;计算机毕设木哥&#x1f525; &#x1f496; 文章目录 一、项目介绍二…

Easy ES技术详解

从Java代码示例到高级特性 框架介绍 Easy-Es 是一款以 “简化 Elasticsearch 操作的 ORM 框架” 为核心定位的开源工具&#xff0c;旨在通过低代码设计降低 Elasticsearch 的使用门槛。作为国内 Top1 Elasticsearch 搜索引擎框架&#xff0c;其最显著的优势在于大幅缩减代码量…

【51单片机】【protues仿真】基于51单片机停车场的车位管理系统

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 一、主要功能 1、LCD1602液晶显示 2、统计并显示停车场现有车辆数和已停放过车辆数 3、按键设置总车位数以及剩余车位数 4、统计并显示累计驶入和累计驶出车辆数 5、用16个LED灯模拟停车位 6、车…

【Python】S1 基础篇 P4 if 语句指南

目录简单示例条件测试检查是否相等与不等检查多个条件检查特定的值是否在/不在列表中布尔表达式if语句简单的if语句if-else语句if-elif-else语句使用if语句处理列表检查特殊元素确定列表非空使用多个列表总结if 语句是Python编程中最基本也是最重要的控制结构之一。它允许程序根…

【实战中提升自己】内网安全部署之STP的安全技术部署

1 1拓扑 「模拟器、工具合集」复制整段内容 链接&#xff1a;https://docs.qq.com/sheet/DV0xxTmFDRFVoY1dQ?tab7ulgil1 STP的安全技术部署 说明&#xff1a;为什么需要注意STP的安全呢&#xff0c;在二层中其实存在很多不安全的因素&#xff0c;物理上…

GEM5学习(5): ARM 架构功耗仿真

运行脚本基于gem5提供的脚本&#xff0c;启动功耗仿真。实际工作中应该不会用gem5进行功耗的仿真吧&#xff0c;Cadence和Synopsys好像都有配套的的功耗建模工具。事先要配置好 IMG_ROOT的环境变量./build/ARM/gem5.opt configs/example/arm/fs_power.py \--caches \--bootl…

【Python基础】 19 Rust 与 Python if 语句对比笔记

一、基本语法对比 Rust if 语句 // 基本形式 let number 7;if number < 5 {println!("condition was true"); } else {println!("condition was false"); }// 多条件 else if if number % 4 0 {println!("number is divisible by 4"); } el…

Vue项目_项目配置脚本代码详细讲解

Vue项目代码详细讲解 1. jsconfig.json - JavaScript配置文件 {"compilerOptions": { // 编译器选项配置"target": "es5", // 编译目标&#xff1a;将代码编译为ES5版本&#xff0c;确保更好的浏览器兼容性"module": "esnext…

第一节:Vben Admin 最新 v5.0 (vben5) + Python Flask 快速入门

Vben Admin vben5 系列文章目录 &#x1f4bb; 基础篇 ✅ 第一节&#xff1a;Vben Admin 最新 v5.0 (vben5) Python Flask 快速入门 ✅ 第二节&#xff1a;Vben Admin 最新 v5.0 (vben5) Python Flask 快速入门 - Python Flask 后端开发详解(附源码) ✅ 第三节&#xff1a;V…

Guava中常用的工具类

1. 集合工具类&#xff08;com.google.common.collect&#xff09;Guava 对 Java 集合框架进行了丰富扩展&#xff0c;解决了标准集合的诸多痛点。&#xff08;1&#xff09;Lists / Sets / Maps:用于简化集合创建和操作&#xff1a;// 创建不可变集合&#xff08;线程安全&…

redission实现读写锁的原理

Redisson 实现分布式读写锁的核心原理是 ​基于 Redis 的 Lua 脚本原子操作​ ​Pub/Sub 通知机制&#xff0c;在保证强一致性的同时实现高效的读并发&#xff08;读不阻塞读&#xff0c;写阻塞读&#xff09;。以下是其核心设计&#xff1a;​一、核心数据结构​Redisson 使用…

【 ​​SQL注入漏洞靶场】第二关文件读写

SQLi-Labs​它是一个开源的、专门为学习 ​​Web安全​​ 和 ​​SQL注入技术​​ 而设计的靶场项目。开发者故意在代码中留下了各种不同类型的SQL注入漏洞&#xff0c;让安全研究人员、学生和爱好者可以在一个合法、安全的环境中进行实战练习&#xff0c;从而掌握发现和利用SQ…