前后端分离项目云上部署实战:腾讯云CVM + Nginx反向代理 + SpringBoot自动化发布完整代码

apphuang2026年06月28日 17:14:411

一、开篇:为什么选择腾讯云CVM部署前后端分离项目

前后端分离架构已经成为现代Web应用开发的主流模式——后端提供RESTful API,前端独立构建和部署。这种架构带来的开发效率和维护便利性毋庸置疑,但到了部署环节,很多开发者却卡在了"最后一公里":后端jar包不知道放哪里、前端dist文件不会配Nginx、端口不通、跨域报错、页面空白……折腾一整天可能连一个hello world都跑不起来。

本文的目标就是解决这个问题。我们将从一个典型的SpringBoot + Vue前后端分离项目出发,在腾讯云CVM上完整走完从0到上线的全过程。所有操作基于真实的腾讯云控制台界面和Linux命令行,不跳步、不省略、不假设你懂Linux。文章会包含完整的配置文件代码和自动化部署脚本,你可以直接复制使用。

需要先登录腾讯云控制台,点击:腾讯云控制台,还没有账号,点击:注册后再关联,已有账号点击:登录后再关联

二、项目架构与准备

2.1 项目结构

本文以典型的SpringBoot + Vue前后端分离项目为例:

  • 后端:Spring Boot 2.7.x(或3.x),Maven打包生成app.jar,内置Tomcat,默认端口8080
  • 前端:Vue 3 + Vue Router + Axios,npm run build生成dist/静态资源目录
  • 通信方式:前端所有API请求以/api/开头,由Nginx反向代理转发至后端服务
  • 运行环境:腾讯云CVM,Ubuntu 22.04或CentOS 7.9

2.2 服务器选购建议

很多新手买服务器时容易踩坑——要么配置选太小跑不动,要么选错地域导致访问延迟高。对于SpringBoot + Vue项目,建议配置如下:

  • CPU & 内存:2核4GB起步——够跑后端服务 + Nginx + MySQL,留有余量避免OOM
  • 系统镜像:Ubuntu 22.04 或 CentOS 7.9——生态稳定,教程最多
  • 公网带宽:3Mbps起——够日常访问和小流量接口调用,后续可随时升配
  • 地域:选离目标用户最近的数据中心,全国用户可选北京或上海

地域选择有一个重要原则:如果你有多台服务器需要内网通信(如Web服务器+数据库服务器),必须选同一个地域——不同地域的服务器之间内网不互通。

三、安全组配置:开放必要端口

买了服务器之后,新手最容易犯的错误就是忘记配置安全组——服务器配好了、网站部署了,但浏览器就是打不开,排查了半天发现是端口没开。安全组是CVM的"虚拟防火墙",具备状态检测和数据包过滤功能,不开放对应端口,外部流量就进不来。

以下是必须开放的端口清单:

端口协议用途是否必须安全建议
22TCPSSH远程登录✅ 必须建议限制来源IP
80TCPHTTP网站访问✅ 必须对所有IP开放
443TCPHTTPS加密访问✅ 必须对所有IP开放
8080TCPSpringBoot后端按需建议仅内网或通过Nginx代理
3306TCPMySQL数据库按需仅内网开放,严禁对公网

安全组配置实操步骤:

  1. 登录腾讯云控制台,在左侧导航找到"云服务器CVM" → 选择你的实例
  2. 点击"安全组"选项卡 → 点击"添加规则"
  3. 类型选"自定义",来源填0.0.0.0/0(允许所有IP)或指定IP段
  4. 协议端口填TCP:80,443(多端口用逗号分隔),策略选"允许"
  5. 保存后立即生效

安全组配置的几个常见错误:忘记开80/443端口、数据库端口对公网全开放(3306/6379等仅对内网IP开放)、图省事把所有端口全开放、SSH端口不限制来源IP、配置了安全组但忘记绑定到实例。

四、服务器环境搭建

4.1 连接服务器

购买完成后,在腾讯云控制台获取公网IP。Windows用户可以用MobaXterm或Xshell,Mac和Linux直接用终端:

ssh root@你的公网IP

首次登录需重置密码,按提示设一个含大小写字母+数字+符号的强密码。

4.2 安装JDK

Spring Boot 2.7.x推荐JDK 11,Spring Boot 3.x需要JDK 17。以Ubuntu为例:

# Ubuntu/Debian
sudo apt update
sudo apt install -y openjdk-11-jdk

# CentOS/RHEL
yum install -y java-11-openjdk-devel

# 验证安装
java -version

4.3 安装Nginx

Nginx用于托管前端静态资源和反向代理后端API请求:

# Ubuntu/Debian
sudo apt install -y nginx

# CentOS/RHEL
yum install -y nginx

# 启动并设置开机自启
sudo systemctl start nginx
sudo systemctl enable nginx

启动后在浏览器访问 http://你的公网IP,看到"Welcome to nginx!"说明安装成功。

五、部署SpringBoot后端

5.1 本地打包

在项目根目录执行Maven打包命令:

mvn clean package -Dmaven.test.skip=true

参数说明:clean清理之前的构建产物,package执行编译、测试、打包,-Dmaven.test.skip=true跳过测试加速打包并避免测试失败影响构建。打包成功后,jar包位于target/目录下,例如myapp-0.0.1-SNAPSHOT.jar。

5.2 上传jar包到服务器

使用scp命令将jar包上传到服务器:

scp target/myapp-0.0.1-SNAPSHOT.jar root@你的公网IP:/opt/app/

也可以使用SFTP工具(如MobaXterm自带的文件浏览器)直接拖拽上传。

5.3 启动后端服务

使用nohup命令让程序在关闭SSH连接后继续运行:

cd /opt/app
nohup java -jar myapp-0.0.1-SNAPSHOT.jar --server.port=8080 > app.log 2>&1 &

命令解析:nohup让进程不受终端关闭影响,--server.port=8080指定运行端口,> app.log 2>&1将标准输出和错误都重定向到app.log文件,&放入后台运行。

验证服务是否启动成功:

# 查看进程
ps aux | grep myapp

# 测试接口
curl http://localhost:8080/api/health

如果返回正确的JSON数据,说明后端服务已经在服务器上正常工作。

六、部署Vue前端与Nginx配置

6.1 前端打包

在Vue项目根目录执行构建命令:

npm run build

构建完成后生成dist/目录,包含所有静态资源文件(HTML、CSS、JS)。

6.2 上传前端静态文件

将dist/目录下的所有文件上传到服务器的Nginx静态目录。Ubuntu默认的Nginx静态目录是/var/www/html/,CentOS是/usr/share/nginx/html/。

# 创建项目前端目录
sudo mkdir -p /var/www/myapp

# 上传dist文件(在本地执行)
scp -r dist/* root@你的公网IP:/var/www/myapp/

6.3 配置Nginx反向代理

Nginx的核心配置位于/etc/nginx/nginx.conf,但更规范的做法是在/etc/nginx/conf.d/或/etc/nginx/sites-enabled/下创建独立的配置文件。

创建配置文件 /etc/nginx/conf.d/myapp.conf:

server {
    listen 80;
    server_name 你的域名或公网IP;

    # 前端静态资源
    location / {
        root /var/www/myapp;
        index index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    # API反向代理到SpringBoot
    location /api/ {
        proxy_pass http://127.0.0.1:8080/;
        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 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

配置要点解析:

  • location /:托管前端静态文件,try_files确保Vue的SPA路由能正确回退到index.html
  • location /api/:拦截所有以/api/开头的请求,转发到后端SpringBoot服务
  • proxy_pass末尾的/:至关重要——它会剥离匹配的路径前缀(/api/),再转发请求,避免后端收到重复路径
  • proxy_set_header:确保后端能正确识别原始请求的客户端IP、Host和协议

配置完成后测试并重启Nginx:

# 测试配置语法
sudo nginx -t

# 重新加载配置(不中断服务)
sudo systemctl reload nginx

6.4 常见Nginx配置陷阱

根据实际项目经验,Nginx反向代理配置中最容易踩的坑是代理路径错误。例如,前端请求 /api/user/login,但Nginx转发后后端实际收到 /user/login,导致JWT白名单不匹配直接返回401。解决方案是确保proxy_pass配置正确——如果proxy_pass末尾带/,Nginx会移除location匹配的路径前缀。

另一个常见问题是后端实际端口与预期不符。排查时先用curl直接测试后端端口:

curl -i -X POST http://127.0.0.1:8080/api/user/login

如果返回Connection refused,说明端口不对,需要查看jar包内的application.properties确认server.port配置。

七、自动化发布脚本

手动部署的痛点在于每次更新都需要重复执行打包、上传、重启等一系列操作。编写Shell脚本可以将这些步骤自动化。

7.1 服务器端部署脚本

在服务器上创建 /opt/deploy/deploy.sh:

#!/bin/bash
# ================= 配置区 =================
APP_NAME="myapp-0.0.1-SNAPSHOT.jar"
APP_PORT="8080"
WORK_DIR="/opt/app"
BACKUP_DIR="/opt/app/backup"
JAVA_OPTS="-Xms512m -Xmx512m -Dspring.profiles.active=prod"
LOG_FILE="${WORK_DIR}/app.log"
# ===========================================

cd ${WORK_DIR} || exit 1

# 1. 停止旧进程(优雅停止)
echo "[$(date)] Stopping old process..."
PID=$(ps -ef | grep ${APP_NAME} | grep -v grep | awk '{print $2}')
if [ -n "$PID" ]; then
    kill $PID
    # 等待端口释放,最多30秒
    for i in {1..30}; do
        if ! netstat -tlnp 2>/dev/null | grep :${APP_PORT} > /dev/null 2>&1; then
            break
        fi
        sleep 1
    done
    # 如果端口仍占用,强制杀死
    if netstat -tlnp 2>/dev/null | grep :${APP_PORT} > /dev/null 2>&1; then
        PID=$(ps -ef | grep ${APP_NAME} | grep -v grep | awk '{print $2}')
        [ -n "$PID" ] && kill -9 $PID
        sleep 2
    fi
    echo "[$(date)] Old process stopped."
fi

# 2. 备份旧版本
if [ -f "${WORK_DIR}/${APP_NAME}" ]; then
    mkdir -p ${BACKUP_DIR}
    cp ${APP_NAME} ${BACKUP_DIR}/${APP_NAME}.$(date +%Y%m%d%H%M%S)
    echo "[$(date)] Backup completed: ${BACKUP_DIR}/${APP_NAME}.$(date +%Y%m%d%H%M%S)"
fi

# 3. 启动新服务
echo "[$(date)] Starting new process..."
nohup java ${JAVA_OPTS} -jar ${APP_NAME} --server.port=${APP_PORT} > ${LOG_FILE} 2>&1 &

# 4. 健康检查(等待最多60秒)
echo "[$(date)] Checking health..."
HEALTH_URL="http://localhost:${APP_PORT}/actuator/health"
for i in {1..60}; do
    sleep 1
    if curl -s ${HEALTH_URL} 2>/dev/null | grep -q "UP"; then
        echo "[$(date)] Deployment successful!"
        exit 0
    fi
done

# 如果健康检查失败,回滚
if [ -f "${BACKUP_DIR}/$(ls -t ${BACKUP_DIR} | head -1)" ]; then
    echo "[$(date)] Health check failed! Rolling back..."
    LATEST_BACKUP=$(ls -t ${BACKUP_DIR} | head -1)
    cp ${BACKUP_DIR}/${LATEST_BACKUP} ${WORK_DIR}/${APP_NAME}
    # 重新启动旧版本
    nohup java ${JAVA_OPTS} -jar ${APP_NAME} --server.port=${APP_PORT} > ${LOG_FILE} 2>&1 &
    echo "[$(date)] Rollback completed. Please check logs."
fi

echo "[$(date)] Deployment failed! Check ${LOG_FILE}"
exit 1

脚本核心逻辑:

  • 停止旧进程:先优雅停止(kill),等待端口释放,如果超时则强制杀死(kill -9)
  • 备份旧版本:将当前运行的jar备份到backup目录,带时间戳
  • 启动新服务:使用nohup后台启动新jar包
  • 健康检查:通过/actuator/health端点验证服务是否正常启动
  • 自动回滚:如果健康检查失败,自动恢复最近备份的版本

7.2 本地一键发布脚本

在本地项目根目录创建 publish.sh:

#!/bin/bash
# ================= 配置区 =================
SERVER_IP="你的公网IP"
SERVER_USER="root"
SERVER_DIR="/opt/app/"
LOCAL_JAR="./target/myapp-0.0.1-SNAPSHOT.jar"
# ===========================================

# 1. 本地打包
echo "[$(date)] Building project..."
mvn clean package -Dmaven.test.skip=true
if [ $? -ne 0 ]; then
    echo "Build failed!"
    exit 1
fi

# 2. 上传jar包
echo "[$(date)] Uploading jar to server..."
scp ${LOCAL_JAR} ${SERVER_USER}@${SERVER_IP}:${SERVER_DIR}
if [ $? -ne 0 ]; then
    echo "Upload failed!"
    exit 1
fi

# 3. 远程执行部署脚本
echo "[$(date)] Executing deploy script on server..."
ssh ${SERVER_USER}@${SERVER_IP} "bash /opt/deploy/deploy.sh"

echo "[$(date)] Publish completed!"

使用方法:

chmod +x publish.sh deploy.sh
./publish.sh

脚本执行后,会自动完成打包→上传→停止旧服务→备份→启动新服务→健康检查的完整流程。

八、生产环境增强建议

8.1 使用Systemd管理服务

纯nohup启动缺乏守护能力,生产环境建议使用systemd管理。创建 /etc/systemd/system/myapp.service:

[Unit]
Description=MyApp Spring Boot Service
After=network.target

[Service]
User=root
WorkingDirectory=/opt/app
ExecStart=/usr/bin/java -Xms512m -Xmx512m -jar /opt/app/myapp-0.0.1-SNAPSHOT.jar --server.port=8080
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable --now myapp

8.2 日志切割

使用logrotate防止app.log无限增长。创建 /etc/logrotate.d/myapp:

/opt/app/app.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 644 root root
    postrotate
        systemctl reload myapp > /dev/null 2>&1 || true
    endscript
}

8.3 配置HTTPS

生产环境建议启用HTTPS。使用腾讯云SSL证书或Let's Encrypt免费证书,在Nginx配置中添加443端口监听:

server {
    listen 443 ssl http2;
    server_name 你的域名;
    ssl_certificate /etc/nginx/ssl/your-domain.crt;
    ssl_certificate_key /etc/nginx/ssl/your-domain.key;
    # ... 其他配置同80端口
}

server {
    listen 80;
    server_name 你的域名;
    return 301 https://$server_name$request_uri;
}

8.4 安全加固

几个关键的安全措施:

  • 为每个应用分配独立的系统用户运行,防止文件权限冲突
  • 数据库端口(3306、6379)仅对内网开放,严禁暴露到公网
  • SSH端口限制来源IP,或改用密钥登录
  • Nginx配置中隐藏后端服务版本信息

九、总结

本文完整梳理了前后端分离项目在腾讯云CVM上的部署全流程,从服务器选购、安全组配置、环境搭建、后端部署、前端托管、Nginx反向代理配置,到自动化发布脚本的编写。核心要点可以概括为:

  • 安全组是第一步——80、443等端口必须开放,数据库端口严禁对公网
  • Nginx反向代理是核心——统一入口、静态托管、API转发三位一体,注意proxy_pass末尾的/决定路径拼接方式
  • 自动化脚本是效率提升的关键——包含停止、备份、启动、健康检查、回滚的完整流程
  • 生产环境需要systemd + 日志切割 + HTTPS——提升服务的稳定性和安全性

通过本文的方案,你可以将部署时间从手动操作的30分钟缩短到脚本一键执行的2分钟,同时大幅降低人为失误的风险。


常见问题与解答

问1:部署后访问页面空白,但Nginx欢迎页能打开,是什么原因?
答:通常是前端静态文件路径不对。检查Nginx配置中root指向的目录是否包含了正确的dist文件,以及try_files配置是否正确。另外检查浏览器控制台是否有404错误,确认静态资源(JS、CSS)是否加载成功。

问2:API请求返回404,但后端服务已经在跑,怎么排查?
答:首先用curl直接测试后端端口:curl http://localhost:8080/api/xxx,确认后端能正常响应。如果后端正常但通过Nginx访问404,问题在Nginx代理配置——检查location和proxy_pass的路径拼接是否正确,特别是proxy_pass末尾是否有/。

问3:部署脚本中的健康检查一直失败怎么办?
答:健康检查依赖Spring Boot Actuator的/actuator/health端点。如果项目没有引入spring-boot-starter-actuator依赖,或者management.endpoints.web.exposure.include没有包含health,这个端点会返回404。解决方案:在pom.xml中添加actuator依赖,或在application.properties中配置management.endpoints.web.exposure.include=health。

问4:为什么我的Nginx配置修改后不生效?
答:可能是配置语法错误导致Nginx重载失败。先用nginx -t测试语法,如果有错误会提示具体位置。另外确认修改的是正确的配置文件——Ubuntu下/etc/nginx/sites-enabled/中的配置会覆盖/etc/nginx/nginx.conf中的默认配置。

问5:自动化脚本中scp上传失败,提示Permission denied怎么办?
答:检查服务器是否开启了密码登录(默认开启),或者改用密钥认证。如果使用root用户,确认sshd_config中PermitRootLogin设置为yes。另外检查服务器磁盘空间是否充足。

问6:前后端分离项目部署后出现跨域问题,但本地开发时没有,怎么解决?
答:本地开发时前端devServer通常会配置proxy代理,但生产环境通过Nginx反向代理解决跨域是最佳实践。只要Nginx将/api/请求正确转发到后端同一域名/IP的不同端口,浏览器就不会有跨域限制。如果仍有跨域问题,检查Nginx配置中proxy_set_header是否正确传递了Host和Origin。

相关文章

腾讯云服务器购买优惠!3 个省钱攻略 + 1 个安全真相,新手必看!

腾讯云服务器购买优惠!3 个省钱攻略 + 1 个安全真相,新手必看!

最近后台总收到小伙伴私信:“腾讯云服务器看着挺好,但价格有点顶,学生党 / 小团队实在买不起咋办?” 别急!今天就来手把手教你 “花小钱办大事”,不光有省钱攻略,还会扒一扒大家最关心的安全问题,看完这…

After 10 Years as a Tencent Cloud Agent, Let Me Talk About Rebates

After 10 Years as a Tencent Cloud Agent, Let Me Talk About Rebates

Lately, I’ve been getting a lot of questions from friends: “Does Tencent offer rebates? Can you…

2026腾讯云代理商返利政策深度解析:头部代理合作指南与成本优化策略

2026腾讯云代理商返利政策深度解析:头部代理合作指南与成本优化策略

一、腾讯云代理商返利机制核心逻辑1. 行业背景与代理模式腾讯云作为国内公有云市场的第二大领导者(据IDC 2025年数据,占据国内27.6%的市场份额),采用渠道商代理模式拓展市场。代理商负…

2026腾讯云代理商返佣政策全解析:五级代理体系与企业上云成本优化指南

2026腾讯云代理商返佣政策全解析:五级代理体系与企业上云成本优化指南

一、腾讯云五级代理体系:权益阶梯与合作价值1. 五级代理的核心权益差异腾讯云按规模、服务能力与合作深度,构建了从基础到顶级的五级代理体系,各级权益呈现显著阶梯差:•标准级代理:入门门槛最低,仅能提供基…

2026年腾讯云代理深度解析:从折扣体系到最优合作策略

2026年腾讯云代理深度解析:从折扣体系到最优合作策略

上海汪远信息科技有限公司作为腾讯云全国级殿堂级代理,凭借13年云服务经验与深厚的官方合作关系,为企业提供全方位的上云支持,可百度:上海汪远信息科技有限公司,微信:791201210一、腾讯云代理体系全…

上海汪远信息:全国Top5腾讯云代理商,10年深耕为企业上云保驾护航

上海汪远信息:全国Top5腾讯云代理商,10年深耕为企业上云保驾护航

核心摘要本文深度解析腾讯云代理商行业现状,揭示小代理商生存困境的核心原因(低业绩导致提成少、厂商压款、市场淘汰),重点推荐上海汪远信息科技有限公司——一家拥有10年腾讯云代理经验、年销量超2亿的全国T…