JackyLove 的技术人生

人生艰难,唯有一技傍身才能慢慢走向通途。个人博客,记录生活,分享技术,记录成长。专注全栈技术,next技术。

第44章—实战篇ReactNotes服务器部署

首次发表于 2024-03-22, 更新于 2024-03-22

前言

假设我们的项目做完了,现在该部署上线了。你有两个方向可以选择:

  1. 部署到 Vercel
  2. 部署到自己的服务器

因为网络原因,国内无法直接访问 Vercel 。假如你要部署到自己的服务器,你有三种选择:

  1. 使用 Node.js Server
  2. 使用 Docker Image
  3. 使用静态导出

其中使用 Node.js Server,也就是我们目前开发的这种方式,主要步骤是将代码部署到服务器上,然后运行npm run start 启动 Node.js Server,适用于全栈项目。使用 Docker Image 的方式我们会在下篇文章讲到。静态导出适用于纯前端项目,在本地构建导出后,将构建的内容部署到服务器上。

本篇我们讲讲如何以 Node.js Server 的方式部署到自己的服务器上。我们先从买一个服务器开始说起。

服务器

买个服务器

现在我们买个服务器,这里我们以目前市场占有率第一的阿里云服务器为例,购买可以参考 《一篇从购买服务器到部署博客代码的详细教程》

假设我们已经买完了,得到了一个全新的服务器:

image.png

注:当然你也可以重置一个服务器,此过程叫做“云服务器初始化”,是指将云服务器恢复到最初创建的状态,类似手机的恢复出厂设置。因为阿里云服务器数据是保存在云盘上的,所以这个过程也叫做重新初始化云盘,具体操作参考阿里云文档《重新初始化系统盘》

获取登录密码

首先要做的应该是重置密码。根据阿里云文档的《重置密码操作说明》,重置一下密码,否则我们无法登陆服务器。

登陆服务器

获取密码后,我们尝试用命令行的方式登陆服务器:

# 语法:ssh 用户名@<实例的固定公网IP或EIP>

# 示例:
ssh root@39.100.83.124

# 输入实例登录密码
# 如果出现 Welcome to Alibaba Cloud Elastic Compute Service ! 表示成功连接到实例。

效果如下:

image.png

当重新登陆的时候,又要输入一次密码,为了能够免密码登陆,我们配置下公钥:

# 在本地起一个终端,获取本地公钥
cat ~/.ssh/id_rsa.pub

# 登陆服务器,将获取的公钥写入服务器的 authorized_keys
echo "这里修改为你的公钥内容" >> ~/.ssh/authorized_keys

这样我们再次登陆的时候就不需要输入密码了。注意,我们写入的是 ~ 目录里,这就意味着如果我们切换了用户,是需要再按照这个方式重新配置一遍的。

登陆后如果我们一段时间没有操作,再操作的时候就会断开连接。为了长时间保持 SSH 会话连接不断开,需要设置下心跳以保持连接,运行:

vim /etc/ssh/sshd_config

添加配置项:

ClientAliveInterval 600      
ClientAliveCountMax 10
TCPKeepAlive yes

ClientAliveInterval 600 表示 每 600 秒发送一次请求, 从而保持连接。ClientAliveCountMax 10 表示服务器发出请求后客户端没有响应的次数达到 10 次,就自动断开连接。所以无响应的 SSH 客户端将在大约 600 x 10 = 6000 秒后断开连接。

重启 sshd 服务,使配置生效:

systemctl restart sshd

装个宝塔

各种环境(Nginx、Redis、MySQL 等)慢慢装其实也可以,但这里为了简单起见,我们直接装个宝塔。宝塔是一款简单好用的 Linux/Windows 服务器运维管理面板。使用宝塔,我们可以快捷的安装环境、查看文件、修改配置等。

登录服务器后,运行脚本:

if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O install_panel.sh https://download.bt.cn/install/install_panel.sh;fi;bash install_panel.sh ed8484bec

这里我们用的是万能安装脚本,适用于不确定使用哪个 Linux 系统版本的情况。其他脚本内容请参考宝塔的安装说明

安装的时候需要输入一个 y进行确认:

image.png

然后就是等待安装,大概 1 到 2 分钟左右会安装完。安装成功后,你会看到:

image.png

及时保存这些信息。 出于安全考虑,宝塔使用的端口号是随机生成的端口号,从上图中可以看出,这次端口号开在了 14913,为了能够正常访问,需要在云服务器 ECS 的安全组中开启此端口号。

打开服务器控制台

截屏2024-01-22 下午5.51.15.png

添加此端口号,我们顺便把后面会用到的 3000(项目开启在 3000 端口)、80(HTTP 默认端口)、443(HTTPS 默认端口)顺便也都开启了:

image.png

然后打开宝塔给出的外网面板地址,因默认启用自签证书 https 加密访问,浏览器将提示不安全,点击【高级】-【继续访问】或【接受风险并继续】访问。

然后输入命令行中给出的用户名和密码。第一次登录还需要勾选同意协议,并绑定宝塔账号。绑定后,会进入到宝塔的主界面:

image.png

Nginx、MySQL 与 Node

首次登录宝塔的时候,宝塔会给出推荐安装套件提示:

image.png

当然你也可以在软件商店中一一安装:

截屏2024-01-22 下午9.28.34.png

这里我们至少需要把 Nginx 和 MySQL 安装了,至于 FTP、PHP、phpMyAdmin 则视个人情况选择。(我是直接在推荐的时候把 LNMP 都极速安装了。虽说是极速,安装还是需要一会的……)

然后安装 Node 环境。点击宝塔左侧的网站选项,打开 Node 项目,安装 Node 版本管理器:

截屏2024-01-22 下午7.03.04.png

如果列表中给出的 Node.js 版本比较老,你可以点击右侧的“更新版本列表”,选择一个合适的版本进行安装,这里我选择的是最新的稳定版 v20.11.0,选择后等待安装成功(安装的时候也会自动安装 PM2 模块):

截屏2024-01-23 下午2.48.45.png

注意安装完成后,图中的 “命令行版本” 要选择上,如果不选择的话,我们登录服务器,不会有 node 命令,也就是运行 node 相关的命令会报错。

FTP

如果你安装了 FTP,可以使用 FileZilla 这个 FTP 软件查看、编辑、上传、下载文件。Finder 虽然也可以连接 FTP 服务器,但无法上传、编辑、删除文件,功能有限。使用 FTP,首先要做一番配置:

打开服务器控制台-安全组

截屏2024-01-22 下午5.51.15.png

添加 202139000-40000 范围端口:

image.png

在宝塔的 “安全” 面板里也把这些端口添加一遍:

image.png

注:阿里云服务器的端口范围写法为 39000/40000,宝塔里的端口范围写法为 39000-40000,略有不同。

然后在 “软件商店” 中修改 PureFTPd 的设置:

截屏2024-01-24 下午4.19.05.png

在“配置修改”里,搜索 ForcePassiveIP,然后将其值更改为服务器的 IP:

截屏2024-01-24 下午4.21.44.png

回到宝塔的“FTP”界面,点击“添加 FTP”,添加一个用户名和密码,根目录设置为 /www/wwwroot,这是宝塔的默认建站目录,我们也会把项目的代码放到这个目录下。

截屏2024-01-24 下午4.24.10.png

添加用户后,点击查看快速连接,根据提示使用 FTP 工具连接即可:

截屏2024-01-24 下午4.26.31.png

成功连接后的效果如下:

image.png

域名

买个域名

现在我们买个域名,购买域名可以参考《一篇域名从购买到备案到解析的详细教程》

假设我们已经购买并备案好了。在阿里云域名控制台可以查到:

image.png

解析域名

现在我们把域名解析到我们的服务器地址上。点击左边的“解析”按钮,然后点击“添加记录”:

image.png

这里我们用的是二级域名 notes.yayujs.com,记录值填写我们购买的服务器地址。添加后,应该很快就会生效。

SSL 证书

阿里云提供免费的 SSL 证书(很多网站都提供的),最近证书的有效期从之前的 1 年改为了目前的 3 个月,虽然有些麻烦,但也能凑合着用。

参考《VuePress 博客优化之开启 HTTPS》,搞一份免费证书:

image.png

点击“下载”,服务器类型选择 nginx,点击“下载”,会下载证书文件,包含一个 .pem文件,一个 .key 文件。

这两个文件留着备用,后面开启 HTTPS 会用到。

部署 Next.js 项目

本地检查

现在我们尝试部署我们的项目,首先要保证自己的项目本身没有问题。所以我们在本地先 npm run build,然后 npm run start检查一下运行。为了简单起见,我们可以在脚本里再添加一个 prod命令,修改 package.json

{
  "name": "next-react-notes-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    // ...
    "prod": "next build && next start"
  },
  "dependencies": {
    // ...
  },
  "devDependencies": {
    // ...
  }
}

当运行 npm run prod的时候,就会先构建后运行,省的每次都要手动执行两个命令。

现在我们在本地运行生产版本,发现新建笔记的时候会报错:

image.png

在生产版本中,next-auth 并不认为 localhost:3000是值得信任的 Host,使用 next-auth 正常部署网站时,需要将 NEXTAUTH_URL 环境变量设置为网站的规范 URL:

NEXTAUTH_URL=https://example.com

如果开发的时候确实没有正式的地址,可以在 auth.js中强行设置 trustHost。修改 auth.js,添加 trustHost: true配置项:

import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import CredentialsProvider from "next-auth/providers/credentials";
import { addUser, getUser } from "@/lib/prisma";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers:[// ... ],
  pages: {
    signIn: '/auth/signin'
  },
  callbacks: {
  	// ...
  },
  trustHost: true
})

这样再运行 npm run prod的时候,就不会有 auth 报错了。不过我们既然已经有了域名,可以在根目录新建一个 .env.production文件,设置生产环境的 NEXTAUTH_URL

AUTH_URL=https://notes.yayujs.com

这样部署线上的时候就不会报 untrustedHost 错误了。

注:next-auth v5 里,AUTH_URLNEXTAUTH_URL 的别名。

创建 Git 项目

本地运行没啥问题,我们就可以把项目代码提交到服务器上。虽然可以连接服务器直接上传代码,但为了以后每次提交方便,我们在服务器上创建一个裸仓,这样每次更新代码,Git 提交到远程,服务器上的代码就会自动更新,这样使用起来更加方便。

登陆服务器,运行 yum install git,安装 Git,效果如下:

image.png

安装后,运行:

# 1. 进入目录,这是宝塔的默认建站目录
cd /www/wwwroot
# 2. 建个仓库文件夹
mkdir notes.git && cd notes.git
# 3. 创建裸仓库
git init --bare .

这个时候我们就获得了一个服务器上的仓库地址:

root@39.100.83.124:/www/wwwroot/notes.git

这里我们使用 git init --bare 初始化仓库,它与我们常使用的 git init 初始化的仓库不一样,你可以理解为它专门用来创建远程仓库,这种仓库只包括 git 版本控制相关的文件,不含项目源文件,所以我们需要借助一个 hooks,在有代码提交到该仓库的时候,将提交的代码迁移到其他目录,这里我们在 notes.git 同级目录下创建了一个 notes 文件夹,用于存放提交的源代码文件:

# 1. 进入 hooks 目录
cd hooks

# 2. 创建并编辑 post-receive 文件
vim post-receive

# 3. 这里是 post-receive 写入的内容,注意根据自己的实际情况修改
#!/bin/bash
git --work-tree=/www/wwwroot/notes checkout -f

# 4. 赋予执行权限
chmod +x post-receive

# 5. 退出目录到 notes.git 同级目录并创建项目目录
cd ../../ && mkdir notes

如果顺利的话,现在在本地打开我们的 React Notes 项目, commit 提交代码后,运行:

git push -f root@39.100.83.124:/www/wwwroot/notes.git master

以后每次修改代码、提交代码后,运行这行,代码就会自动更新到服务器上。

此时服务端的 notes 文件夹应该有我们提交的代码了,我们可以通过宝塔的“文件”查看:

image.png

MySQL 与 Prisma

目前服务器上的 MySQL 数据库还没有设置,我们直接在宝塔添加一个 MySQL 数据库:

image.png

这里我们创建了一个名为 notes 的数据库,用户名自动设置为 notes,你不能更改为 root。设置后你可以以 notes 用户加密码的方式访问这个数据库。这样我们写在 envDATABASE_URL 就要修改为:

DATABASE_URL="mysql://notes:WKNSjB3DeNB4xYje@localhost:3306/notes"

当然,我们也可以改 root 密码,把 root 密码改为 admin,这样连代码都不用改了……

image.png

当然处于安全的角度考虑,最好还是使用 notes 用户。项目根目录新建一个 .env.development文件,代码如下:

DATABASE_URL="mysql://root:admin@localhost:3306/notes"

.env 中设置线上的 DATABASE_URL:

// ...

DATABASE_URL="mysql://notes:i54f8znxfy2wh3w3@localhost:3306/notes"

还记得服务器代码修改的流程吗?本地代码修改后,正常运行 git addgit commit 命令后,运行:

git push -f root@39.100.83.124:/www/wwwroot/notes.git master

服务器代码就会发生改变,你可以通过宝塔的“文件”面板或者 FTP 工具查看服务器上的文件。

因为我们代码中还使用了 Prisma,目前 MySQL 数据库只是新建了数据库,但没有同步数据模型,所以我们登录服务器进入项目目录:

# 1. 进入项目目录
cd /www/wwwroot/notes
# 2. 安装依赖
npm install

如果 npm install 有网络问题,可以设置 npm 镜像:

# 设置成 npm 镜像
npm config set registry https://registry.npmmirror.com
# 改回来
npm config set registry https://registry.npmjs.org/

这个时候我们执行 prisma 相关的命令,我们先运行 npx prisma -v 试试,大概率会出现这个错:

image.png

如果出现这个错误,运行:

# 1. 编辑环境变量
vim ~/.bashrc
# 2. 添加环境变量
export PRISMA_ENGINES_MIRROR=https://registry.npmmirror.com/-/binary/prisma
# 3. 保存退出后运行
source ~/.bashrc

然后再次运行 npx prisma -v试试:

image.png

这说明 prisma 已经可以正常使用了。此时我们的数据模型要同步数据库。

如果你把迁移时生成的 prisma/migrations下的文件也都提交了,那你可以在服务器上运行:

npx prisma migrate deploy

正常部署效果如下:

image.png

然后运行 npx prisma generate初始化 Prisma Client:

npx prisma generate

如果你没有提交,反正也是初始化,你可以运行:

npx prisma migrate dev

此时通过 Prisma,MySQL 的数据表也设置完毕。

开启项目

现在我们运行 npm run prod试试,应该能顺利的运行在 3000 端口:

image.png

然后这个时候访问 http://39.100.83.124:3000/可能还打不开,因为 3000 端口还没有开启,先在宝塔的“安全”面板上开启:

image.png

我们之前只是在阿里云 ECS 的安全组中开启了 3000(项目开启在 3000 端口)、80(HTTP 默认端口)、443(HTTPS 默认端口),这里需要在宝塔里也开启一遍。

此时打开 http://39.100.83.124:3000/ 应该能正常访问了:

image.png

不过点击 Sign In 的时候还会报错,不着急,我们慢慢解决,先学习点基础知识。

Screen 命令

现在我们先在服务器上关掉项目的 node 程序。如果 ssh 还在保持连接,会话还在,那直接 control + c 也就断开了。但如果 ssh 断开了连接,恢复之前的会话就有些麻烦了。搞不好还要先强行关闭 3000 端口:

# 1. 获取端口进程 PID
lsof -i:3000
# 2. 关闭 PID
kill -9 xxxx

这个时候就要说到 Screen 了。Screen 是 Linux 下的一个多重视窗管理程序。拥有多窗口、会话恢复、会话共享等功能。所谓会话恢复,对于远程登录的用户,即使连接中断,用户也不会失去对已经打开的命令行会话的控制。只用登录到服务器,运行 screen -r即可恢复会话。

登陆服务器,安装 Screen:

yum install screen

Screen 的命令并不多,简单介绍下常用到的:

Screen 的状态分为两种:Attached(前台运行) 和 Detached(后台运行)。

通过 screen -S xxx可以新建一个会话,并进入 Attached 状态。如果该程序要长期运行,但你要暂时离开该会话,Control + A Control + D,该会话就会进入 Detached 状态。当你再登陆进来的时候,可以通过 screen -ls查看有哪些会话,然后通过 screen -r xxxx恢复会话,将会话从 Detached 状态转为 Attached 状态。

如果你要进入 Attached 的会话,你需要先通过 screen -d xxxx,将其状态转为 Detached,才能通过 screen -r xxxx 进入会话。这个过程也可以合并成 screen -d -r xxxx一个命令。

如果你要删除某个 screen,运行 screen -S xxxx -X quit

PM2 命令

PM2 是部署 node 项目常用的一个工具。正常我们要安装 pm2 这个模块,但我们在宝塔装 Node.js 版本管理器的时候,宝塔已经为我们安装了 PM2 模块,所以登陆服务器,直接就有 pm2 命令了,不信你运行 pm2 -v试试。

PM2 是一个 Node.js 进程管理器,使用 PM2,可以实现性能监控、自动重启、负载均衡等。让我们看下常用的命令吧:

# 启动
pm2 start app.js
# 声明一个名字
pm2 start app.js --name app_name
# 监听文件变化
pm2 start python-app.py --watch

# 列表
pm2 list
# 监控
pm2 monit

# 重载
pm2 reload app_name
# 重启
pm2 restart app_name
# 停止
pm2 stop app_name
# 删除
pm2 delete app_name

我们来看这样一个命令:

pm2 start npm --watch --name notes -- run prod

这个命令的意思是以 pm2 开启 npm run prod脚本命令,此进程命名为 notes,并监听文件变化。

配置 Nginx 和 HTTPS

现在回到我们的项目上。在宝塔的“网站”面板中添加 Node 项目,相关配置如图:

截屏2024-01-24 下午8.17.54.png

添加成功后:

image.png

此时宝塔已经为我们的项目做好了 Nginx 配置,可以在设置中查看:

截屏2024-01-24 下午8.26.30.png

宝塔做的 Nginx 配置为监听 notes.yayujs.com80端口,然后代理到 http://127.0.0.1:3000

我们在 Screen 中运行我们的 Next.js 项目吧:

# 1. 登陆服务器
screen -S notes
# 2. 进入程序目录
cd /www/wwwroot/notes
# 3. 运行脚本
pm2 start npm --watch --name notes -- run prod

稍等一会,等待编译构建完成,我们再访问 http://notes.yayujs.com/应该可以正常运行了:

image.png

但是当我们点击 Sign In的时候,依然会报错。服务器上的报错为:

image.png

之所以会有这个报错,是因为我们的 Nginx 配置中 x-forwarded-host标头带了端口号,在宝塔上修改 Nginx 配置,把下图中标示的 $server_port 去除即可。

截屏2024-01-24 下午8.37.31.png

Ctrl+S 保存后,Nginx 配置就会自动生效。此时再访问 http://notes.yayujs.com/,点击 Sign In,虽然还是报错,但至少不报刚才那个错了……哈哈哈哈

现在让我么来解决 HTTPS 的问题吧。点击 Node 项目的“未部署”按钮:

截屏2024-01-24 下午8.42.08.png 还记得之前在阿里云服务器上下载的 .key.pem文件吗?用编辑器打开这两个文件,将这两个文件中的内容复制到这两个输入框中,然后点击“保存并启用证书”。宝塔会自动更新 Nginx 配置:

image.png

可以看到,Nginx 配置更新,监听 HTTPS 的 443 端口,代理到 3000 端口。

此时访问 https://notes.yayujs.com/,依然可以正常访问,不仅如此,点击 "Sign In" 也能正常登录了,项目运行正常:

image.png

此外,因为我们使用的是 pm2 运行的项目,并设置了监听文件变化,所以当你通过 git push -f root@39.100.83.124:/www/wwwroot/notes.git master推送代码到服务器的时候,因为文件变化,pm2 会自动重启该应用。

宝塔 Nginx

关于宝塔的 Nginx,我们多说一点,了解就行,以防万一用到。

正常我们登陆服务器直接安装 Nginx,比如参考《一篇从购买服务器到部署博客代码的详细教程》中的方式,Nginx 的配置文件是放在 /etc/nginx目录下的。但是宝塔装的 Nginx 并没有放在该目录下。

此外,因为宝塔面板内有些包是未托管到服务器 systemd 包管理内的,所以 systemctl 是无法正常使用的。也就是说,使用 systemctl status nginx.service这种命令是无法准确判断 nginx 状态的,当然也无法重启重载等。

宝塔的 Nginx 安装在了 /www/server/nginx,配置文件在 /www/server/nginx/conf/nginx.conf,其内容可以通过目录直接查找,也可以在 Nginx 的设置中查看:

截屏2024-01-24 下午9.05.10.png

这是 nginx 的主配置文件,在这个文件中底部有一句:

 include /www/server/panel/vhost/nginx/*.conf;

打开此目录:

image.png

这其实就是我们具体项目的 Nginx 配置,可以在具体项目的设置里查看:

截屏2024-01-24 下午8.26.30.png

如果你要操作宝塔的 Nginx 配置,可以在命令行中运行:

# 启动
/etc/init.d/nginx start

# 停止
/etc/init.d/nginx stop

# 重启
/etc/init.d/nginx restart

# 重载
/etc/init.d/nginx reload

参考链接

  1. https://juejin.cn/post/7049692191110725645
  2. https://authjs.dev/getting-started/providers/oauth-tutorial#setting-up-nextauth_url
© Copyright 2025 JackyLove 的技术人生. Powered with by CreativeDesignsGuru