欢迎光临
我们一直在努力

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

AI写的系统(Vibe Coding)在自己电脑上已经能运行了,下一步怎么让同事也能用?

这时最容易想到的是买台服务器,把代码传上去。但代码传完之后,问题才逐渐具体起来:前端为什么还在请求 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/aiproject/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辅助生成的系统,最有用的交付也不是保存一大段聊天记录,而是让另一个人能够在新的环境中理解并重复这些步骤。

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

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

    参考资料

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

  • Spring Boot:Externalized Configuration
  • Ubuntu手册:systemd.service
  • Nginx:proxy_pass
  • Nginx:Configuring HTTPS servers
  • MySQL 8.4:mysqldump
  • MySQL 8.4:Reloading SQL-Format Backups
  • 赞(0)
    未经允许不得转载:171主机测评 » AI写的系统怎么部署到服务器?以Spring Boot + Vue为例,走通部署与恢复检查
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址