返回洞察
AI创新

AI写的系统怎么部署到服务器?以Spring Boot + Vue为例,走通部署与恢复检查

系统在自己电脑上已经能运行了,下一步怎么让同事也能用? 这时最容易想到的是买台服务器,把代码传上去。但代码传完之后,问题才逐渐具体起来:前端为什么还在请求 localhost?后端重启后上传的附件还在不在?数据库能不能直接开放端口?下次更新失败,又该退回哪里? 上一篇讨论了AI写的系统适合承担什么业

广州数友科技2026/9/1

AI写的系统怎么部署到服务器?以Spring Boot + Vue为例,走通部署与恢复检查

系统在自己电脑上已经能运行了,下一步怎么让同事也能用?

这时最容易想到的是买台服务器,把代码传上去。但代码传完之后,问题才逐渐具体起来:前端为什么还在请求 localhost?后端重启后上传的附件还在不在?数据库能不能直接开放端口?下次更新失败,又该退回哪里?

上一篇讨论了AI写的系统适合承担什么业务责任。这篇往下走一步,用一个项目管理工具的示例,把服务器部署、上线检查和恢复验证串起来。

本文面向已经做出原型、能看懂基本配置的业务负责人和产品经理,也适合负责接手原型的开发人员。

范围说明:本文包含Ubuntu教学部署方案,以及一次Windows本地隔离恢复实测,不是客户项目实测报告。第六节记录了小样本验证结果;Ubuntu、Nginx、HTTPS及完整业务验收尚未实测。命令、路径和接口名均需按实际项目调整。部署成功也不等于业务、安全和性能已经验收。

目录

  1. [先确认系统由哪些部分组成](#一先确认系统由哪些部分组成)
  2. [把代码、配置和业务文件分开](#二把代码配置和业务文件分开)
  3. [先让后端成为一个可管理的服务](#三先让后端成为一个可管理的服务)
  4. [让前端和接口通过同一个入口访问](#四让前端和接口通过同一个入口访问)
  5. [按层验收不要只看首页](#五按层验收不要只看首页)
  6. [做一次隔离恢复检查](#六做一次隔离恢复检查)
  • [本地实测:数据库恢复了,附件为什么还打不开?](#本地实测数据库恢复了附件为什么还打不开)
  1. [升级失败时究竟回退什么](#七升级失败时究竟回退什么)
  2. [留下一份下一位维护者能用的交付记录](#八留下一份下一位维护者能用的交付记录)

一、先确认系统由哪些部分组成

本文选择一个明确的示例范围:

部分本文假设上线前需要核对
服务器Ubuntu 24.04,使用systemd已安装运行环境,有授权的管理入口
前端Vue静态构建产物 dist/不是SSR应用,不依赖开发服务器
后端与Java 17兼容的Spring Boot可执行JARJDK、依赖和实际项目匹配;不是所有项目都能直接用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_USERDB_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格式备份恢复

验证至少包含三件事:

  1. 比较关键表数量、抽样业务记录和关联关系;在线备份应与对应快照口径比较,而非和持续变化的实时库硬比。
  2. 用兼容版本应用连接恢复库,在隔离环境走一次核心业务。
  3. 从对应附件备份恢复测试文件,检查记录里的文件引用是否仍然可用。

数据库和附件分开备份时,还需要约定一致性窗口,例如维护窗口内暂停写入,或设计可关联的快照机制。否则可能恢复了附件记录,却找不到对应文件。

记下恢复耗时和备份时间点。它们分别帮助判断“需要多久恢复”和“最坏可能损失多久的数据”,但一次演练结果不代表任何故障都能在同样时间内恢复。

本地实测:数据库恢复了,附件为什么还打不开?

为了把这个区别实际跑一遍,我搭了一个最小验证应用。它只有两项业务操作:新建测试项目时生成一份文本附件,以及根据项目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辅助生成的系统,最有用的交付也不是保存一大段聊天记录,而是让另一个人能够在新的环境中理解并重复这些步骤。

到这里,完成的是从“本地能运行”到“能部署、能检查、知道怎样恢复”的基础路径。是否适合正式业务,还要结合权限、流程、数据敏感程度、性能和持续运维进一步验收。

如果你已经部署过自己做的系统,第一次卡住的是环境、接口地址、数据库,还是更新后的数据和附件?具体故障往往比“部署难不难”更值得一起拆解。

参考资料

以下为对应技术点的官方文档,配置需按实际安装版本核对:

  1. Spring Boot:Externalized Configuration
  2. Ubuntu手册:systemd.service
  3. Nginx:proxy_pass
  4. Nginx:Configuring HTTPS servers
  5. MySQL 8.4:mysqldump
  6. MySQL 8.4:Reloading SQL-Format Backups