Nest 使用 PM2 部署
本章内容
使用 PM2 部署 Nest.js 应用程序
HTTPS 证书配置
nginx 配置
nginx 检查
PM2 部署与配置
bash
pm2 start ecosystem.config.js --env=production
pm2 list # 查看pm2列表
pm2 delete|stop|restart|start [ID编号||all] #操作
pm2 logs # 检查是否正常启动
pm2 monit #监控 未下载:pm2 install monit
pm2 reload ecosystem.config.js --env production # 不停机重启
ecosystem.config.js
- 项目根目录新建 ecosystem.config.js
ts
module.exports = {
apps: [
{
name: "你自己的名字", //名称
script: "./dist/main.js",
instances: 2, //核心数4,如果是fork就设置成1即可
//启动应用程序的模式,可以是“cluster”或“fork”,默认fork
exec_mode: "cluster", //集群模式
watch: false, //路劲不存在会cpu100% 无限重启
ignore_watch: [
// 可以设置不用监听的文件,watch设置为true才有效
"node_modules",
"public",
"logs",
],
//分钟 (0-59)、小时 (0-23)、一个月中的第几天 (1-31)、月份 (1-12) 、星期几 (0-6) (星期天 为0)
//设置定时重启,具体可以看文档,一般用来解决重启能解决的问题
//例如:老程序内存泄露,一定时间会爆内存,但程序员跑路了,为了保持稳定运行只能重启
// "cron_restart": '0 0 * * *',
// 启用/禁用应用程序崩溃或退出时自动重启,默认为true, 发生异常的情况下自动重启
autorestart: true,
//设置日志路径,可以方便我们查看一些错误原因或者其他信息,服务器上需要有对应文件或者读写权限
// "error_file" : "./log/err.log",
// "out_file" : "./log/out.log",
log_date_format: "YYYY-MM-DD HH:mm Z", //日期格式
//集群状态下合并日志
merge_logs: true,
//环境变量,我们前面的 .env 服务端会被忽略,可以注入这里面的环境变量,可以避免正式服务器信息泄露
//pm2 start ecosystem.config.js --env develop
env: {
NODE_ENV: "development",
HOST: "192.168.1.1", //出现在程序的host环境变量
PORT: "8080",
},
//pm2 start ecosystem.config.js --env test
env_test: {
NODE_ENV: "test",
},
//pm2 start ecosystem.config.js --env production,可以选择环境变量
env_production: {
NODE_ENV: "production",
HOST: "0.0.0.0", //出现在程序的host环境变量
PORT: "8080",
},
instance_var: "INSTANCE_ID", // 实例标识,可以在main.ts
max_memory_restart: "800M", // 单实例内存上限(防泄漏)
},
],
//不会用
deploy: {
production: {
user: "root",
host: "127.0.0.1",
ref: "origin/master",
repo: "GIT_REPOSITORY",
path: "DESTINATION_PATH",
"pre-deploy-local": "",
"post-deploy":
"npm install && pm2 reload ecosystem.config.js --env production",
"pre-setup": "",
},
},
};
NestJS 中计算端口
ts
// 动态计算端口:3000 + PM2 实例ID
const basePort = parseInt(process.env.PORT || "8080");
const instanceId = process.env.INSTANCE_ID
? parseInt(process.env.INSTANCE_ID)
: 0;
const port = basePort + instanceId;
await app.listen(port, "0.0.0.0");
NestJs 设置 https(一般不用)
ts
const httpsOptions = {
key: fs.readFileSync("./src/config/daysmatters.top.key"),
cert: fs.readFileSync("./src/config/daysmatters.top_bundle.pem"),
};
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
// new FastifyAdapter({ logger: true })
new FastifyAdapter({
https: httpsOptions, // https 配置
}),
// new ExpressAdapter(),
{
// httpsOptions, //express https 配置
cors: true, // 开启跨域访问
snapshot: true, // 启用内存优化
autoFlushLogs: true, // 自动刷新日志
// keepAliveTimeout: 60_000 // 长连接保活
}
);
nginx 配置
bash
# 定义后端服务负载均衡
upstream nest_backend {
# 配置策略加权轮询(weight=3的实例获得60%流量)
server 127.0.0.1:8080;
server 127.0.0.1:8081;
keepalive 32; # 保持连接复用
}
server {
# 监听80端口(HTTP)
listen 80;
# 绑定域名(无域名用_)
server_name baidu.com;
# 强制HTTP跳转HTTPS(推荐)
return 301 https://$host$request_uri;
}
server {
# 监听443端口 (HTTPS)
listen 443 ssl;
server_name baidu.com;
# SSL证书配置
ssl_certificate /www/nest-server/src/config/daysmatters.top_bundle.pem;
ssl_certificate_key /www/nest-server/src/config/daysmatters.top.key;
# SSL优化参数
#ssl_protocols TLSv1.2 TLSv1.3;
#ssl_prefer_server_ciphers on;
#ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
location ^~ /admin/static/ {
alias /www/admin/dist/static/;
# 确保 Nginx 有目录访问权限
disable_symlinks off;
# 长期缓存
expires 1y;
access_log off;
add_header Cache-Control "public, immutable";
#disable_symlinks off; # 允许符号链接
# 确保处理所有文件类型
types {
application/javascript js;
text/css css;
image/png png;
image/jpeg jpg;
image/svg+xml svg;
application/font-woff woff;
application/font-woff2 woff2;
}
}
location /admin/ {
alias /www/admin/dist/;
index index.html;
# 处理 Vue 路由
try_files $uri $uri/ /admin/index.html;
# 添加 MIME 类型定义
types {
text/html html;
application/javascript js;
text/css css;
image/png png;
image/jpeg jpg jpeg;
image/gif gif;
image/svg+xml svg;
application/font-woff woff;
application/font-woff2 woff2;
application/vnd.ms-fontobject eot;
font/ttf ttf;
}
# 缓存设置
expires 1h;
access_log off;
#禁止自动索引
#autoindex off;
}
# 静态资源直返(绕过Node节省资源)
location ~* \.(js|css|png|jpg|webp)$ {
# 静态文件存放路径
root /var/www/static;
# 客户端缓存30天
expires 30d;
# 关闭日志减少磁盘IO
access_log off;
}
# API请求转发规则
location / {
# 指向后端服务器组
proxy_pass https://nest_backend;
# 必须的HTTPS代理参数
proxy_ssl_verify off; # 关闭证书验证(测试用)
proxy_ssl_trusted_certificate /www/daysmatter/daysmatters_nestjs/nest-admin/server/src/config/daysmatters.top_bundle.crt; # 生产环境应验证证书
# HTTP协议升级(WebSocket必须)
proxy_http_version 1.1;
#proxy_set_header Upgrade $http_upgrade;
#proxy_set_header Connection 'upgrade';
# 透传客户端信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时控制(按需调整)
proxy_connect_timeout 75s; # 连接后端超时
proxy_send_timeout 600s; # 发送请求超时
proxy_read_timeout 600s; # 读取响应超时
# 缓冲优化(防内存溢出)
proxy_buffering on;
proxy_buffer_size 4k; # 单缓冲区大小
proxy_buffers 8 16k; # 缓冲区数量*大小
proxy_busy_buffers_size 32k;
}
# 2. 限流保护(防CC攻击)
#limit_req_zone $binary_remote_addr zone=api_rate:10m rate=100r/s;
location /api/ {
#limit_req zone=api_rate burst=50 nodelay;
proxy_pass https://nest_backend/api/;
}
error_page 503 @maintenance;
location @maintenance {
root /www;
rewrite ^(.*)$ /maintenance.html break;
}
}
nginx 检查
bash
#1.需要保证前端文件权限
sudo chmod -R 755 /var/www/xxx
# 修正所有权和权限
sudo -u www-data ls /path/to/file
#验证构建输出路径
ls -l /full/path
#2.需保证安全组开放端口,并且服务器开放防火墙
sudo ufw allow 端口号
# 重启防火墙使规则生效
sudo ufw reload
# 查看端口是否已开放
sudo ufw status
#sudo ufw allow from 客户端IP to any port xxxxx # 仅允许特定 IP 访问
#3.nginx需要保证文件无误并重启
#验证配置
sudo nginx -t
sudo nginx -t -c /var/file/xxx.conf #需要在主文件中引入
#检查错误日志
sudo tail -f /var/log/nginx/error.log
#重启生效
sudo systemctl restart nginx