1. 项目概述
1.1 这个项目到底在做什么
如果你点进这篇文章,大概率和我一样,正在经历从“本地能跑”到“服务器上也能跑”的蜕变期。这个项目的标题很直白——Vue3 前端 + Node.js 后端 + MongoDB 数据库,一套全栈项目从零开始开发,最终部署到阿里云服务器上。简单说,就是用一套完整的真实项目流程,把“开发环境搭建”和“生产环境部署”这两件看似简单、实则坑不少的事情彻底走通。
这套技术栈在 2024 年的中小型项目里非常主流:Vue3 负责页面交互,Node.js 负责接口逻辑,MongoDB 负责数据存储。三者的组合很适合快速迭代的 Web 应用,尤其是 CMS 内容管理、小程序后台、个人博客这类体量的项目。相比 Java + MySQL 那套传统组合,这套技术栈更轻、更灵活,尤其适合个人开发者和小团队快速验证业务模型。
1.2 适合谁来参考
- 刚学完 Vue3 和 Node.js 基础,想了解“真实项目”长什么样的前端/全栈初学者。
- 已经在本地把项目跑通了,但不知道如何从零部署到云服务器的新手。
- 被 MongoDB 安装、端口配置、反向代理、HTTPS 证书等环节折磨过,想直接找到一套“可复现方案”的开发者。
这篇文章不追求大而全,而是按照我自己从本机到阿里云的完整流程来写,过程中会把每一步的选择理由和踩坑经历都交代清楚。你能直接照着操作,也能在遇到问题时快速定位排查。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型思路:为什么是 Vue3 + Node.js + MongoDB + 阿里云
2.1 前后端技术栈的匹配逻辑
先说前端。Vue3 现在已经是 Vue 生态的绝对主线版本,组合式 API 让组件逻辑复用变得干净很多。配合 Vite 构建工具,开发体验确实比 Webpack 时代流畅不少——热更新几乎是秒级响应。如果你之前用过 Vue2,切到 Vue3 之后,最大的感触应该是“终于不用在 data 和 methods 之间来回跳了”。
再说后端,Node.js 选择 Express 或 Koa 框架。对于中小型项目的 CRUD 接口、文件上传、登录鉴权这些常规需求,Node.js 的异步非阻塞模型完全够用,而且前后端都用 JavaScript,心智负担小得多。我个人的习惯是使用 Express,因为它的中间件生态足够成熟,部署时配合 PM2 做进程守护也很方便。
最后是数据库。MongoDB 的文档型数据结构对前端开发者特别友好,存储 JSON 风格的数据几乎不需要做对象关系映射。在这个项目里,用户信息、文章内容、日志记录都直接用对象形式写入,查询时的条件表达也更接近“自然语言”。相比 MySQL 的建表、关联查询,MongoDB 的上手速度对团队里有前端背景的成员来说明显更快。
2.2 部署平台为什么选阿里云
国内云厂商里,阿里云的基础文档最全,遇到问题搜出来的教程匹配度也最高。尤其是域名备案、SSL 证书、安全组规则这几个环节,阿里云控制台的操作路径相对清晰。ECS 云服务器按量付费的模式也适合用来做技术验证——先低配跑起来,不够再加。
选择 CentOS 或 Ubuntu 作为服务器系统都可以。我个人偏好 Ubuntu,因为软件源更新快,apt 安装 Node.js 和 MongoDB 更省事。如果选 CentOS,很多命令要换成 yum,且有些软件源需要额外配置 EPEL,稍麻烦一点。这篇文章以 Ubuntu 20.04/22.04 为例。
2.3 整体架构预览
code复制用户浏览器
↓ HTTPS
阿里云负载均衡 / Nginx
↓ 反向代理
Node.js 应用(Express 接口服务,端口 3000)
↓
MongoDB(端口 27017,仅内网访问)
前端打包后的静态资源由 Nginx 直接托管,接口请求通过 Nginx 反向代理到 Node.js 进程。MongoDB 不直接暴露到公网,只监听内网地址,避免数据库被外部扫描工具碰运气破解。
3. 本地环境搭建与项目初始化
3.1 Node.js 安装:版本选择比安装本身更重要
在本地开发机上安装 Node.js,我建议直接去官网下载 LTS 版本。目前 LTS 是 20.x 系列,建议不要用最新的 24.x,因为部分 npm 原生模块的预编译二进制可能还没跟上,编译源码容易踩坑。
注意:MongoDB 官方驱动对 Node.js 版本有明确的支持范围。以 mongoose 为例,较老版本在 Node 18 之后遇到过一些兼容性告警,用 LTS 版本可以最大程度避免这类问题。
Linux/Mac 环境下推荐用 nvm 管理 Node 版本,方便切换;Windows 用户直接下载 .msi 安装包即可,它会自动配置环境变量。安装完成后,在终端输入以下命令验证:
bash复制node -v
npm -v
确认两个版本号都能正常输出,说明 Node.js 和 npm 已经可用。npm 是随 Node.js 一起安装的,不需要单独下载。
如果你的网络环境访问 npm 官方源较慢,可以切换为国内镜像源:
bash复制npm config set registry https://registry.npmmirror.com
3.2 MongoDB 安装与启动
MongoDB 的安装在不同系统上差异较大,这里分别说明。
Windows 环境
下载 MongoDB Community Server 的 .msi 安装包,安装过程中选择 “Complete”,然后勾选 “Install MongoDB as a Service” 和 “Install Compass”。安装完成后,MongoDB 会作为 Windows 服务在后台运行,默认端口 27017。
安装后一定要检查服务是否真的起来了:
bash复制net start | findstr MongoDB
如果想手动启停:
bash复制net stop MongoDB
net start MongoDB
CentOS / Ubuntu 环境
Ubuntu 用户需要先导入 MongoDB 官方的 GPG 公钥,然后添加软件源。以 Ubuntu 22.04 安装 MongoDB 6.0 为例:
bash复制wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list
sudo apt-get update
sudo apt-get install -y mongodb-org
安装完成后,启动服务并设置开机自启:
bash复制sudo systemctl start mongod
sudo systemctl enable mongod
sudo systemctl status mongod
如果启动失败,先看日志文件 /var/log/mongodb/mongod.log,90% 的情况是权限问题或数据目录未创建。
Docker 方式(适合本地快速体验)
如果你已经习惯 Docker,一条命令就能启动 MongoDB:
bash复制docker run -d \
--name mongodb \
-p 27017:27017 \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=123456 \
-v mongodb_data:/data/db \
mongo:6.0
这里要注意,加了 --auth 或设置了 MONGO_INITDB_ROOT_USERNAME 后,连接 MongoDB 必须带上用户名和密码。本地开发如果嫌麻烦,可以先不配置用户名密码。
3.3 创建 Vue3 前端项目
使用 Vite 创建项目,命令如下:
bash复制npm create vite@latest frontend -- --template vue
cd frontend
npm install
npm run dev
Vite 创建的项目默认结构清晰,src 目录下按需创建 views、components、router、store 等目录。建议一开始就配置好路径别名:
javascript复制// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
})
这样在组件里 import 文件时就可以用 @/components/xxx.vue 这种写法,不会出现一串 ../../ 的相对路径。
3.4 创建 Node.js 后端项目
后端目录结构建议单独一个文件夹,和前端保持分离。以 Express 为例:
bash复制mkdir backend
cd backend
npm init -y
npm install express mongoose cors dotenv
创建入口文件 app.js:
javascript复制const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
require('dotenv').config()
const app = express()
app.use(cors())
app.use(express.json())
// 连接 MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/myapp')
.then(() => console.log('MongoDB connected'))
.catch(err => console.error('MongoDB connection error:', err))
// 一个最简单的测试接口
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', time: new Date() })
})
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`)
})
这里用 dotenv 管理环境变量,本地开发默认连 localhost:27017,生产环境则从 .env 文件中读取真实的连接字符串。建议把 .env 加入 .gitignore,避免数据库密码和密钥提交到代码仓库。
3.5 用一张表理清端口规划
在开发阶段和部署阶段,涉及到的端口比较多,提前规划好可以少走很多弯路。
| 服务 | 默认端口 | 说明 |
|---|---|---|
| Vue 开发服务器 | 5173 | 仅本地访问 |
| Node.js 接口服务 | 3000 | 本地直接访问;生产环境经 Nginx 代理 |
| MongoDB | 27017 | 生产环境仅监听内网,不可公网访问 |
| Nginx | 80 / 443 | 公网入口,处理 HTTP 和 HTTPS 请求 |
4. 数据库设计与接口开发的核心细节
4.1 MongoDB 数据模型设计思路
在 MongoDB 中,集合(Collection)对应 MySQL 的表,文档(Document)对应一行记录。因为是文档型数据库,不需要像 MySQL 那样先建表再写数据,直接在代码里定义模型即可。
以“用户”和“文章”两个集合为例:
javascript复制// models/User.js
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
nickname: String,
avatar: String,
createdAt: { type: Date, default: Date.now }
})
module.exports = mongoose.model('User', userSchema)
javascript复制// models/Article.js
const mongoose = require('mongoose')
const articleSchema = new mongoose.Schema({
title: { type: String, required: true },
content: { type: String, required: true },
author: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
tags: [String],
views: { type: Number, default: 0 },
createdAt: { type: Date, default: Date.now }
})
module.exports = mongoose.model('Article', articleSchema)
在设计阶段,我建议遵守三个原则:
- 不要过度建模:文档型数据库天然适合“一次查询拿到完整对象”,不要每次都在代码里做多层 join 操作。
- 合理使用引用关系:像文章作者这种场景,用
ObjectId引用是合理的,直接嵌套会导致作者信息修改时所有文档都要同步更新。 - 提前考虑索引:
username唯一索引能保证业务层的唯一性;createdAt加索引可以提升按时间排序的查询效率。
4.2 接口层常用的中间件和工具
Express 的中间件机制极大地方便了通用逻辑的抽取。我在项目里必用的几个中间件是:
cors:解决浏览器跨域问题。生产环境建议配置白名单,只允许自己的前端域名访问。express.json():解析 POST 请求中的 JSON 请求体。morgan:记录接口访问日志,方便排查问题。jsonwebtoken+express-jwt:实现基于 JWT 的用户认证。
一个典型的登录接口如下:
javascript复制const jwt = require('jsonwebtoken')
const User = require('../models/User')
const bcrypt = require('bcryptjs')
app.post('/api/auth/register', async (req, res) => {
const { username, password } = req.body
try {
const hashedPassword = await bcrypt.hash(password, 10)
const user = await User.create({ username, password: hashedPassword })
res.status(201).json({ message: '用户创建成功', userId: user._id })
} catch (err) {
res.status(400).json({ error: err.message })
}
})
app.post('/api/auth/login', async (req, res) => {
const { username, password } = req.body
const user = await User.findOne({ username })
if (!user) {
return res.status(401).json({ error: '用户名或密码错误' })
}
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
return res.status(401).json({ error: '用户名或密码错误' })
}
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET, { expiresIn: '7d' })
res.json({ token, user })
})
注意,密码绝对不能明文存储,bcryptjs 是常用方案。JWT_SECRET 也要放到环境变量里,不要硬编码在代码中。
4.3 前端接口请求封装
Vue3 项目中推荐用 axios 做请求层封装。基本思路是:创建一个统一实例,配置 baseURL、超时时间,通过拦截器自动附加 JWT Token,并对响应做统一错误处理。
javascript复制// src/api/http.js
import axios from 'axios'
const http = axios.create({
baseURL: '/api',
timeout: 10000
})
// 请求拦截器:附加 token
http.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// 响应拦截器:统一错误处理
http.interceptors.response.use(
response => response.data,
error => {
if (error.response && error.response.status === 401) {
// 跳转登录页
localStorage.removeItem('token')
window.location.href = '/login'
}
return Promise.reject(error)
}
)
export default http
开发环境下,baseURL 写 /api,同时配置 Vite 的开发代理,将请求转发到 localhost:3000:
javascript复制// vite.config.js 中加入 server 配置
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
这样前端开发时不会遇到跨域问题,生产环境则由 Nginx 统一处理,代码里完全不需要区分环境。
5. 阿里云服务器购买与基础环境配置
5.1 购买 ECS 实例时的选择建议
进入阿里云 ECS 控制台,选择“按量付费”可以先跑起来。实例规格方面,2 核 4G 是底线,低于这个配置跑 MongoDB 加 Node 应用会比较吃力。系统镜像选择 Ubuntu 22.04 64 位,带宽按量计费即可,峰值选 3Mbps 到 5Mbps 就够用。
注意:地域选择靠近你目标用户的位置。如果用户主要在国内大陆,选择华东或华北地域;如果用户主要在海外,考虑香港或新加坡。
5.2 安全组规则必须提前配好
很多新手遇到“我怎么访问不了服务器”的问题,不是服务器没启动,而是安全组没放行端口。在 ECS 实例详情页,找到“安全组”,点击“配置规则”,添加入方向规则:
| 协议 | 端口范围 | 授权对象 | 说明 |
|---|---|---|---|
| TCP | 80 | 0.0.0.0/0 | HTTP 访问 |
| TCP | 443 | 0.0.0.0/0 | HTTPS 访问 |
| TCP | 22 | 你的本机公网 IP | SSH 登录,不建议对所有人开放 |
| TCP | 3000 | 0.0.0.0/0 | 前端调试时可临时放行,部署完成后建议关闭 |
MongoDB 的 27017 端口永远不要加到安全组中,因为生产环境无需从外部访问数据库。
5.3 使用 SSH 登录服务器并完成基础配置
Windows 用户可以直接在 PowerShell 或 Windows Terminal 中使用 OpenSSH 客户端。登录方式选择密钥对比密码更安全,购买实例时如果没创建密钥,也可以在控制台重置实例。
bash复制ssh ubuntu@你的服务器公网IP
登录后,先更新软件源并创建普通用户(Root 直接登录有安全风险):
bash复制sudo apt update && sudo apt upgrade -y
sudo adduser deploy
sudo usermod -aG sudo deploy
接下来的所有部署操作都用 deploy 用户执行。这里我踩过的一个小坑是:Ubuntu 云服务器默认的 ubuntu 用户和 Debian 系列的 deploy 用户权限范围不同,很多操作需要 sudo,如果哪条命令提示 Permission denied,先检查当前用户是否有权限。
6. 服务器端 Node.js 与 MongoDB 部署
6.1 在服务器上安装 Node.js
服务器上推荐使用 NodeSource 提供的二进制仓库安装 Node.js 20 LTS:
bash复制curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
安装完成后验证:
bash复制node -v
npm -v
6.2 在服务器上安装 MongoDB
Ubuntu 22.04 安装 MongoDB 6.0 的步骤和本地一致。有一点需要特别注意:MongoDB 默认会监听所有网卡的 27017 端口,这在生产环境非常危险。修改配置文件 /etc/mongod.conf:
yaml复制# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
将 bindIp 改为 127.0.0.1,然后重启 MongoDB:
bash复制sudo systemctl restart mongod
这样做之后,MongoDB 只能在本机被访问,外网无法连接。
6.3 配置 MongoDB 用户和数据库
首次进入 MongoDB Shell:
bash复制mongosh
切换到 admin 数据库,创建管理员用户:
javascript复制use admin
db.createUser({
user: "root",
pwd: "一个足够复杂的管理员密码",
roles: [{ role: "root", db: "admin" }]
})
然后创建业务数据库的用户,这里假设项目数据库名是 myapp:
javascript复制use myapp
db.createUser({
user: "myapp",
pwd: "业务数据库密码",
roles: [{ role: "readWrite", db: "myapp" }]
})
启用 MongoDB 认证机制。编辑 /etc/mongod.conf,在 security 段加入:
yaml复制security:
authorization: enabled
再次重启 MongoDB 后,远程和本机的连接都要求认证了。
6.4 用 PM2 守护 Node.js 进程
服务器上直接用 node app.js 启动会有两个问题:一是 SSH 断开后进程就没了,二是进程崩溃后不会自动重启。PM2 是解决这些问题的最简单方案:
bash复制sudo npm install -g pm2
在项目根目录启动:
bash复制pm2 start app.js --name myapp
pm2 save
pm2 startup
pm2 startup 会生成一条开机自启的命令,把命令输出的内容完整执行一遍即可。PM2 的常用命令:
bash复制pm2 list # 查看所有进程状态
pm2 logs myapp # 查看日志
pm2 restart myapp # 重启
pm2 stop myapp # 停止
pm2 monit # 实时监控 CPU 和内存
6.5 项目的生成环境变量配置
在服务器上创建 /home/deploy/myapp/.env 文件,内容是本地 .env 的升级版本:
code复制MONGODB_URI=mongodb://myapp:业务数据库密码@127.0.0.1:27017/myapp
JWT_SECRET=给生产环境单独生成一个足够长的随机字符串
PORT=3000
注意,不要在代码仓库中提交 .env,部署时手动创建,或者用 CI/CD 工具注入环境变量。
7. Nginx 反向代理与前端静态资源部署
7.1 安装 Nginx
bash复制sudo apt install -y nginx
启动服务并设置开机自启:
bash复制sudo systemctl start nginx
sudo systemctl enable nginx
此时通过浏览器访问服务器公网 IP,应该能看到 Nginx 的欢迎页。如果看不到,依次排查:安全组是否放行 80 端口、服务器防火墙是否开启、Nginx 服务状态。
7.2 前端项目打包
本地构建 Vue 项目:
bash复制cd frontend
npm run build
构建产物在 dist 目录。使用 scp 命令上传到服务器:
bash复制scp -r dist ubuntu@你的服务器IP:/home/deploy/frontend-dist
如果你用的是 Windows 的 MobaXterm 或 FinalShell,也可以直接在图形界面上拖拽上传,效果一样。
7.3 配置 Nginx 站点
创建站点配置文件:
bash复制sudo vim /etc/nginx/sites-available/myapp
内容如下:
nginx复制server {
listen 80;
server_name yourdomain.com; # 换成你自己的域名
# 前端静态资源
root /home/deploy/frontend-dist;
index index.html;
# Vue Router history 模式需要配置
location / {
try_files $uri $uri/ /index.html;
}
# 接口反向代理
location /api {
proxy_pass http://127.0.0.1:3000;
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;
}
}
启用站点:
bash复制sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
注意:Vue Router 如果使用了 history 模式(即 URL 中没有 #),刷新页面时 Nginx 需要把不存在的路径都回退到 index.html,
try_files那一行就是干这个的。如果漏掉,刷新后会出现 404。
7.4 部署过程中最容易遗漏的细节
- 上传 dist 后,服务器上目录权限要确认:Nginx 运行用户
www-data需要读取前端文件的权限。如果部署到/home/deploy/下,可能需要修改目录权限或把目录放到/var/www/下。 - 检查
/etc/nginx/sites-enabled/下是否残留默认的default配置,如果有,建议先禁用,避免配置冲突。 - 接口代理路径要前后端一致。前端 axios 的
baseURL是/api,后端 Express 路由也要以/api开头,否则代理找不到路径。
8. 域名解析与 HTTPS 证书配置
8.1 域名解析到服务器
在阿里云域名控制台,添加一条 A 记录,主机记录填写 www,解析线路默认,记录值填写你的服务器公网 IP。TTL 可以保持默认 600 秒。
DNS 解析生效需要几分钟到几十分钟不等,可以用 ping yourdomain.com 来验证。
8.2 申请免费 SSL 证书
阿里云提供免费的 SSL 证书(目前单域名免费的额度是每个自然年一定数量,具体以控制台为准)。路径:控制台搜索“数字证书管理服务”,选择“免费证书”,申请一张单域名证书。
如果域名做的 A 记录已经在阿里云解析,证书审核很快。审核通过后下载证书文件,里面通常包含两个文件:.pem(证书)和 .key(私钥)。
8.3 配置 Nginx 启用 HTTPS
将证书上传到服务器,例如放在 /etc/nginx/cert/ 目录下,然后修改站点配置:
nginx复制server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/nginx/cert/yourdomain.pem;
ssl_certificate_key /etc/nginx/cert/yourdomain.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
root /home/deploy/frontend-dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:3000;
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;
}
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
配置测试与重载:
bash复制sudo nginx -t
sudo systemctl reload nginx
这样 HTTP 请求会自动跳转到 HTTPS,用户访问你的域名时就是加密连接了。
8.4 证书到期续期的经验
阿里云免费证书的有效期是 1 年,到期前续期新证书后,需要重新下载并替换服务器上的证书文件。我自己是把证书文件放在固定目录,续期时只替换两个文件,再 nginx -t && reload 即可。
有一点提醒:如果更换证书后浏览器仍提示“您指定的页面不存在”或证书错误,先检查证书和私钥是否匹配:
bash复制openssl x509 -noout -modulus -in yourdomain.pem | openssl md5
openssl rsa -noout -modulus -in yourdomain.key | openssl md5
两个命令输出的 MD5 值一致,说明证书和私钥匹配;不一致就是配错文件了。
9. 常见问题与排查技巧实录
9.1 MongoDB 连接失败排查
症状:Node.js 应用启动时报 MongooseServerSelectionError 或 getaddrinfo ENOTFOUND。
排查步骤:
- 确认 MongoDB 服务在运行:
sudo systemctl status mongod。 - 确认连接字符串的地址是
127.0.0.1而不是localhost,在某些系统中 localhost 会解析为 IPv6 的::1,而 MongoDB 默认只监听 IPv4。 - 确认用户名密码正确,并检查是否启用了认证。没有启用
authorization: enabled时,带用户名密码也能连接,但这说明你之前的配置没生效。 - 检查 MongoDB 日志:
sudo tail -100 /var/log/mongodb/mongod.log。
9.2 前端刷新页面 404
这个问题的原因在配置 Nginx 时提到过。Vue Router 的 history 模式下,刷新 /about 页面时,浏览器会向服务器请求 /about 路径,但静态目录下并没有这个文件,所以返回 404。
对应解法就是 try_files $uri $uri/ /index.html;。
9.3 接口请求超时或连接被拒绝
可能原因和排查顺序:
- 先确认 Node.js 服务在运行:
pm2 list,状态如果是errored,看日志pm2 logs myapp。 - 再确认 Nginx 代理配置是否正确:
sudo nginx -T | grep proxy_pass。 - 然后确认安全组是否放行了相应端口。生产环境里,客户端只会访问 80/443,所以 3000 端口在安全组中不需要对外开放。
- 最后检查服务器防火墙:
sudo ufw status,如果有ufw规则,放行 80/443 和 22。
9.4 前端打包后接口地址混乱
如果你在本地开发时把 baseURL 写死为 http://localhost:3000,打包部署后请求会直接打到用户自己的电脑上,自然无法访问。解决方案就是我在前面提到的:请求路径统一写 /api,开发时用 Vite 代理,生产用 Nginx 代理。
9.5 MongoDB 磁盘占用持续增长
MongoDB 默认不会自动回收删除数据后的磁盘空间。如果频繁删除数据,建议在业务低峰期执行:
bash复制mongosh myapp --eval "db.runCommand({ compact: 'articles' })"
或者用 db.repairDatabase() 进行更彻底的整理,但这个操作在数据量大时会锁定数据库,谨慎使用。
10. 项目上线后的运维小技巧
10.1 日志集中查看
PM2 的默认日志是按进程和错误区分的,分别存在 ~/.pm2/logs/ 下。建议把 PM2 日志做一下切割,防止单个文件太大:
bash复制pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7
10.2 数据备份策略
MongoDB 的备份最简单的方式是用 mongodump:
bash复制mongodump --uri="mongodb://myapp:密码@127.0.0.1:27017/myapp" --out=/backup/$(date +%F)
再配合 crontab 做每日全量备份:
bash复制crontab -e
# 每天凌晨 2:30 执行备份
30 2 * * * /usr/bin/mongodump --uri="mongodb://myapp:密码@127.0.0.1:27017/myapp" --out=/backup/$(date +\%F)
10.3 监控告警
阿里云自带云监控,可以设置磁盘使用率超过 80% 时发短信提醒。Node.js 应用自身的健康检查接口就是 /api/health,可以用外部监控服务定期请求这个接口,如果连续失败就报警。
11. 最后分享两点实操体会
整个部署链路走完,我最大的感受是:开发时觉得“能跑就行”,部署时才发现真正考验人的是“环境一致性”和“网络安全”。很多问题不是代码逻辑导致的,而是环境差异、配置遗漏、端口未放行这些小细节叠加出来的。
我个人的建议是:在动手部署前,先把端口规划、域名、证书、安全组这些信息写在一张表里,对着表格操作,比边查边做要稳得多。另外,不要跳过 MongoDB 的认证启用和端口内网绑定,虽然本地开发时裸奔很方便,但服务器面向公网,这类安全配置绝对不能省。
如果你也在部署这条路上折腾,希望这篇文章能帮你少踩几个坑。后面我会再整理一篇关于 CI/CD 自动部署的实践,把本地提交代码后自动构建、自动部署到服务器的流程补全。
