Note
web项目部署
把web全栈项目部署到Ubuntu系统
发布于 更新于
Ubuntu 部署步骤
1. 部署默认值
本方案使用 nvm 管理 Node.js,因为不同项目可能需要不同 Node 版本。注意:nvm 是“按用户安装”的,所以必须在对应用户下安装,如这里使用的是 deployer ,不要在 root 下安装。并且项目以my_cs_logs为例。
占位符:
<你的服务器IP> 例如 1.2.3.4
<你的子域名> 例如 cs.example.com
<你的Git仓库地址> 例如 git@github.com:xxx/my_cs_logs.git
<数据库强密码> 你自己设置的 MySQL 密码
<JWT随机密钥> 后面用 openssl 命令生成
<项目目录> 以下为my_cs_logs
<数据库> 以下为my_cs_logs
部署结构:
Nginx:负责域名、HTTPS、前端静态文件、API 反向代理
NestJS:后端服务,只监听服务器本机 3000 端口
MySQL:安装在同一台 Ubuntu 服务器
nvm:给 deployer 用户管理 Node.js 版本
2. 环境配置
2.1域名解析和安全组
域名绑定IP
在域名控制台添加解析:
记录类型:A
主机记录:<子域名前缀>
记录值:<你的服务器IP>
例如 `cs.example.com`:
记录类型:A
主机记录:cs
记录值:1.2.3.4
端口处理
放行:
22 SSH 登录服务器
80 HTTP 访问、申请 HTTPS 证书
443 HTTPS 正式访问
不要放行:
3000 本地后端端口,只允许 Nginx 在服务器内部访问
3306 本地MySQL 端口,只允许后端在服务器内部访问
验证
# 在你本地电脑执行:测试域名是否已经解析到服务器 IP
ping <你的子域名>
2.2登录服务器并创建部署用户
目的:角色化,不同角色负责不同的职责,专门创建一个负责部署项目的角色
# 用 root 登录服务器;如果云厂商默认用户是 ubuntu,就把 root 换成 ubuntu
ssh root@<你的服务器IP>
# 创建 deployer 用户,后续项目都用这个用户部署
adduser deployer
# 给 deployer 用户添加 sudo 权限,让它能执行管理员命令
usermod -aG sudo deployer
# 切换到 deployer 用户;nvm 必须在这个用户下安装
su - deployer
2.3 安装基础软件
更新软件
# 更新 apt 软件源索引
sudo apt update
# 升级服务器已有软件包;-y 表示自动确认
sudo apt upgrade -y
安装必须软件
# 安装 Git、curl、证书工具、编译工具、Nginx、MySQL
sudo apt install -y git curl ca-certificates build-essential nginx mysql-server
安装Node.js(使用nvm)
# 确认当前用户是 deployer;如果不是 deployer,先不要继续
whoami
# 安装 nvm;这是官方 nvm 仓库当前推荐的安装方式
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.4/install.sh | bash
# 让当前终端立即加载 nvm;否则可能要重新登录后 nvm 才能使用
source ~/.bashrc
# ======================================================================================
❗如果 `source ~/.bashrc` 后仍提示 `nvm: command not found`,执行以下命令,没有问题则跳过
# 手动加载 nvm 脚本;用于解决当前 shell 没有自动加载 nvm 的情况
export NVM_DIR="$HOME/.nvm"
# 如果 nvm.sh 存在,就加载它
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
# ======================================================================================
# 安装 Node.js 22 最新小版本(根据项目需求安装对应版本即可)
nvm install 22
# 切换当前终端使用 Node.js 22
nvm use 22
# 把 Node.js 22 设为 deployer 用户默认版本,重新登录后也会默认使用它
nvm alias default 22
验证
# 查看 MySQL 状态;active (running) 表示正常运行
sudo systemctl status mysql
# 查看 Nginx 状态;active (running) 表示正常运行
sudo systemctl status nginx
# 查看 Node.js 版本,应当是 v22.x.x,且不低于 v22.12.0
node -v
# 查看 npm 版本,确认 npm 可用
npm -v
# 查看 node 命令路径;应当在 /home/deployer/.nvm/ 下面
which node
3. 项目配置
3.1 获取项目
创建目录
# 创建 /var/www 目录,通常用来存放网站项目
sudo mkdir -p /var/www
# 把 /var/www 目录所有者改成 deployer,后续 deployer 才能写入项目文件
sudo chown -R deployer:deployer /var/www
克隆项目
# 从 Git 仓库克隆项目到服务器
git clone <你的Git仓库地址> /var/www/my_cs_logs
# 如果是私有仓库,需要获取秘钥,以下为获取方法(以Gitee为例):
# 生成服务器 SSH key,用来访问 GitHub/Gitee 私有仓库
# -t key 类型
# -C 注释
ssh-keygen -t ed25519 -C "deployer@my_cs_logs"
# 会出现需要确定的内容,中间通过三次回车键确定即可
# 查看生成的 SSH 公钥和私钥
ls ~/.ssh/ # 输出:私密文件id_ed25519 公钥文件id_ed25519.pub
# 读取公钥文件 ~/.ssh/id_ed25519.pub
cat ~/.ssh/id_ed25519.pub
# 输出如ssh-ed25519 AAAA***5B Gitee SSH Key,把它添加到个人的SSH 公钥中即可
# 在通过SSH克隆仓库即可,如git clone git@gitee.com:July1st/my_cs_logs.git
# 进入项目根目录
cd /var/www/my_cs_logs
3.2 初始化 MySQL
进入MYSQL命令行
# 进入项目根目录,确保能找到 my_cs_logs.sql
cd /var/www/my_cs_logs
# 导入项目 SQL;会创建 my_cs_logs 数据库和表
sudo mysql < my_cs_logs.sql
# 进入 MySQL 管理命令行
sudo mysql
创建MYSQL账户
-- 创建项目专用数据库用户,只允许服务器本机连接
CREATE USER '<数据库用户名>'@'localhost' IDENTIFIED BY '<数据库强密码>';
-- 授权项目用户读写 my_cs_logs 数据库
GRANT SELECT, INSERT, UPDATE, DELETE ON my_cs_logs.* TO '<数据库用户名>'@'localhost';
-- 刷新权限,让授权立即生效
FLUSH PRIVILEGES;
-- 退出 MySQL 命令行
EXIT;
验证账号
# 使用项目数据库用户登录,会要求输入 <数据库强密码>
mysql -u <数据库用户名> -p my_cs_logs
-- 查看是否已经成功导入表
SHOW TABLES;
-- 退出 MySQL
EXIT;
3.3 配置后端环境变量
# 进入 NestJS 后端目录
cd /var/www/my_cs_logs/service-nest
# 复制环境变量模板为正式 .env 文件
cp .env.example .env
# 编辑 .env 文件;nano操作:Ctrl + O 保存; Enter 确认文件名; Ctrl + X 退出
nano .env
.env 内容修改
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=<数据库用户>
DB_PASSWORD=<数据库强密码>
DB_DATABASE=my_cs_logs
JWT_SECRET=<JWT随机密钥>
JWT_EXPIRES_IN=7d
NODE_ENV=production
// 配置邮箱 SMTP
MAIL_HOST=
MAIL_PORT=587
MAIL_SECURE=false
MAIL_USER=
MAIL_PASS=
MAIL_FROM=
MAIL_CODE_EXPIRES_MINUTES=5
MAIL_CODE_RESEND_SECONDS=60
# 生成 JWT_SECRET,用输出结果替换 .env 里的 <JWT随机密钥>
openssl rand -base64 48
3.4 构建并启动后端
构建并启动
# 进入后端目录
cd /var/www/my_cs_logs/service-nest
# 根据 package-lock.json 安装后端依赖,服务器部署推荐 npm ci
npm ci
# 编译 NestJS,把 TypeScript 编译到 dist 目录
npm run build
# 创建日志目录,后端会写入 logs/error.log 和 logs/combined.log
mkdir -p logs
# 安装 PM2;因为使用 nvm,所以不要加 sudo
npm install -g pm2
# 查看 pm2 路径,应当在 /home/deployer/.nvm/ 下面
which pm2
# 用 PM2 启动后端;--cwd 用来指定工作目录,保证 .env 能被读取
pm2 start dist/main.js --name my-cs-logs-api --cwd /var/www/my_cs_logs/service-nest
# 查看 PM2 进程状态,online 表示后端正在运行
pm2 status
# 查看后端日志;如果卡住不退出,按 Ctrl + C 返回
pm2 logs my-cs-logs-api
# 保存当前 PM2 进程列表,用于服务器重启后恢复
pm2 save
# 生成 PM2 systemd 开机自启配置;适配 deployer 用户和 nvm 环境
pm2 startup systemd -u deployer --hp /home/deployer
# 上一条命令会输出一条 `sudo ...` 开头的命令,把那条 sudo 命令复制出来,再执行一次。
验证
# 直接请求服务器本机后端接口,确认 NestJS 正常
curl http://127.0.0.1:3000/api/csLogs/getDictionary
3.5 构建前端
# 进入前端目录
cd /var/www/my_cs_logs/web
# 根据 package-lock.json 安装前端依赖
npm ci
# 构建前端,生成 dist 静态文件。构建产物:/var/www/my_cs_logs/web/dist
npm run build
4. 代理配置(Nginx)
创建配置文件
# 创建 Nginx 站点配置文件
sudo nano /etc/nginx/sites-available/my_cs_logs
写入
server {
listen 80;
server_name <你的子域名>;
root /var/www/my_cs_logs/web/dist;
index index.html;
client_max_body_size 20m;
location /api/ {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
配置含义
listen 80 监听 HTTP
server_name 绑定你的子域名
root 指向前端 dist 目录
location /api/ API 请求交给后端
proxy_pass 127.0.0.1:3000 转发到 NestJS
try_files ... /index.html 支持 Vue 路由刷新不 404
启用站点
# 把 sites-available 里的配置链接到 sites-enabled,表示启用这个站点
sudo ln -s /etc/nginx/sites-available/my_cs_logs /etc/nginx/sites-enabled/my_cs_logs
# 删除 Nginx 默认站点,避免默认欢迎页占用域名
sudo rm -f /etc/nginx/sites-enabled/default
# 检查 Nginx 配置语法
sudo nginx -t
# 重新加载 Nginx 配置
sudo systemctl reload nginx
验证
http://<你的子域名>
5. 配置 HTTPS
# 安装 snap 基础组件
sudo snap install core
# 更新 snap core
sudo snap refresh core
# 移除 apt 版本 certbot,避免和 snap 版本冲突
sudo apt remove -y certbot
# 安装官方推荐的 snap 版 certbot
sudo snap install --classic certbot
# 创建 certbot 命令软链接,让系统能直接执行 certbot
sudo ln -sf /snap/bin/certbot /usr/bin/certbot
# 为子域名申请 HTTPS 证书,并让 certbot 自动修改 Nginx 配置
sudo certbot --nginx -d <你的子域名>
# 测试证书自动续期是否正常
sudo certbot renew --dry-run
验证
https://<你的子域名>
6. 问题排查
# 查看后端 PM2 进程是否在线
pm2 status
# 查看 Nginx 是否正常运行
sudo systemctl status nginx
# 查看 MySQL 是否正常运行
sudo systemctl status mysql
# 查看 HTTPS 首页响应头
curl -I https://<你的子域名>
# 测试 API 是否能通过域名访问
curl https://<你的子域名>/api/csLogs/getDictionary
浏览器检查:
1. https://<你的子域名> 可以打开
2. 刷新页面不 404
3. 登录/注册页面可以打开
4. API 请求不报 502
5. 配好 SMTP 后验证码能收到
14. 后续更新项目
# 进入项目根目录
cd /var/www/my_cs_logs
# 拉取最新代码
git pull
数据库更新:
# 进入项目根目录
cd /var/www/my_cs_logs
# 创建数据库备份目录
mkdir -p ~/db-backups
# 备份当前数据库,防止迁移 SQL 执行失败后无法恢复
sudo mysqldump --single-transaction --no-tablespaces my_cs_logs > ~/db-backups/my_cs_logs_$(date +%F_%H%M%S).sql
# 执行数据库脚本
sudo mysql my_cs_logs < service-nest/sql/XXXXX.sql
后端更新:
# 进入后端目录
cd /var/www/my_cs_logs/service-nest
# 确认node版本
nvm use 22
# 重新安装后端依赖(非必须)
npm ci
# 重新构建后端
npm run build
# 重启后端服务
pm2 restart my-cs-logs-api
前端更新:
# 进入前端目录
cd /var/www/my_cs_logs/web
# 确认node版本
nvm use 22
# 重新安装前端依赖(非必须)
npm ci
# 重新构建前端
npm run build
# 重新加载 Nginx
sudo systemctl reload nginx
15. 常见问题
nvm: command not found:确认你是在deployer用户下;执行source ~/.bashrc,或重新 SSH 登录。node路径不在/home/deployer/.nvm/:说明当前可能没用 nvm 的 Node,执行nvm use 22。sudo npm找不到或版本不对:使用 nvm 后不要用sudo npm install -g ...。502 Bad Gateway:后端没启动,检查pm2 status和pm2 logs my-cs-logs-api。- 页面打不开:检查 DNS、安全组、
sudo systemctl status nginx。 - 接口失败:检查 Nginx
/api/代理和后端3000端口。 - 数据库失败:检查
.env的数据库账号、密码、库名。 - 验证码失败:检查 SMTP 配置。
参考文档
- nvm 官方安装说明:nvm-sh/nvm README
- MySQL 安装:Ubuntu MySQL Server docs
- PM2 开机自启:PM2 Startup Hook
- HTTPS 证书:Certbot Nginx instructions