使用 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 以隔离依赖版本的影响等等

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

相关推荐
HaSaKing_72110 分钟前
【非关系型数据库】【IOT设备】InfluxDB、TimescaleDB、Cassandra和MongoDB
物联网·mongodb·nosql
桃根仙17 小时前
mongoDB设置访问用户名密码
数据库·mongodb
骑士99911117 小时前
mongoDB的安装及使用
数据库·mongodb
来一杯龙舌兰21 小时前
【MongoDB】MongoDB的集群,部署架构,OptLog,集群优化等详解
数据库·mongodb·集群·集群优化·optlog·部署架构
周星猩21 小时前
linux 安装 mongodb
数据库·mongodb
StudyHappiness21 小时前
MongoDB新版本,单节点安装
linux·运维·mongodb·kylin
晴天のVlog21 小时前
Fastapi使用MongoDB作为数据库
数据库·python·mongodb·fastapi
小徐敲java2 天前
docker 安装mongodb
mongodb·docker·容器
webxue2 天前
NestJS配置环境变量、读取Yaml配置的保姆级教程
node.js·nestjs
安静读书3 天前
MongoDB 详解:深入理解与探索
数据库·mongodb