一个人运维五个项目的生存指南
风格:阮一峰(技术科普体)
一、引言
我用 AI 辅助开发了五个个人项目:番茄钟 App(iOS + Spring Boot 后端)、ClawChat(聊天机器人)、OpenClaw(AI agent 框架)、喝水提醒、以及一套数字资产知识体系。
过去一周(3/1–3/6),其中两个项目接连出了线上事故。修完之后,我把教训整理成了六条原则。这篇文章是对它们的系统性梳理。
如果你也是一个人在做 side project,尤其是有后端服务在跑的那种,希望这些经验对你有用。
二、发生了什么
先说事实。
事故一(3/5):ClawChat 的上游服务 OpenClaw 不可达,HTTP 请求直接超时。系统没有任何回退机制,给用户返回一个固定的“暂时不可用”文案。根因是 launchd 启动时 PATH 环境变量与手动启动不同,导致调用了错误版本的二进制文件。
事故二(3/6):番茄 App 的 token 过期,触发重新登录,App 在重登时清除了本地缓存——包括 6 条尚未同步到服务器的番茄记录。与此同时,后端也出了问题:一条慢 SQL 引发锁等待,Tomcat 的 120 个线程全部阻塞,客户端重试请求不断涌入,连接池耗尽,整个服务雪崩。
两次事故的表象不同,但根因相同:只设计了正常流程(happy path),没有处理失败流程。
三、原则一:失败路径的三条底线
失败路径不可能穷举——一条正常流程对应的异常分支是指数级的。但以下三条底线成本低、收益高,值得作为硬性要求。
1. 用户数据不能静默丢失
凡是涉及用户创建的数据,失败时至少做到三件事:
- 不删、不覆盖:未同步的数据是用户资产,不是临时缓存
- 给用户一个可见的提示:不允许静默失败
- 本地留一份兜底:关键数据有导出或恢复入口
反面案例:番茄 App 的 pendingSync 数据在重登时被清除,6 条记录永久丢失。
2. 上游变慢时不要把自己拖死
不需要完美的熔断体系,但至少做到:
- 设 timeout:不要用框架默认的无限等待
- 设 max inflight:不要让请求无限堆积
- 客户端重试要有退避:5xx 或超时期间不做高频平推
反面案例:1 核机器用 120 线程默认值,慢 SQL + 客户端重试直接雪崩。
3. 关键路径加一个 fallback
不需要完美的降级方案,只要“不是直接给用户一个死胡同”:
- HTTP 不可达 → 自动回退 CLI 调用
- 主数据源失败 → 展示本地缓存 + 状态标记
- 关键接口超时 → 返回降级响应而非空白
正面案例:ClawChat 修复后增加了 OPENCLAW_CLI_FALLBACK_ENABLED,HTTP 失败时自动回退 CLI。
四、原则二:小机器要主动校准参数
这是一个容易被忽略的盲区。框架的默认配置面向多核服务器设计,而大多数个人项目跑在 1 核 2GB 的小机器上。
以下是单核机器的关键参数速查表:
| 参数 | 框架默认值 | 单核建议值 |
|---|---|---|
| Web 线程池(max-threads) | 200 | 4–8 |
| DB 连接池(maximum-pool-size) | 10 | 3–5 |
| 请求超时(timeout) | 无限/30s | 10–30s |
| 等待队列(accept-count) | 100 | 16–32 |
| 连接泄漏检测(leak-detection) | 关闭 | 10–15s |
核心原则很简单:默认值不是安全值。 把运行参数从“默认没碰过”变成“我看过、我定的”,这件事十分钟就能做完,但能防住一次雪崩。
部署新服务时的检查项:
- 确认目标机器 CPU 核数和内存
- 按速查表设置线程池、连接池、超时参数
- 不使用框架默认值,显式写入配置文件
- 首次部署后观察 30 分钟 CPU 和内存水位
五、原则三:自愈优先于监控
一个人运维时,告警的价值是有限的。告警来了你可能在睡觉、开会、或者在另一个项目的深度工作里。你不可能 24 小时盯着。
所以正确的优先级是:先做自愈,再做监控。
| 场景 | 监控思路 | 自愈思路 |
|---|---|---|
| 进程挂了 | 发通知,等人来重启 | systemd/launchd 自动重启 |
| 请求堆积 | 发通知,等人来限流 | 内置 max inflight,自动拒绝 |
| 上游不可达 | 发通知,等人来排查 | 自动 fallback(如 CLI 回退) |
| 证书快过期 | 发通知,等人来续期 | certbot 自动续期 |
| 连接池满 | 发通知,等人来杀连接 | 连接超时 + 泄漏检测自动释放 |
给 AI 提运维需求时,建议默认先问:这个能不能做成自愈的? 而不是先问“怎么监控”。
此外,减少运维面积本身也很重要。最有效的运维优化不是“更好地监控”,而是“少一个要监控的东西”。每上线新功能前问自己:能不能不加新服务?能不能复用已有的?
六、原则四:数据备份要设计驱动,不要事故驱动
事故之后我手写 SQL 把丢失的 6 个番茄补录回去了。补录之所以能成功,是因为三个条件碰巧满足:我还记得做了什么、数据量很小、我会写 SQL。这三个条件任意一个不满足,数据就永久消失。
盘点下来,我的数据保护是事故驱动的——出过事的就备份了,没出过事的就裸奔。
| 数据 | 有备份 | 能恢复 |
|---|---|---|
| 番茄记录(阿里云 MySQL) | ✅ | ✅ |
| ClawChat 对话(腾讯云 MySQL) | ❌ | ❌ |
| digital-assets 文档 | ✅(Git) | ✅ |
| OpenClaw 配置(本机) | ❓ | ❓ |
空白格就是风险。
从“有备份”到真正安全,有三级台阶:
- 补齐空白:有 > 无。先把裸奔的数据备份起来
- 确认有效:能恢复 > 有备份。定期做恢复演练,确认备份不是坏的
- 自治运转:备份任务自身有健康检查,失败自动重试,无需人工干预
手动补录是最后手段,不应该是常规手段。
七、原则五:AI 说“完成了”需要分层验收
AI 给番茄后端写了一个 TimeConfig 修复,测试通过,AI 说“修复完成”。我信了,直接部署。结果发现改了一半。
问题不是 AI 能力不行,而是“修复完成”和“线上真的好了”之间缺少验收环节。
验收不是只能人来做。AI 和人应该各守不同层次:
| 层次 | 谁做 | 验什么 |
|---|---|---|
| L1 代码正确性 | AI | 类型检查、lint、单元测试 |
| L2 功能可用性 | AI | 冒烟测试:API 能响应、数据能存取 |
| L3 线上真实性 | AI | 部署后自动验证,确认修复在线上生效 |
| L4 体验判断 | 人 | 交互顺不顺、流程合不合理 |
当前的缺口在 L3。建议每个项目的部署流程最后一步,自动跑一组最小验证。例如:
- 番茄后端:调一次
/api/current-pomodoro,确认 200 且延迟 < 500ms - ClawChat:发一条测试消息,确认收到回复且状态 SUCCESS
以后让 AI 做修复时,在需求最后加一句:“修完之后,写一个部署后自动验证脚本,确认修复在线上真的生效。”
八、原则六:让知识自己生长
这一周我还做了一件事:审视自己的知识沉淀体系。
3 月 1 日我建立了每日自动生成知识卡片的机制。运行一周后发现三个问题:
- 输入源不完整:最有价值的学习发生在 OpenClaw 对话里(故障排查、修复决策),但卡片生成流程捕捉不到
- 只做单日提炼:同一类问题反复出现(如 launchd PATH 差异),但系统无法跨天识别模式
- 知识存了不消费:卡片存到目录后没有任何流程引用它
对应的改进是建立三级进化节奏:
| 级别 | 频率 | 产出 | 目标 |
|---|---|---|---|
| 微进化 | 每日 | 1 张知识卡片 | 捕捉当天最有价值的认知点 |
| 大进化 | 每周 | 原则性文档 | 跨天模式识别,提炼结构性认知 |
| 超进化 | 每月 | 知识体系重构 | 淘汰过时认知,合并散点为专题 |
微进化产出原材料(卡片),大进化产出结构(原则)。没有微进化,大进化没有素材;没有大进化,微进化只是日记。
本文就是一次“大进化”的产出——它不可能从任何单张卡片中生成。
九、检查清单
最后,如果你也在一个人运维个人项目,这里是一份可以直接用的检查清单。每个项目进入“运营期”(你开始依赖它的数据,开始给它写运维文档)时,花半天过一遍:
- 所有用户数据写入路径:失败时是否保留?是否有提示?
- 所有外部依赖调用:是否有 timeout?是否有 max inflight?
- 关键链路:是否有至少一个 fallback?
- 运行参数:是否按实际机器校准过(线程池、连接池、队列)?
- 数据备份:是否覆盖所有重要数据?是否做过恢复演练?
- 部署流程:是否有自动化的部署后验证?
- 进程管理:挂了能不能自动重启?
过完之后再补代码,优先级比加新功能高。
十、小结
AI 让独立开发者拥有了以前小团队才有的开发速度,但也同时制造了以前小团队才会遇到的运维问题。
开发是一次性的,运维是永久性的。每上线一个新服务,就是签了一份永久运维合同。
速度是好事,但速度之后要主动补上被跳过的东西。六条原则——失败路径底线、容量基线、自愈优先、数据安全、分层验收、知识进化——本质上都是同一件事:让你的系统有能力在你不在的时候照顾好自己。
(完)