10-生产部署清单-从云服务器到稳定运行
系列:AI 伙伴(AI-Partner)——具身智能陪伴机器人 · 数据接口部署与二次开发篇(10/12)
一、先说结论:这套 Demo 距离生产差几步
AI 伙伴(AI-Partner)的后端是一个克制、清晰的工程,但它的默认配置是为"本地跑通"设计的。上生产前,心里要有这张差距账(均来自源码实证,如实列出):
| 现状(开发态默认) | 生产要求 | 风险 |
|---|---|---|
jpa.hibernate.ddl-auto=update | 改validate,DDL 受控执行 | 自动改表可能在生产造成意外结构变更 |
| 所有接口无鉴权拦截器 | 必须加鉴权 | 任何拿到地址的人都能读写全库 |
CORS 对/api/**全开 | 收紧白名单 | 任意网页可跨域调接口 |
| 数据库默认账号 root/root | 专用账号 + 强口令 | 弱口令等于送库 |
| actuator 只暴露 health,info | 按需保留,禁止公网直连 | 基线安全 |
| 密钥全走环境变量 | 保持,并升级为密钥管理 | 别写进代码库就行 |
这篇就按"从裸服务器到稳定运行"的顺序,把每一步讲透。
二、服务器与系统准备
- 机型:入门 2核4G 可跑通全套(后端 + MySQL + 视觉服务),视觉服务吃 CPU/NPU,预算紧可以把它放边缘设备。
- 系统:主流 Linux 发行版均可,选你熟的。开启防火墙时只放行 80/443,8080、3306、8000、1883 一律不对公网开放——数据库和 broker 只允许本机或内网访问。
- 时间同步:开启 NTP。系统提示词里会拼"当前时间",时区错了,提醒全乱。
三、JDK17 与 MySQL8 安装要点
- JDK:用发行版仓库或压缩包方式装 JDK 17,
java -version确认。别装 21 再抱怨 weird 兼容性问题——项目基线就是 17。 - MySQL 8:安装后立刻做三件事——改 root 强口令、建专用账号(只授
ai_partner库的权限)、确认字符集 utf8mb4。建库语句沿用项目文档:
CREATEDATABASEai_partnerDEFAULTCHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ci;导表sql/init.sql。注意该脚本无外键、无种子数据,表间关系靠 user_id/device_code 逻辑关联——备份和迁移时要把"逻辑一致性"当成自己的责任。
四、DDL 从 update 切到 validate
生产环境把spring.jpa.hibernate.ddl-auto改为validate:启动时只校验实体和表结构是否匹配,不自动改表。所有结构变更改走受控流程:
- 开发环境验证变更 → 2. 写成增量 SQL 脚本(含回滚语句)→ 3. 低峰期人工/流水线执行 → 4. 重启服务用 validate 校验。
这一步必须配代码外置配置:用application-prod.yml或启动参数--spring.jpa.hibernate.ddl-auto=validate覆盖,不要直接改仓库里的默认值然后忘记。
五、密钥与敏感配置的安全管理
原则一句话:密钥永远不进代码库。项目的约定本来就对——LLM_API_KEY、MYSQL_PASSWORD、DASHSCOPE_API_KEY等全部走环境变量。生产再加三道锁:
- 注入方式:systemd 的
EnvironmentFile(权限 600,属主运行账号)或密钥管理服务(各云厂商都有 KMS/凭据管理),不要把密钥写进启动脚本再提交 git。 - 最小权限:LLM key 与语音 key 分开,泄露时好排查、好吊销。
- 轮换演练:定期换 key 走一遍"旧 key 下线、新 key 生效"流程,确认没有密钥被硬编码在某个角落。
六、JVM 参数建议
以 4G 内存机器、给后端 2G 堆为例(示意):
java-Xms1g-Xmx2g\-Duser.timezone=Asia/Shanghai\-XX:+UseG1GC\-XX:MaxGCPauseMillis=200\-jarai-partner-backend.jar-Xms/-Xmx:初始与最大堆,设成一样可避免动态伸缩抖动;这台服务对话链路有 LLM HTTP 等待,堆不用贪大。-Duser.timezone=Asia/Shanghai:强烈建议。提醒调度器每分钟扫描remindTime,时区不对提醒就是灾难。- GC 用 G1 即可,这是 JDK17 的默认选择;没有极端延迟需求不必上花活。
七、systemd 托管服务
别再用nohup java -jar &了,挂了都没人拉。systemd 单元文件(示意):
# /etc/systemd/system/ai-partner.service [Unit] Description=AI Partner Backend After=network.target mysql.service [Service] User=aipartner WorkingDirectory=/opt/aipartner EnvironmentFile=/opt/aipartner/aipartner.env ExecStart=/usr/bin/java -Xms1g -Xmx2g -Duser.timezone=Asia/Shanghai -jar ai-partner-backend.jar Restart=always RestartSec=5 [Install] WantedBy=multi-user.targetsystemctl daemon-reload&&systemctlenable--nowai-partner journalctl-uai-partner-f# 看日志要点:专用低权用户运行、Restart=always自动拉起、After=mysql.service保证数据库先起。视觉服务同理再写一个 unit,uvicorn app:app --host 127.0.0.1 --port 8000——注意生产上视觉服务只监听本机,由后端内网调用。
八、Nginx 反向代理与 HTTPS
server { listen 443 ssl; server_name your.domain; # ssl_certificate / ssl_certificate_key 按云厂商证书配置 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 60s; # 对话要等 LLM,别设太短 } }要点:HTTPS 必须(语音、健康数据在裸 HTTP 上传输不可接受);proxy_read_timeout放宽到 60s 以上,因为/api/chat要同步等大模型返回;若未来引入长连接(语音流、WebSocket),需要额外配proxy_http_version 1.1、Upgrade/Connection头并拉长读写超时——现在项目的阻塞式对话暂时用不到,提前知道有这回事就行。
九、MQTT broker:EMQX 部署要点
- 端口 1883 只对内网开放;设备在公网的话,上 8883 + TLS。
- 开启鉴权:至少账号密码认证,进阶做一机一密 + ACL(限制每台设备只能发布/订阅自己的
device/{code}/...主题)。项目默认配置里 MQTT 账号密码为空,生产必须改。 - EMQX 管理台(18083)改默认口令或干脆不对公网开放。
十、日志、监控与告警
- 日志:后端日志级别已按包设置,journalctl 或落盘均可;建议加 logback 滚动策略,磁盘别被日志撑爆。
- 监控:actuator 目前只暴露 health 和 info,
/actuator/health适合接探活(云厂商健康检查、Uptime 机器人定时戳它)。它的局限也要清楚——health=UP 只代表进程活着,不代表 LLM key 有效、不代表 MQTT 通。更实质的探活建议:写个定时任务调/api/ping甚至发起一次测试对话,失败就告警(短信/企微/钉钉,任选)。 - 告警分级:服务挂 → 立刻告;LLM 连续 401 → 立刻告;单次视觉调用超时 → 允许降级,聚合后再告。注意项目当前告警工单(t_alert)只有落库、无短信/推送触达,外部通知通道要自己补。
十一、备份与恢复演练
- MySQL 定时备份:每晚
mysqldump --single-transaction ai_partner | gzip落盘并异地(对象存储/另一台机)同步,保留 7~30 天。 - 恢复演练:每季度找台空机,用备份文件完整恢复一次并起服务验证。没演练过的备份等于没有备份——备份成功 ≠ 能恢复,这是两条独立的命。
- 隐私数据的备份文件要加密存储:里面是老人的健康与情绪记录,属于敏感个人信息。
十二、升级发布与回滚
流程建议(示意):mvn clean package→ 新 jar 上传为ai-partner-backend-new.jar→ 停服务、备份旧 jar → 换名启动 →curl /api/ping验证 → 异常则换回旧 jar 秒级回滚。数据库变更与代码版本解耦:先加兼容性变更(新表、新列可空),后上代码;回滚代码不动数据库,避免结构来回横跳。条件允许就上双实例 + Nginx 切流,单实例时代至少保证 jar 包可秒换。
十三、上线前检查清单
| 档位 | 检查项 |
|---|---|
| 必须 | 关闭或收紧 CORS(白名单替代全开) |
| 必须 | 加接口鉴权(JWT/小程序登录态 + 管理端隔离),当前所有接口裸奔 |
| 必须 | 修改默认数据库密码,建专用最小权限账号 |
| 必须 | 配置LLM_API_KEY等全部密钥(环境变量/密钥服务,不进代码库) |
| 必须 | ddl-auto改 validate,DDL 受控 |
| 必须 | HTTPS 证书就位,8080/3306/8000/1883 不对公网开放 |
| 必须 | 数据库备份任务上线 + 完成一次恢复演练 |
| 必须 | 隐私合规:用户授权告知、健康/情绪数据最小化收集、医疗免责声明(AI 仅善意提醒,不构成诊断) |
| 建议 | systemd 托管 + 自动重启;JVM 时区与堆参数固化 |
| 建议 | MQTT 开鉴权与 TLS,EMQX 管理台改口令 |
| 建议 | 告警通道(服务探活、LLM 401 监控);日志滚动与留存策略 |
| 建议 | 发布/回滚脚本化;提醒下发失败重试(当前失败仅记日志) |
| 加分 | 接口限流;actuator 加独立端口与访问控制 |
| 加分 | 恢复演练常态化;密钥轮换机制;灰度发布 |
最后提醒一句:这个系统服务的是老人、孩子和独居者,稳定运行不只是运维指标,更是责任——你多做的每一项检查,都是某位长辈跌倒时那条告警能送出去的概率。上线清单逐项打勾,别图快。