使用 Jenkins 将个人项目部署至阿里云上:Vue3 + Nestjs + MongoDB

一、购买并启动云服务器【Aliyun】

阿里云的优惠活动还是蛮多的,前段时间出了个 2核2G 99/1年 的云服务器,正好自己最近也在一边学习 Nestjs,一边写点功能当作练习,想着可以试试把自己的项目部署到服务器上。

购买服务器并不复杂,按照官网指引,填写好相关配置并付费即可。

购买完成后前往 工作台 --> 实例 --> 你的服务器实例,实例详情如下,包括公私网IP、实例状态、操作系统等。

至于连接服务器的工具按自己习惯的即可,我们这里直接用阿里云提供的远程连接工具。

点击 实例ID 旁边的远程连接,选择默认的 Workbench

连接成功后,进入操作终端

点击菜单栏的 文件 --> 打开新文件管理,查看服务器文件

在后续的操作中,我们会不时用到该工具,以完成项目的部署工作

二、部署前端项目【Vue3】

我们会用到 Jenkins 来实现前端项目的自动化部署。

Jenkins 的安装步骤暂时不作过多介绍,可以直接在 Jenkins 官网下载安装包或使用 Docker 拉取镜像,网上的相关教程也很多。

Jenkins download: www.jenkins.io/download/

1. 安装插件【Publish Over SSH、Nodjs】

【Dashboard】-->【Manage Jenkins】-->【Plugins】-->【Available plugins】,搜索 Publish Over SSHNodeJS,安装完成后重启 Jenkins

2. Publish Over SSH 配置

【Dashboard】-->【Manage Jenkins】-->【System】,找到 Publish over SSH,并根据你购买的云服务器实例创建对应的 SSH Server

Name:自定义服务名称

Hostname:服务器IP

Username:服务器登录用户

Remote Directory:登陆后访问的地址

高级配置:输入Passphrase / PasswordPort,再点击下方的 Test Configuration 按钮测试连接是否正常,显示 success 为成功。

全部确认后,先点击应用,再保存

3. Nodejs 配置

【Dashboard】-->【Manage Jenkins】-->【Tools】,找到 NodeJS安装,配置好 别名版本 后,先点击应用保存

这里我用的 NodeJS 版本为 v20.10.0

4. 凭证管理

【Dashboard】-->【Manage Jenkins】-->【Credentials】,点击 Add credentials

填写远程项目仓库的用户名、密码、描述等,创建后即可在后续的操作中使用该凭证

5. 创建 Job

经过上述步骤,我们完成了所有的前置准备工作,接下来可以创建一个Job 开始正式部署。

点击【Dashboard】左侧菜单里的【新建Item】,选择创建一个【Frestyle project】

创建完成后,进入项目配置页面,并填写项目需要的信息:

描述

源码管理

构建环境

构建步骤

(1)Execute Nodejs script
(2)Execute shell
bash 复制代码
#打印 node 和 npm 版本
node -v
npm -v
#安装项目依赖
npm install -g cnpm --registry=http://registry.npmmirror.com
cnpm install
#项目构建
npm run build
#进入到打包目
ls
cd dist
ls
#删除上次打包生成的压缩文件
rm -rf *.tar
#把生成的项目打包成压缩包,方便传输到远程服务器
tar -cvf `date +%Y-%m-%d-%H-%M-%S`.tar *
#回到上层工作目录
cd ../
echo "构建结束"
(3)Send files or execute commands over SSH
bash 复制代码
#进入远程服务器的目录
cd /usr/share/nginx/html
#找到新的压缩包,解压
tar -xvf *.tar -C ./
#删除压缩包
echo ">>>移除*.tar"
rm -rf *.tar
nginx -s reload
#发布完成
echo "发布完成"

完成后点击 应用 并 保存,回到项目首页,点击【Build Now】开始构建项目

构建结束后,可以点击构建记录查看构建详情,比如在控制台输出中查看构建每个步骤的执行结果,如果构建失败,也可以籍此定位失败原因

6. 阿里云部署

此时,进入 阿里云工作台 》实例,点击远程连接,这里我们直接使用阿里云提供的 workbench 进行操作

连接成功后打开文件管理,进入我们在填写 Send files or execute commands over SSH 配置时用到的 /usr/share/nginx/html 文件目录,可以看到,打包构建后的文件已经被发送到了我们的服务器上

(1)安装 nginx
yum install nginx -y
(2)启动 nginx
bash 复制代码
#启动 nginx
systemctl start nginx

如果安装成功,我们可以找到服务器的 nginx.conf 配置文件的所在目录,可以使用 systemctl status nginx 查看 nginx 服务状态,也可以在打印结果中看到 nginx.conf 的所在目录

我的配置文件存放在 /etc/nginx/nginx.conf 内,点击编辑打开配置文件

nginx 的配置在此不做过多赘述,这里我们使用默认的 nginx.conf,仅将端口、项目地址等基本信息修改为自己项目的信息即可

ini 复制代码
server {
    #项目端口
    listen      80 default_server;
    listen      [::]:80 default_server;
    server_name  _;
    #项目地址
    root         /usr/share/nginx/html;
    index        index.html;
​
    # 为默认的 server 加载 nginx 配置文件
    include /etc/nginx/default.d/*.conf;
​
    location / {
        try_files $uri $uri/ /index.html;
    }
​
    error_page 404 /404.html;
        location = /404.html {
    }
​
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }
}

(3)重启 nginx 服务

nginx -s reload
7. 打开项目页面

在浏览器中输入公网IP及端口,查看项目是否启动

三、安装数据库【MongoDB】

在安装 MongoDB 之前,我们需要先准备好几个文件目录,分别用来存储数据库的数据及日志等

软件安装位置:/usr/local/mongodb 数据存放位置:/usr/local/mongodb/data/db 日志存放位置:/usr/local/mongodb/data/logs

1. 进入安装 mongodb 的目录
bash 复制代码
cd /usr/local
2. 下载 mongodb 源代码压缩包
arduino 复制代码
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-4.0.4.tgz
3. 解压安装包,并重命名为 mongodb
bash 复制代码
tar zxvf mongodb-linux-x86_64-4.0.4.tgz
mv mongodb-linux-x86_64-4.0.4 mongodb
4. 创建数据和日志存储目录,也可以远程连接后手动创建
bash 复制代码
mkdir /usr/local/mongodb/data/db
mkdir /usr/local/mongodb/data/logs
5. 创建 mongodb.conf 配置文件,放在 usr/local/mongodb 目录下
bash 复制代码
cd /usr/local/mongodb
touch mongodb.conf
vim mongodb.conf

mongodb.conf 配置文件内容

ini 复制代码
#端口号(默认的端口号是27017,也可以为了安全性等因素修改为其他值,在安全组里开放对应端口即可)
port=27017
​
#数据目录(指向刚才创建的数据文件目录)
dbpath=/usr/local/mongodb/data/db
​
#日志目录(指向刚才创建的日志目录,并指定mongodb.log文件名,系统会自动创建)
logpath=/usr/local/mongodb/data/logs/mongodb.log
 
#设置后台运行
fork=true
 
#日志输出方式(写日志的模式:设置为true为追加。默认是覆盖。如果未指定此设置,启动时MongoDB的将覆盖现有的日志文件。)
logappend=true
 
#是否开启认证
auth=false
​
#对外开放端口(默认是127.0.0.1,修改为 0.0.0.0 表示所有 IP 连接)
bind_ip=0.0.0.0
6. 配置安全组

入方向创建一条新规则,端口范围设置为你在 mongodb.conf 中配置的 port 值

7. 启动 mongodb
bash 复制代码
#进入 mongodb 的 bin 目录
cd /usr/local/mongodb/bin
#启动命令
./mongod --config ../mongodb.conf

如果启动成功,我们可以看到以下信息:

arduino 复制代码
about to fork child process, waiting until server is ready for connections.
forked process: 18999
child process started successfully, parent exiting

也可以使用 ps aux |grep mongodb 命令查看 mongodb 进程是否开启

arduino 复制代码
root     31574  0.5  2.6 1000376 48508 ?       Sl   10:43   0:01 ./mongod --config ../mongodb.conf
root     31773  0.0  0.0 112812   980 pts/1    S+   10:47   0:00 grep --color=auto mongodb
8. 中止 mongodb
bash 复制代码
#使用ps aux |grep mongodb打印出来的第一条记录就是刚才启动的 mongodb 进程,进程ID是 31574
kill -9 31574
9. 为了检测云服务器是否开启 mongodb,可以使用以下方法:
(1)在浏览器地址栏输入http:{云服务器公网IP地址}:{mongodb设置的端口号},比如:http://47.99.102.151:27017/进行查看,启动成功会看到如下信息:
sql 复制代码
It looks like you are trying to access MongoDB over HTTP on the native driver port.
(2)使用 Database Client JDBC插件尝试连接,连接成功可以看到:
(3)使用 Mongodb Compass 连接数据库,填写好配置后点击 connect ,成功可以看到:
10. 处理数据库数据丢失的问题

刚开始成功把 MongoDB 服务启动后,运行以来并没有什么问题。

但是过段时间,自己添加的测试数据全部丢失了,重启服务后再次添加的数据,

依然遇到被清空的情况。

难道是有什么定时清除的功能吗?我寻思着也不太可能啊。

当我拿 Database Client JDBC 连接数据库后,突然发现多了一个 READ__ME_TO_RECOVER_YOUR_DATA 库,之前没仔细看,现在注意下就发现不对劲。

阅读此内容助你恢复数据。。。被黑了

原因很简单,之前编辑 mongo.conf 文件时,bind_ip为 0.0.0.0,端口号用的默认的27017,测试期间图方便,也没有设置用户名和密码,这不就是光着身子在互联网上裸奔吗?人家用脚写个脚本也能抓到啊。

为了解决这一问题

首先,修改端口号,连接数据库

bash 复制代码
cd /usr/local/mongodb/bin
./mongo 127.0.0.1:27018

创建用户和权限

php 复制代码
db.createUser({ user: "username", pwd: "password", roles: [{ role: "readWrite", db: "meleon" }, { role: "dbOwner", db: "meleon" }] })

设置开启权限

将配置文件中的 auth=false修改为 auth=true

重启服务,此时再次连接就需要提供用户名及密码了

ini 复制代码
#mongo://myUser:myPassword@127.0.0.1:27018/test
mongo://[username]:[password]@[host]:[port]/[dbName]

四、部署后端项目【Nestjs】

和部署前端项目相比,部署后端项目的主要区别在于项目的打包,或者说构建行为,是放在服务器上进行的。

至于为什么要这么做,我们可以先使用默认的 nest build在本地构建下 Nestjs 项目,结果如下:

即便包含了 .js,.d.ts 以及 .js.map,整个文件夹的体积也不到 1MB,作为经受过 node_modules 折磨的前端打工仔,一看体积才这么点就知道肯定有问题,如果我们完全按照前端部署步骤执行的话,最终的结果是提示Cannot find module XXX,程序也根本跑不起来。

回到构建结果目录,我们注意到有一份特殊的文件tsconfig.build.tsbuildinfo,里面的内容也告诉我们要到 dist 目录外的 node_modules 里找项目所需要的依赖项。

确定好基本思路,接下来就按部就班进行 Nestjs 的部署即可。

1. 搭建环境
(1)安装 Nodejs
bash 复制代码
# 使用 nvm 来帮助我们更加方便的管理多个版本的 nodejs
yum install nvm
bash 复制代码
# 查看可用的 nodejs 版本
nvm list-remote
bash 复制代码
#安装需要的 nodejs 版本
nvm install v17.9.1

选择 v17.9.1 这个版本是因为这是我目前能安装最新且支持 ??= 语法的版本了 T-T

csharp 复制代码
# 设置默认的 node 版本
nvm alias default v17.9.1

在正式部署之前,我们可以先创建一份测试用项目文件 example.js,用以验证如何将项目代码部署到指定端口上

bash 复制代码
#回到 root 目录
cd
#创建测试文件
touch example.js
#编辑测试文件
vim example.js
ini 复制代码
const http = require('http');
const hostname = '0.0.0.0';
const port = 3033;
const server = http.createServer((req, res) => { 
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    res.end('Hello World\n');
}); 
​
server.listen(port, hostname, () => { 
    console.log(`Server running at http://${hostname}:${port}/`);
});
node example.js

此时,我们可以打开浏览器,在地址栏输入 IP + 端口号【3033】,如果页面上显示 Hello World,说明项目已经成功部署。

记得在阿里云服务器的安全组里开启相应的 3033 端口

(2)安装 pm2

使用 node 运行项目的弊端在于必须保持窗口始终处于开启状态,一旦窗口关闭,服务就会中止。很明显,这种方案是不太现实的。为了保证服务即便在窗口关闭后也能保持启动状态,我们需要使用另一个工具 ------ pm2。

a. 安装 pm2

css 复制代码
npm install pm2@latest -g

b. 介绍下 pm2 常用的指令

bash 复制代码
#启动服务
pm2 start <service>
bash 复制代码
#列出已启动的进程
pm2 ls
bash 复制代码
#结束所有或指定进程
pm2 kill
bash 复制代码
#查看 pm2 日志
pm2 logs

c. 顺便介绍下其他可能会用到的指令

shell 复制代码
#返回当前 nodejs 所在的目录【指定 pm2 运行时所用的 nodejs 版本】
#/root/.nvm/versions/node/v17.9.1/bin/node
which node
​
# 查看服务器端口占用情况,确认服务是否启动
netstat -nultp
2. 填写配置

源码管理和构建环境等配置项与前端部署区别不大,根据自己项目的信息自行配置即可,需要介绍的仅是构建步骤上的区别,如下:

(1)Execute shell
bash 复制代码
node -v
npm -v
​
#删除上次打包生成的压缩文件
rm -rf *.tar
​
#把生成的项目打包成压缩包方便传输到远程服务器
#.env 文件通常包含敏感信息,例如 API、密钥、密码等
#默认情况下,tar 命令不会自动包含 .env 文件,如果确实需要,你可以在命令中显式地将 .env 文件打包进去
tar -cvf `date +%Y-%m-%d-%H-%M-%S`.tar .env.production .env.development *
​
echo "打包结束"
(2)Send files or execute commands over SSH
bash 复制代码
#Source files
**/*.tar
bash 复制代码
#Remove prefix
/
bash 复制代码
#Remote directory
/usr/share/nginx/server
bash 复制代码
#Exec command
source /etc/profile
​
#进入远程服务器的目录
cd /usr/share/nginx/server
​
#找到新的压缩包
tar -xvf *.tar -C ./
​
echo ">>>移除*.tar"
rm -rf *.tar
​
#打包构建
npm config set registry https://registry.npmmirror.com
​
npm install
​
npm run build
​
#关闭之前的进程
pm2 delete meleon-profile-backend
rm -rf ~/.pm2
​
#启动进程
pm2 start npm --name meleon-profile-backend --interpreter=/root/.nvm/versions/node/v17.9.1/bin/node -i 1 -- run start:prod
pm2 save --force
​
#发布完成
echo "发布完成"

解决 this.options.length ??= bufferOrReadStream.length; 报错问题

本地开发时我用的 nodejs 版本为 v20.10.0,而我购买的阿里云ECS服务器并不能支持这个版本,无法执行任何 node 相关的指令,所以不得不降低node的版本。

但是降低版本后,比如 14.18.0【公司早期的一些项目会用到 node-sass,需要将 nodejs 降到这个版本才能正常使用,所以最开始就习惯性地用了这个版本】,会出现无法识别 ??=等新语法的报错,经过几次尝试,目前 v17.19.1 能够正常使用且不会出现类似报错。

此外,如果指定 node 版本并重启进程后,依然会有该报错,可以试试清下缓存再重启试试。

bash 复制代码
pm2 kill
rm -rf ~/.pm2
bash 复制代码
#手动设置 pm2 使用的 node 版本
pm2 set pm2:node-version 14.5.0

五、记录待解决的问题

(1)项目启动后,虽然没有报错,但是服务监听的端口并没有被占用。

app.listen(port, callback)callback回调会在端口监听成功时触发,可以在代码中加上这个函数验证服务是否真的启动了

javascript 复制代码
await app.listen(3000, () => {
   console.log('成功监听了 3000 端口')
})

异常定位:

vbnet 复制代码
/**
 * @fix 在部署时,保留 { logger: false, cors: true } 参数会导致项目运行不起来,虽然本地运行正常,原因未知,先暂时注释掉吧
 */
// const app = await NestFactory.create(AppModule, {
//   logger: false,
//   cors: true
// })
const app = await NestFactory.create(AppModule)
(2)Send files or execute commands over SSH 步骤:将文件发送到服务器后并没有执行 Exec command 内编写的指令

blog.csdn.net/weixin_4429...
Exec command 里添加一行 source /etc/profile

(3)项目启动后,会提示端口已被占用,即使强行"杀死"端口后重启项目依然会出现这个问题。

解决方案:

node.js - PM2启动node服务一直报端口被占用,换了好几次端口也没用? - SegmentFault 思否

在使用 pm2 启动项目时,添加 -i 指令,让 pm2 从 fork 模式切换为 cluster 模式。二者的区别主要在于:

  1. Fork 模式

    • 基本概念 :Fork 模式是一种基本的进程派生方式,它使用 child_process.fork API 来创建子进程。
    • 用途:Fork 模式适用于多语言混编的场景,例如同时运行 PHP、Python 等不同类型的服务器。
    • 端口复用:不支持端口复用,需要手动分配端口并自行处理子进程的业务代码。
    • 灵活性:非常灵活,可以实现各种自定义需求,例如在预先分配的端口上启动多个服务器,然后由 HAProxy 或 Nginx 进行负载均衡。
  2. Cluster 模式

    • 基本概念 :Cluster 模式仅适用于 Node.js,它利用 Node.js 的 cluster 模块(例如 isMasterfork 等方法)来自动创建多个子进程。
    • 自动负载均衡:Cluster 模式非常适合零配置的进程管理,因为它会自动将进程分叉为多个实例,实现负载均衡。
    • 启动命令示例 :例如,使用 pm2 start -i 4 server.js 将启动 4 个 server.js 实例,并由 cluster 模块处理负载均衡。

六、总结

根据以上步骤,我们已经完成了前后端及数据库的部署工作

当然,目前来看还是有很多值得进一步优化的地方,比如代码push后的自动更新,又或者结合 Docker 以隔离依赖版本的影响等等

不过暂时先放一边吧,毕竟不算日常工作的话,已经有段时间没正经写代码了,项目里搁置了一堆有一堆的半成品功能,啧,估计半成品都算不上,抽点时间再整整

相关推荐
白总Server10 小时前
MongoDB解说
开发语言·数据库·后端·mongodb·golang·rust·php
码爸12 小时前
spark读mongodb
大数据·mongodb·spark
一个很帅的帅哥1 天前
实现浏览器的下拉加载功能(类似知乎)
开发语言·javascript·mysql·mongodb·node.js·vue·express
JY_H1 天前
MongoDB
数据库·mongodb
东城绝神2 天前
《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.14容器版副本集群》
linux·运维·mongodb·架构
香吧香2 天前
mongo 副本集rs 理解和使用小结
mongodb
香吧香3 天前
mongodb 中rs.stauts()命令参数解析
mongodb
青云交4 天前
大数据新视界 --大数据大厂之MongoDB与大数据:灵活文档数据库的应用场景
大数据·数据库·mongodb·非关系型数据库·文档存储·查询功能、数据处理·开发效率、应用场景、高可扩展性
香吧香4 天前
mongo集群同步数据异常,手动同步节点副本数据
mongodb
Navicat中国5 天前
Navicat 17 新特性 | 聚焦 MongoDB
数据库·sql·mongodb·信息可视化·nosql数据库·navicat