AI写的系统怎么部署到服务器?以Spring Boot + Vue为例,走通部署与恢复检查
系统在自己电脑上已经能运行了,下一步怎么让同事也能用? 这时最容易想到的是买台服务器,把代码传上去。但代码传完之后,问题才逐渐具体起来:前端为什么还在请求 localhost?后端重启后上传的附件还在不在?数据库能不能直接开放端口?下次更新失败,又该退回哪里? 上一篇讨论了AI写的系统适合承担什么业
AI写的系统怎么部署到服务器?以Spring Boot + Vue为例,走通部署与恢复检查
系统在自己电脑上已经能运行了,下一步怎么让同事也能用?
这时最容易想到的是买台服务器,把代码传上去。但代码传完之后,问题才逐渐具体起来:前端为什么还在请求 localhost?后端重启后上传的附件还在不在?数据库能不能直接开放端口?下次更新失败,又该退回哪里?
上一篇讨论了AI写的系统适合承担什么业务责任。这篇往下走一步,用一个项目管理工具的示例,把服务器部署、上线检查和恢复验证串起来。
本文面向已经做出原型、能看懂基本配置的业务负责人和产品经理,也适合负责接手原型的开发人员。
范围说明:本文包含Ubuntu教学部署方案,以及一次Windows本地隔离恢复实测,不是客户项目实测报告。第六节记录了小样本验证结果;Ubuntu、Nginx、HTTPS及完整业务验收尚未实测。命令、路径和接口名均需按实际项目调整。部署成功也不等于业务、安全和性能已经验收。
目录
- [先确认系统由哪些部分组成](#一先确认系统由哪些部分组成)
- [把代码、配置和业务文件分开](#二把代码配置和业务文件分开)
- [先让后端成为一个可管理的服务](#三先让后端成为一个可管理的服务)
- [让前端和接口通过同一个入口访问](#四让前端和接口通过同一个入口访问)
- [按层验收不要只看首页](#五按层验收不要只看首页)
- [做一次隔离恢复检查](#六做一次隔离恢复检查)
- [本地实测:数据库恢复了,附件为什么还打不开?](#本地实测数据库恢复了附件为什么还打不开)
- [升级失败时究竟回退什么](#七升级失败时究竟回退什么)
- [留下一份下一位维护者能用的交付记录](#八留下一份下一位维护者能用的交付记录)
一、先确认系统由哪些部分组成
本文选择一个明确的示例范围:
| 部分 | 本文假设 | 上线前需要核对 |
|---|---|---|
| 服务器 | Ubuntu 24.04,使用systemd | 已安装运行环境,有授权的管理入口 |
| 前端 | Vue静态构建产物 dist/ | 不是SSR应用,不依赖开发服务器 |
| 后端 | 与Java 17兼容的Spring Boot可执行JAR | JDK、依赖和实际项目匹配;不是所有项目都能直接用Java 17 |
| 数据库 | MySQL 8.4,单机示例 | 驱动、SQL语法、字符集及初始化脚本已核对 |
| 访问入口 | Nginx、域名、有效TLS证书 | 域名、证书与访问范围均已准备好 |
| 业务文件 | 独立上传目录 | 应用确实读写该目录,不放进程序包 |
这里不包含Redis、消息队列、SSO或第三方回调。如果你的系统依赖这些组件,也必须纳入清单;不能看到页面运行了,就认为依赖已经齐全。运行环境安装、证书签发和业务代码开发不在本篇展开。

图1:用户通过HTTPS访问Nginx,后端与数据库只在服务器内部通信。单机部署是演示边界,不是高可用方案。
请求路径可以理解为:
浏览器 -> HTTPS / Nginx -> 静态前端文件
-> /api/ -> Spring Boot -> MySQL
-> 上传文件目录
本例后端监听 127.0.0.1:8080,MySQL监听本机地址,不向公网发布8080和3306。对外业务入口是443;管理入口通过VPN或受限来源地址访问。需要HTTP跳转时再按部署策略开放80。
这一步的验收是明确“谁能访问什么”。不能只看云安全组,也要检查进程实际监听地址和主机防火墙。
二、把代码、配置和业务文件分开
示例目录如下:
/opt/ai-project/
releases/
r001/
app.jar
web/ # 前端dist目录内的文件
current -> releases/r001
/etc/ai-project/
application-prod.yml
app.env # 仅在服务器保存,不进入仓库
/srv/ai-project/uploads/ # 上传附件,跨版本保留
/var/backups/ai-project/ # 本机临时备份落点,还需独立副本
代码版本目录由部署人员维护,运行账号不能随意修改。后端使用专用的 aiapp 用户;Nginx仅需读取静态文件。上传目录单独授予后端写权限,不使用 chmod -R 777 解决所有权限问题。
数据库先创建空业务库、受限运行账号,并执行经过审查的初始化脚本。应用不要使用数据库root账号。升级表结构可以由独立迁移账号完成,避免运行账号长期持有不必要的建表、删表权限。
下面是 /etc/ai-project/application-prod.yml 的配置片段:
server:
address: 127.0.0.1
port: 8080
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/ai_project
username: ${DB_USER}
password: ${DB_PASSWORD}
app:
upload-dir: /srv/ai-project/uploads
app.upload-dir 是本文约定的业务配置,不是Spring Boot自动提供的上传存储功能。应用代码必须绑定并使用它;如果项目使用其他配置名称,需要相应替换。
DB_USER、DB_PASSWORD 在服务器的 app.env 中按 KEY=value 保存。文件由root管理、权限设为600;不要把真实密码放进文章、Git、截图或启动命令。环境文件只是本例的基础做法,不等同于专业密钥管理,高要求环境应使用受控凭证服务。
Spring Boot支持外部配置及环境变量,使用独立配置可以让同一程序包用于不同环境,具体覆盖顺序应以项目版本文档为准。参考:Spring Boot外部配置
三、先让后端成为一个可管理的服务
不建议把日常运行依赖在某个终端窗口里。本文用systemd管理JAR进程。
在已创建 aiapp 用户、目录、配置和首个发布版本后,建立 /etc/systemd/system/ai-project.service:
[Unit]
Description=AI Project Application
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=aiapp
Group=aiapp
WorkingDirectory=/opt/ai-project/current
EnvironmentFile=/etc/ai-project/app.env
ExecStart=/usr/bin/java -jar /opt/ai-project/current/app.jar --spring.profiles.active=prod --spring.config.additional-location=file:/etc/ai-project/
Restart=on-failure
RestartSec=5
UMask=0027
NoNewPrivileges=true
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target
/usr/bin/java 必须指向项目兼容的JDK。数据库也需要独立设置开机启动并确认就绪,network-online.target 不代表数据库已可用。重启策略只能处理一部分进程退出情况,不能代替业务监控。参考:systemd.service
在示例服务器执行:
sudo systemctl daemon-reload
sudo systemctl enable --now ai-project
sudo systemctl status ai-project --no-pager
sudo journalctl -u ai-project -n 100 --no-pager
sudo ss -lntp
预期看到后端正常运行且仅监听本机8080。日志要检查是否成功连接业务库,而不只是有没有出现“Started”。查看或分享日志前,需要去除凭证、个人信息和敏感业务数据。
如果启动失败,优先定位最早出现的有效错误:JDK不匹配、配置缺失、数据库连接失败、表结构未初始化,还是文件权限不足。不要连续重启后只看最后一行日志。
四、让前端和接口通过同一个入口访问
前端构建时,接口基础地址设置为 /api,不要保留开发电脑的 localhost:8080。浏览器里的localhost指的是访问者自己的电脑,不是服务器。
本例假设后端接口本身就包含 /api/ 前缀,且Nginx直接终止TLS。在证书已经就位的前提下,Nginx配置示例如下:
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/nginx/tls/app.fullchain.pem;
ssl_certificate_key /etc/nginx/tls/app.key;
root /opt/ai-project/current/web;
index index.html;
client_max_body_size 20m;
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;
}
location / {
try_files $uri $uri/ /index.html;
}
}
把示例域名替换为已配置的域名,并按当前Nginx配置的include机制加载文件。这里的 proxy_pass 没有附加URI,会保留本例的 /api/ 路径;如果后端实际没有该前缀,代理规则就需要调整,不能盲目照搬末尾斜杠。参考:Nginx proxy_pass
TLS证书、私钥权限和续期必须单独管理,本例并未实现自动续期。参考:Nginx HTTPS配置
完成后先检查配置,再加载:
sudo nginx -t
sudo systemctl reload nginx
还要实际测试一个前端子页面的刷新,以及一个真实API请求。首页能打开而接口失败时,重点核对浏览器请求地址、代理路径和后端监听状态。
附件通过经过鉴权的下载接口返回;不要顺手把整个上传目录设成公网静态目录。Nginx的20MB限制也需要与应用上传限制匹配。后台处理很久的大任务,不能只靠延长代理超时解决。
五、按层验收,不要只看首页

图2:每一步都留下检查证据;访问成功只是部署检查的一部分。
下面是可直接用作验收记录的清单。“预期”不是“实测”,最后一列需要部署人员在目标环境实际填写。第六节的本地小样本验证不代替这张完整上线验收表。
| 检查项 | 操作示例 | 预期结果 | 实测结果 |
|---|---|---|---|
| 运行环境 | 核对Java、MySQL和构建版本 | 与该发布包兼容 | 待填写 |
| 进程与监听 | 检查服务状态和监听地址 | 进程正常,后端及数据库不暴露公网 | 待填写 |
| 页面与API | 登录、刷新项目详情页、查询项目 | 页面和接口都正确,不返回伪装成200的错误页 | 待填写 |
| 数据持久化 | 新建测试项目,重启应用后查询 | 记录仍存在,字段内容一致 | 待填写 |
| 数据权限 | 授权测试中,用A部门测试账号访问B部门记录 | 返回约定的拒绝结果,不返回越权数据 | 待填写 |
| 业务一致性 | 选择不应重复的提交动作,模拟重试 | 不产生重复业务结果 | 待填写 |
| 附件存储 | 上传测试附件,更新程序后下载 | 文件存在且下载权限仍有效 | 待填写 |
| 重启与恢复 | 在测试环境重启服务器、恢复备份 | 服务可重新启动,恢复数据通过核对 | 待填写 |
接口路径以你的系统为准,本文不虚构一个所有项目都有的 /health。如果使用健康检查端点,应按项目版本配置,并避免向公网暴露诊断详情。
越权、重复提交和重启测试只在你有授权的隔离环境中进行,使用测试数据。已有业务的生产系统需要维护窗口,不能为了写文章直接停机验证。
常见的“现象—检查位置”也可以整理成小表:
| 现象 | 优先检查 |
|---|---|
| 首页能打开,API出现502 | 后端进程、监听地址、Nginx错误日志 |
| 接口404或返回HTML | /api前缀、代理规则、前端实际请求地址 |
| 程序正常启动,查询报表不存在 | 连的是哪个库、初始化和迁移脚本是否执行 |
| 更新后附件丢失 | 是否把上传文件放进了被替换的发布目录 |
| 能登录,但访问其他部门数据 | 服务端数据范围校验,不只是页面菜单 |
这些是排查起点,不是仅凭一个状态码就能确定原因。
六、做一次隔离恢复检查

图3:程序版本、数据库、附件和配置需要一起管理,但不能用同一种方式恢复。
“有备份文件”与“能够恢复”之间,还差一次验证。
以全部业务表使用InnoDB、导出期间不做DDL变更为前提,手工演示备份可以采用:
# 使用已经按需授权的备份账号;-p交互输入密码,不把密码写在命令里。
# 目标目录事先创建,并仅授权备份操作人员访问。
set -e
umask 077
backup_file="/var/backups/ai-project/db-$(date +%Y%m%d-%H%M%S).sql"
mysqldump -h 127.0.0.1 -u backup_user -p \
--single-transaction --routines --triggers --events \
--no-tablespaces --set-gtid-purged=OFF \
ai_project > "$backup_file"
test -s "$backup_file"
sha256sum "$backup_file"
备份账号需要具备本次导出对象要求的权限;权限不足或工具报错时,不能把留下的SQL文件当成成功备份。文件非空和校验和都只是基础检查,不代表业务数据正确。
--single-transaction 的一致性能力有存储引擎和并发DDL限制,不能推广为所有表都可无条件在线一致备份。参考:MySQL mysqldump
这个命令是手工演示,不是完整的自动备份方案。长期运行还要有调度、失败告警、加密、保留周期和异机副本;不能把唯一备份留在同一块服务器磁盘上。
恢复验证时,使用独立测试MySQL实例和新建测试库,关闭可能产生外部副作用的定时事件、通知和第三方集成,不直接恢复到生产库。由管理员预先创建 ai_project_restore 并授权恢复账号,再导入:
# 以下主机名必须替换为确认无误的隔离测试数据库地址。
mysql -h restore-db.example.internal -u restore_user -p \
ai_project_restore < /path/to/verified-backup.sql
这里导出时没有使用 --databases,以便将内容导入指定测试库。但如果例程、视图或SQL内部显式写了库名,或者存在DEFINER权限要求,仍需要审查处理。不能只改数据库连接地址就默认隔离完成。参考:MySQL SQL格式备份恢复
验证至少包含三件事:
- 比较关键表数量、抽样业务记录和关联关系;在线备份应与对应快照口径比较,而非和持续变化的实时库硬比。
- 用兼容版本应用连接恢复库,在隔离环境走一次核心业务。
- 从对应附件备份恢复测试文件,检查记录里的文件引用是否仍然可用。
数据库和附件分开备份时,还需要约定一致性窗口,例如维护窗口内暂停写入,或设计可关联的快照机制。否则可能恢复了附件记录,却找不到对应文件。
记下恢复耗时和备份时间点。它们分别帮助判断“需要多久恢复”和“最坏可能损失多久的数据”,但一次演练结果不代表任何故障都能在同样时间内恢复。
本地实测:数据库恢复了,附件为什么还打不开?
为了把这个区别实际跑一遍,我搭了一个最小验证应用。它只有两项业务操作:新建测试项目时生成一份文本附件,以及根据项目ID读取记录和附件内容。
测试在2026年8月31日完成,环境如下:
| 项目 | 本次实际条件 |
|---|---|
| 操作系统与运行时 | Windows,OpenJDK/JBR 21.0.7 |
| 应用与数据库 | Spring Boot 3.5.5,MySQL Community 8.4.8 |
| 数据规模 | 3条合成项目记录,3个文本附件,每个42字节,不含客户数据 |
| 隔离方式 | 两个独立MySQL进程、不同端口和数据目录,应用与数据库均只监听127.0.0.1 |
| 备份时点 | 停止测试应用写入后,导出数据库并复制对应附件 |
这里没有Vue页面、Nginx、HTTPS或鉴权功能,附件也是由测试接口生成的文本文件,并非完整的浏览器上传流程。这个应用只能用于本机验证,不能直接暴露到公网。
验证顺序及结果如下:
| 实际操作 | 观察到的结果 | 能说明什么 |
|---|---|---|
| 通过HTTP创建3条记录及对应附件 | 记录、文件均创建成功 | 准备好可核对的样本 |
| 结束应用进程,再启动同一个程序包 | 3条记录的字段、文件名及附件内容全部一致 | 本次应用进程重启没有丢失样本数据 |
| 停止应用,导出SQL并复制附件 | SQL文件为2407字节,附件快照包含3个文件 | 得到了同一停写窗口下的两份备份 |
| 只把SQL导入第二个MySQL实例,不恢复附件 | 表中有3条记录,但读取记录与附件的3次HTTP请求全部返回500 | 数据库导入成功,不代表业务读取已经恢复 |
| 补回附件备份,再启动连接恢复库的应用 | 3次读取全部成功,字段内容一致,3个文件的SHA-256均与原文件相同 | 本次小样本的记录与附件恢复通过核对 |

图4:仅恢复数据库时,3条记录存在但附件读取全部失败;补回附件后,记录内容和3个文件校验值均一致。本地合成数据测试,不代表完整上线验收。
其中最值得注意的是第四步。只检查SQL导入有没有报错,再执行下面的查询,会得到看起来正常的结果:
SELECT COUNT(*) FROM ai_project.project;
-- 本次恢复实例的实际结果:3
但数据库里的 attachment 字段保存的只是文件名,文件本体在独立目录。新实例虽然知道“应该读哪个文件”,磁盘上却没有这个文件,因此读取失败。
补回文件后,再用同一接口读一遍,并比较原文件与恢复文件的SHA-256,才把这次验证补完整。这里的500是最小测试应用未对缺失文件做业务化处理的结果,并不是建议正式系统这样处理;正式应用应提供适当的错误提示、日志和异常告警。
这次实测没有覆盖服务器重启、数据库崩溃、并发写入、版本升级、权限或大数据量恢复。小文件恢复得快,也不能据此推算生产恢复时间。它验证的是一个很具体的问题:备份里是否包含了业务读取实际依赖的数据,而不只是数据库里能查到的记录。
七、升级失败时,究竟回退什么
发布前保留旧版前端、JAR、配置版本和数据库迁移记录;新版本放进新的release目录,不在旧目录里混着覆盖。
单机可以采用维护窗口内短暂停机的发布方式:暂停写入,确认备份,执行已验证迁移,切换完整发布版本,再启动并验收。这不是零停机发布,也不覆盖多节点并发升级。
回退时至少要分三种情况:
- 只有程序变化,数据库仍兼容:可以按演练过的流程切回旧前后端版本及兼容配置,再复核业务。
- 数据库结构已经变化,但仍向后兼容:可以评估应用回退,但必须验证旧程序是否确实能使用当前结构。
- 删列、改语义或产生了不兼容数据:不能只切换旧JAR。需要选择前向修复或受控数据恢复,并评估上线后新增数据如何保留、补偿。
直接用旧备份覆盖当前生产库,可能丢失备份之后的新业务数据。因此,“备份恢复”不是一个可以随时无代价点击的撤销按钮。
八、留下一份下一位维护者能用的交付记录
部署结束后,比一张“服务启动成功”的截图更有价值的是下面这份记录:
- 发布版本、源码提交号、前后端构建方式和依赖版本。
- 服务器、域名、各组件位置及责任人;公开材料中不写秘密信息。
- 配置项名称、密钥存放机制和访问权限,不复制真实凭证。
- 启停、日志查看、数据库迁移及附件存储说明。
- 验收用例、预期、实测结果、时间和证据位置。
- 备份策略、恢复演练结果、回退条件和已知限制。
对于AI辅助生成的系统,最有用的交付也不是保存一大段聊天记录,而是让另一个人能够在新的环境中理解并重复这些步骤。
到这里,完成的是从“本地能运行”到“能部署、能检查、知道怎样恢复”的基础路径。是否适合正式业务,还要结合权限、流程、数据敏感程度、性能和持续运维进一步验收。
如果你已经部署过自己做的系统,第一次卡住的是环境、接口地址、数据库,还是更新后的数据和附件?具体故障往往比“部署难不难”更值得一起拆解。
参考资料
以下为对应技术点的官方文档,配置需按实际安装版本核对: