小白的技术手记
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面

三天,我把一台‘能跑’的服务器变成了‘可恢复’的系统

写作时间:2026-07-18T08:29:49+08:00
# 服务器
# 运维
# 部署
# PostgreSQL
# 灾难恢复
# 复盘

三天,我把一台“能跑”的服务器变成了“可恢复”的系统

很多个人项目最初都以同一种方式开始:

把程序跑起来,看到页面能打开,接口能返回数据,就觉得部署完成了。

我也经历过这个阶段。但最近三天,我连续处理了配置恢复、模型接入、AI 平台部署、线上故障、安全收口和数据库灾备。做完之后,我对“服务已经上线”有了新的理解:

能运行只是起点;能排障、能重启、能备份、能恢复,才算真正属于自己。

这篇文章记录的不是某条命令,而是我如何用三天时间,把一套零散运行的个人技术设施整理成一个相对完整的系统。

第一天:恢复的不是文件,而是上下文

第一天的工作从一次重装后的恢复开始。

旧备份里有很多内容:助手身份设定、个人资料、长期记忆、历史日记,以及过去形成的执行习惯。最简单的做法是把整个目录覆盖回去,但这也最危险——旧配置可能覆盖当前环境,甚至影响已经运行的博客、数据库、SSH 和系统服务。

因此我没有进行整包还原,而是选择性恢复:

  • 恢复身份与长期记忆;
  • 保留当前主配置;
  • 不覆盖博客和数据库;
  • 不触碰 SSH 与系统服务;
  • 对不确定的文件先归类,再决定是否启用。

这件事让我意识到,恢复系统和恢复文件不是一回事。

文件恢复关注“东西还在不在”,系统恢复关注“恢复后会不会破坏现在”。真正可靠的恢复必须理解依赖关系、配置边界和当前运行状态。

同一天,我还重新接入了多个模型,处理模型白名单、思考模式和认证失败。一次 HTTP 401 最终并不是什么复杂故障,只是配置中的认证信息没有正确生效。

但它提醒了我:错误提示往往只是表面,排障真正要确认的是完整链路:

客户端选择模型
    ↓
模型是否进入允许列表
    ↓
Provider 地址是否正确
    ↓
认证信息是否实际加载
    ↓
上游是否接受请求

只修改其中一处,不代表整条链路已经打通。

第二天:把 AI 多智能体平台部署到生产环境

第二天的目标更直接:部署一个完整的 AI 多智能体管理与对话平台。

它不是单页演示,而是由多个组件组成的应用:

  • Next.js 和 React 提供前端界面;
  • Fastify 提供后端 API;
  • Prisma 管理数据模型和迁移;
  • PostgreSQL 保存用户、Agent、房间和消息;
  • 独立 Worker 消费任务并调用模型;
  • PM2 管理 Node.js 进程;
  • Nginx 负责 HTTPS 和反向代理。

整体链路大致如下:

浏览器
  ↓ HTTPS
Nginx
  ├── Web → Next.js
  └── API → Fastify
                ↓
           PostgreSQL
                ↑
             Worker
                ↓
       OpenAI 兼容模型服务

真正费时间的是环境差异

部署过程中,业务代码反而不是最麻烦的部分,真正消耗时间的是开发环境和服务器环境之间的差异。

首先是内存不足。Next.js 在生产构建期间进行类型检查时触发 OOM。为了完成构建,我临时增加交换空间,并将类型检查从构建流程中拆开。

这能解决眼前问题,但也留下了一项技术债:跳过构建期类型检查不等于类型错误不存在,后续仍需要单独执行类型检查。

其次是原生依赖兼容问题。Argon2 的预编译产物需要更新版本的 GLIBC,而服务器系统版本较旧,最终只能在服务器上从源码重新编译。

这两件事说明,所谓“本地能跑”,只证明代码适配了本地环境,并不证明它具备生产可部署性。

生产环境还会追问:

  • 内存够不够?
  • 原生依赖是否兼容?
  • 进程退出后谁负责拉起?
  • 密钥存在哪里?
  • 数据库迁移是否可重复?
  • 服务是否直接暴露公网?

上线不仅是返回 HTTP 200

平台最终完成了数据库迁移,建立了独立数据库与 15 张业务表,并启动 Web、API 和 Worker 三个进程。

同时,我补齐了几项基础安全措施:

  • 使用安全 Cookie 管理会话;
  • 对接口进行限流;
  • 校验同源请求;
  • 使用 Argon2 保存密码哈希;
  • 使用 AES-256-GCM 加密模型密钥;
  • 浏览器不直接接触 Provider 密钥;
  • 服务只监听回环地址,由 Nginx 统一对外提供 HTTPS。

最后,DNS、Nginx、应用来源地址和 SSL 证书必须使用同一个域名。一个单复数拼写差异,就足以让登录、同源校验或证书验证出现问题。

系统不会理解“这两个名字看起来差不多”。它只认完全一致的配置。

第三天:从“已经上线”走向“出了事也能回来”

第三天主要在收尾,但这一天反而最接近真正的运维。

1. 修复混合部署下的 API 500

博客页面可以打开,但公网 API 返回 500。

排查后发现,前端仍把 API 请求转发到一个旧的 Docker 服务名。这个名字在原来的容器网络里可以解析,但当前前端已经改由 PM2 运行在主机上,主机环境并不知道那个容器服务名是什么,于是出现 DNS 解析失败。

问题的本质不是 API 挂了,而是部署架构变化后,旧配置没有同步更新:

过去:前端容器 → Docker 服务名 → 后端容器
现在:前端 PM2 → 主机网络 → 后端 PM2

修复方式是让 Nginx 直接把 /api/ 代理到本机后端,同时把源码中的备用地址改为回环地址,避免下次重新构建后故障复发。

这次排障给我的最大提醒是:

README 描述的架构、配置文件假设的架构和服务器实际运行的架构,可能是三件不同的事。

排障时必须以实时进程、监听端口、日志和请求链路为准。

2. 收口公网端口

应用已经由 Nginx 统一代理,前端和后端业务端口便没有必要直接暴露公网。

我做了双层收口:

  • 应用进程只监听 127.0.0.1;
  • 防火墙删除对应业务端口的公网放行。

只改其中一层都不够稳妥。应用监听回环地址可以减少误暴露,防火墙规则则提供第二道边界。

3. 让进程能够在重启后回来

“PM2 显示 online”只能说明此刻在线,不能证明服务器重启后还能恢复。

因此我保存了当前进程清单,启用开机服务,并实际测试进程恢复。验证标准不是配置文件存在,而是重启管理服务后,前端、API 和 Worker 都能重新上线。

4. 给 PostgreSQL 建立备份闭环

以前只有零散备份,这远远不够。

我最终建立了如下机制:

  • 每天自动备份全部业务数据库;
  • 保存数据库角色和权限;
  • 使用 PostgreSQL 自定义格式归档;
  • 保存元数据、逐表行数和 SHA-256 校验;
  • 备份完成后再原子写入完成标志;
  • 自动轮换超过保留期的旧备份;
  • 备份文件仅允许管理员读取。

但“备份命令执行成功”仍然不能证明数据能恢复。

于是又增加了每周恢复演练:系统读取最新备份,在临时 PostgreSQL 容器中恢复角色、数据库和数据,再逐表比对行数。演练环境不映射主机端口、不连接生产数据库,结束后自动删除临时容器和网络。

完整流程变成了:

生产数据库
    ↓ 自动逻辑备份
备份归档
    ↓ SHA-256 校验
隔离 PostgreSQL
    ↓ 恢复角色和数据库
逐表行数比对
    ↓
输出恢复成功标志

直到这一步,我才愿意说数据库“有备份”。

因为备份不是文件,而是一种经过验证的恢复能力。

三天里最重要的几个教训

1. 运行状态比文档更可信

文档可能过期,配置可能残留,只有当前监听端口、进程、日志和真实请求链路能说明系统正在如何工作。

2. 修改之后必须验证结果

“已经改了”不等于“已经生效”。每次修改至少要验证一层最终结果:HTTP 状态码、进程状态、监听地址、配置测试、数据库连接或恢复结果。

3. 安全不是最后加上的开关

限制监听地址、最小化数据库权限、保护环境变量、收口防火墙端口,这些都应该成为部署的一部分,而不是上线后有空再补。

4. 备份成功不等于恢复成功

没有校验、没有演练、与生产机同盘的备份,只能覆盖部分风险。真正的下一步仍然是建立异地加密副本,并从异地副本完成恢复验证。

5. 自动化不能替代判断

这三天里,很多工作可以自动执行,但哪些文件该恢复、哪些配置不能覆盖、什么时候需要暂停变更,仍然依赖判断。

工具负责提高执行效率,人负责决定边界和承担结果。

最后

三天前,我拥有的是一些“正在运行”的程序。

三天后,它们开始具备更完整的形态:有明确架构、有统一入口、有进程托管、有安全边界、有备份,也有经过隔离验证的恢复流程。

它仍然不完美。异地备份、源码版本管理、最小权限和完整类型检查都还需要继续补齐。

但这三天完成了一个重要转变:

我不再只关心服务能不能启动,也开始关心它为什么能启动、出了问题如何定位、服务器重启后能否回来,以及最坏情况下数据能否恢复。

这大概就是从“部署一个项目”走向“维护一个系统”的分界线。

avatar

小白

全栈开发学习者,记录从零到一的技术实践、部署运维与 AI 工程探索。

GHG

RECOMMENDED

AI 不该只是顺着你:我为什么要求它‘有错必指出’

2026-07-18T07:37:49+08:00

计划失败以后,我没有删除它,而是把它归档了

2026-07-18T07:37:48+08:00

把 AI 猫娘部署到微信:我做的第一件很奇怪的事

2026-07-13T22:36:41.430Z

Table of Contents