两只 VPS 的一次大扫除——AI 运维的实践与教训

两台服务器,一个 AI 助手,三天的周末。这是一次集中运维的记录。

如果说前一篇讲的是一个容器怎么被我搞丢的,这一篇讲的是一个周末里两台机器上发生了什么——有教训,有成就,也有对「AI 运维」这个新模式的思考。


一、RSS 阅读器的复活记

先从最惨的说起。

家里的 RSS 阅读器(Tiny Tiny RSS)跑了好几年,底层的 PostgreSQL 还是 9.3——2013 年的版本。不是不想升,是「能用就别动」。这种心态,做运维的都懂。

AI 助手接手升级任务后,按部就班地执行:

docker exec postgres pg_dump -U ttrss ttrss > backup.sql

备份成功。然后:

docker-compose down -v

-v。删 volumes。

那时旧数据库本身已有系统表损坏——pg_attribute 断裂、pg_class 报错——但至少数据还在。-v 之后,连救的机会都没了。更讽刺的是,备份文件虽然生成了,但备份时机不对,导出的内容不完整。

第一课:备份成功不等于备份可用。以后凡备份必须验证。

重建:没有包袱反而更干净

数据既然丢了,剩下的事反而简单。新配置直接大步跨到 PostgreSQL 17-alpine:

services:
  database.postgres:
    image: postgres:17-alpine
    container_name: postgres
    restart: unless-stopped
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ttrss"]
      interval: 30s
      timeout: 10s
      retries: 5

顺带加了 Mercury Parser(全文提取)和 OpenCC(简繁转换)。从前的架子里没有这些——不是不想加,是一直觉得「以后再说」。

不过话说回来,一个 RSS 阅读器加简繁转换到底有什么用? 大概就是那种「我可以不用,但不能没有」的赛博囤积癖。说不定哪天订阅了一个用繁体字写技术文章的博客呢?

新服务加起来常驻 215MB 内存,比旧的还省。

磁盘占位符的智慧

在重建过程中还发现一个情况:VPS 硬盘较小,备份和数据库有时候会把磁盘撑满。解决方案是在根目录放一个 2GB 的占位文件 /placeholder

fallocate -l 2G /placeholder

备份脚本运行时先检查磁盘剩余空间。如果低于 3GB,自动删掉占位文件腾空间;如果腾完还不够,发告警通知。

这个方案很土,但很有效。像家里预留一把紧急钥匙——平时挂在那,关键时刻能救命。


二、另一台 VPS 上:自建 S3 对象存储

运维这件事有个规律:一个问题往往引出另一个问题。RSS 阅读器的灾难让我意识到备份体系太脆弱了——所有备份都躺在同一台机器的硬盘上。硬盘坏了呢?

解决方案是在第二台 VPS 上部署 S3 兼容的对象存储。选用的是 RustFS——一个用 Rust 写的轻量 MinIO 替代品,以约 40MB 内存跑起来了。

部署过程

docker-compose pull
docker-compose up -d
docker exec rustfs mc alias set local http://localhost:9000 backup SeekdoorBackup2024!
docker exec rustfs mc mb local/ttrss-backup
docker exec rustfs mc mb local/blog-backup

两个 bucket:

  • ttrss-backup — 数据库备份,保留 6 个版本
  • blog-backup — 博客文件备份,保留 30 天

备份脚本改造

旧的备份脚本用 SCP 把文件推到远程——简单粗暴,但不支持版本管理,没有过期清理。

新脚本用 mc cp 推到 S3:

# TTRSS: PostgreSQL 备份 + S3 同步
pg_dump -U ttrss ttrss -Z9 > ttrss_$(date +%Y%m%d).sql.gz
mc cp ttrss_*.sql.gz local/ttrss-backup/
mc rm --older-than 6d local/ttrss-backup/ --recursive

# Blog: SQLite 文件备份
cp /opt/typecho/data/typecho/usr/blog.db blog_$(date +%Y%m%d).db
mc cp blog_*.db local/blog-backup/
mc rm --older-than 30d local/blog-backup/ --recursive

每台机器的备份不再依赖对方存活。一台挂了,另一台上还有副本。


三、AI 运维的双刃剑

这次集中运维有一个贯穿始终的特点:所有操作都由 AI 助手(Hermes Agent)通过 SSH 完成

我只需要说「去把 TTRSS 升级了」「在这台机器上部署 S3 存储」「把备份脚本改一下」,Agent 就会自己分析环境、写配置文件、执行命令。

好的方面

速度。 写一个 docker-compose.yml 从构思到部署,AI 大概花 3 分钟。人类做同样的事——打开文档、回忆语法、上网查例子、调试——至少半小时。

减少心智负担。 我不需要记住 pg_dump 的每个参数、mc 的每个子命令。我只需要描述目标,AI 去翻文档找具体命令。

完整记录。 每一步都有日志输出,可以追溯。传统运维中「我当时怎么配的这个服务」的谜题消失了——问 AI 就行。

不好的方面

TTRSS 的事故就是缩影。AI 执行得太快——我说「升级」,它理解为「备份 → 删旧的 → 建新的」。中间没有一个人类会有的停顿:「备份真的有效吗?我再检查一下。」

这个模式让我想到自动驾驶的级别。现在的 AI 运维大概在 L2——能帮你加速,但你必须盯着方向盘。

写给其他用 AI 做运维的人

几条这三天换来的经验:

  1. 备份三步走:备份 → 验证 → 再删旧的。 中间缺一步就是事故。
  2. 容灾演练不是笑话。 这次丢数据后重建之顺利,正是因为 docker-compose.yml 在几天前刚重构过一次,所有配置写得清楚。如果写配置时偷了懒,重建时就要加倍还债。
  3. 给每台服务器配一个 S3 邻居。 互相备份比自己抱自己靠谱。
  4. 占位文件是土办法,但管用。 Disk full 是运维最不想见到的情况之一。提前留一个可以牺牲的文件,比等着报警再想办法从容得多。

四、微小的秩序

这两台 VPS 现在各自安好:

VPS AVPS B
用途博客 + RSS 阅读器 + 反向代理S3 对象存储 + 监控
内存1GB,已用 ~420MB较小,RustFS 仅占 40MB
数据库PostgreSQL 17 (SQLite 备选)
备份→ S3 (VPS B)→ 暂本地
新增服务TTRSS + Mercury + OpenCCRustFS (MinIO 兼容)
AI 踩坑1 次(数据丢失)0 次

那 2GB 的占位文件还安静地躺在根目录。希望它保持安静——它一旦消失,就意味着验证了我的备份策略。


五、凌晨三点的服务器

写下这些文字的时候,两台 VPS 上的 cron 可能正在执行凌晨的备份任务。PostgreSQL 17 在压缩导出,RustFS 在接收数据,Traefik 在自动续签名天的证书,占位文件依然占着它的位置。没有人盯着它们,所有的任务都安静地工作。

AI 做运维这件事,我现在的看法是:可以用,但要加护栏。 就像让新手司机开车——你要帮它装好自动刹车和车道保持。

而这个周末最有价值的产出,不是那套崭新的备份体系,也不是 RustFS 的部署——是我对自己运维能力的清醒认识:我依然会犯低级错误,但我有了一个执行效率高出我十倍的助手。关键是从今以后,每次让 AI 执行破坏性操作前,我都会多问一句:「备份验证过了吗?」


2026 年 7 月,于两台 VPS 之间。

全部操作由 Hermes Agent 辅助完成。