post: four posts — rclone/Drive quota, DSM space reclamation, mail domain/PTR, embedded fonts (EN+ZH) + og/banner
Deploy / build (push) Successful in 32s
Deploy / build (push) Successful in 32s
- my-backup-was-using-someone-elses-api-quota (notes, 2026-10-15, pinned by its own evidence) - space-reclamation-wasnt-stuck-it-was-waiting (devops, 2026-10-14, pinned by its own samples) - adding-a-domain-to-someone-elses-mail-server (notes, 2025-05-06, backdated: no date/version pins) - embedding-fonts-so-a-pdf-looks-the-same-everywhere (engineering, 2025-01-22, backdated: no date/version pins) All four updatedDate 2026-10-16 so every lastmod stays honest; the two backdated posts carry no prose links (a backdated post may only link backwards). og measured rows=1/overflow=0, banners 8 rows/delta=2/overflow=0.
This commit is contained in:
1 parent
636e6f9624
commit
08fa7a9412
18 files changed
+1036
No files matched your search
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: "我的备份爬得比爬虫还慢,因为我在用别人的 API 配额"
|
||||
description: "1 TB 的 restic 种子传输卡在零字节。日志里点名的那个 Google 项目不是我的:那是 rclone 的共享 OAuth 客户端,配额和全世界每个 rclone 用户共池。"
|
||||
pubDate: 2026-10-15
|
||||
updatedDate: 2026-10-16
|
||||
category: notes
|
||||
tags: ["rclone", "backup", "google-drive", "oauth", "restic"]
|
||||
ogImage: /og/my-backup-was-using-someone-elses-api-quota.png
|
||||
banner: /banners/my-backup-was-using-someone-elses-api-quota.png
|
||||
draft: false
|
||||
---
|
||||
|
||||
## 订阅到期了,我得搬一个 TB
|
||||
|
||||
这件事以最不方便的方式找上门:承载我每晚备份的那个 OneDrive 账号已经没有订阅了,而手上还躺着一个 1.5 年没用的 Google 订阅。把备份搬过去、保住历史、别丢一晚。
|
||||
|
||||
第一件撞上的不是技术问题:**我记不清这套备份到底是谁在写**。rclone?Duplicati?还是群晖自带的什么?在一套 78 个 compose 栈、三台机器的环境里(就是[《一个人跑生产基础设施》](/posts/running-production-infrastructure-solo/)里那套),「我的备份是谁写的」本该是我张口就答的问题,而它当时不是。所以第一步是去读机器。
|
||||
|
||||
## 现场到底有什么
|
||||
|
||||
**两份 rclone 配置**,正是那种会咬人的细节:Backrest(restic 的前端)用的是 GUI 那份,而另外还有一份**陈旧的 CLI 副本**,被某个 cron 或某次人手 shell 用过。改了一份,等于只改了一半系统。
|
||||
|
||||
搬迁移本身在顺序对的时候很无聊,而我是先读后动的:
|
||||
|
||||
```text
|
||||
rclone.conf 追加 [gdrive-*] + [gdrive-*-crypt]
|
||||
rclone/rclone.conf(CLI 副本) 同样两个 remote
|
||||
Portainer stack 177 env → 新的 crypt remote;配置 :ro → :rw
|
||||
restic init 在 Google Drive 上建新仓库
|
||||
Backrest SetConfig dsm-repo 的 URI → 新位置
|
||||
```
|
||||
|
||||
在动计划 URI 之前,我先去源码里确认了一件一切依赖它的事:**restic 仓库的身份是 `guid = sha256(id)`** —— 一个由仓库 id 派生的稳定值,**不是**从 URL 推出来的。所以把计划指到新后端,不可能悄悄把历史分叉。六个计划事后逐字节与原版一致,仓库的 `id`/`guid` 未被动过。
|
||||
|
||||
## 然后种子停住了,而日志里点了一个陌生人的名字
|
||||
|
||||
新仓库灌到 1075 GB 里的大约 2.88 GB —— 0.27% —— 然后慢成爬行。实时日志里有答案:
|
||||
|
||||
```text
|
||||
'Read requests per minute' ... reason: RATE_LIMIT_EXCEEDED
|
||||
consumer: projects/202264815644 <- 不是我的项目
|
||||
quota_limit_value: 840000
|
||||
```
|
||||
|
||||
`202264815644` 是 **rclone 自己的共享 OAuth 项目**。我那个 TB 级种子吃的是每分钟读取配额,和地球上每一个 rclone 用户共池——这就是为什么它从约 750 KB/s 掉到零,而我的 NAS、我的磁盘、我的线路全都闲着。等我去量的时候,种子已经完全停摆:**90 秒内 0 字节**、159 个错误、依旧 `rateLimitExceeded`;而六个夜间计划也全撞同一堵墙:
|
||||
|
||||
```text
|
||||
Fatal: unable to save snapshot: unexpected HTTP response (500)
|
||||
googleapi: Error 403: Quota exceeded for quota metric 'Queries'
|
||||
and limit 'Requests per minute' ...
|
||||
```
|
||||
|
||||
在进度条里,「慢」和「被挡住」看起来一样,而它们是两个完全不同的问题。**采样 90 秒得到的零**,才是让我做出这个判断的东西。
|
||||
|
||||
## 你必须有自己的 OAuth 客户端,而控制台绕不过去
|
||||
|
||||
唯一的真修法只有一个:别再用共享客户端,用你自己的。也就是一个 Google Cloud 项目、一个 OAuth 同意屏、一个凭据。两个设置花掉了真正的时间:
|
||||
|
||||
- **Application type 必须选 Desktop app。** rclone 的回调是 `http://127.0.0.1:53682`,只有这个类型能不用额外配置就允许它。
|
||||
- **要点「Publish app」。** 留在 Testing 状态,Google 会**每 7 天**让刷新令牌过期一次——那就把一个「能跑」的系统变成了每周一次的静默故障。
|
||||
|
||||
我查过能不能用机器上已经认证好的 CLI 抄近路。不能,而原因值得记下来:这里的 `gcloud` 只以一个**别人的项目里的服务账号**登录,而服务账号**无法**创建同意屏或 OAuth 客户端 ID。凭据必须属于一个真人拥有的项目。这是一条死路——我是验证出来的,不是猜的。
|
||||
|
||||
## 授权流程里的三个坑
|
||||
|
||||
1. **未经验证就发布的应用,可能会硬性堵死受限 scope。** `.../auth/drive` 是受限 scope,一个**已发布但未完成 Google 审核**的应用有时会被直接拒绝且**没有旁路**。我这一次旁路是存在的——「Google hasn't verified this app」页面,然后 **Advanced → Go to rclone-backup (unsafe)**——但那取决于 scope 与审核状态,是一枚硬币,而不是保证;而「unsafe」只是个标签,意思是从没走过付费审核。
|
||||
2. **回调只接受一次请求。** 刷新页面,或者开第二个标签,都会在**没有 code** 的情况下打到 `127.0.0.1:53682`,把这次尝试弄死。我第一次就是这么死的(`no code returned`),而它看起来像凭据错了。**一个标签,别刷新。**
|
||||
3. **同意屏必须在跑 rclone 的那台机器的浏览器里完成。** 回调指向 `localhost`,所以手机或另一台电脑完不成——浏览器必须和等待那个端口的进程在同一台主机上。
|
||||
|
||||
之后就简单了:把 token 读出来,把 `client_id` + `client_secret` + `token` 写进**两份**配置(GUI 那份和陈旧的 CLI 副本),并恢复两个对长时无人值守上传很关键的开关:`use_trash = false` 与 `stop_on_upload_limit = true`。
|
||||
|
||||
## 旧的那一半:靠读,不靠问
|
||||
|
||||
清空旧 remote 时报了错——`itemNotFound`——而空间照样回来了:
|
||||
|
||||
```text
|
||||
Used: 1.012 TiB -> 3.800 GiB
|
||||
Free: ~0 -> 1.007 TiB
|
||||
DSM 文件夹: 在 -> 不在 回收站: 0 B
|
||||
```
|
||||
|
||||
那个错误是 rclone 在**干完活之后**发的抱怨。另外值得记一笔:工具的文档说个人账号无法 `permanentDelete`,按它你就得手动清回收站——实际是回收站已经是空的,约 1 TB 就这么回来了,不需要任何浏览器步骤。文档描述的是 API,而 API 不总是你拿到的东西。
|
||||
|
||||
还有一条量出来的事,因为它改变了我下一个决定:把那台机器上共享的同步容器暂停之后,负载均值直接减半——16.70 → 8.89。这就是「网络慢」和「机器忙」的区别。
|
||||
|
||||
## 如果重来一次
|
||||
|
||||
1. **从第一天起就用自己的 OAuth 客户端。** 共享客户端 ID 是一条你排在里面、却看不到进度、也找不到人负责的队伍。
|
||||
2. **「我的备份是谁写的」要写在文档里,不靠记忆。** 两份 rclone 配置,对一个一年没人画过图的系统来说是很正常的漂移量。
|
||||
3. **读目的地,别读退出码。** 那次清空「失败」了,却释放了一个 TB;而一次看起来成功的改名,可能什么都没落地。跟[《它报告成功,其实什么都没变》](/posts/it-reported-success-nothing-had-changed/)是同一课——这套环境一直在教这一课。
|
||||
4. **不要在注意力只剩最后百分之几的时候启动一个要跑几天的上传。** 在 0.27% 时发现 `RATE_LIMIT_EXCEEDED` 只花几分钟;跑了两天才发现,花掉的是整个窗口。
|
||||
|
||||
## 结果,诚实地说
|
||||
|
||||
管道是通的:计划现在住在 Google Drive 上、仓库身份没变、种子花的是**我的**配额而不是共享池、旧的那一侧退回了大约 1 TB。但它现在**还不是一个备份**:写这篇的时候,第一轮种子才上传了约 1075 GB 里的 2.88 GB,而且新仓库里**快照数为零**——在第一张快照落地之前,什么都还原不了。那是我盯着的数字,因为「传输在跑」和「你能还原」是两句不同的话。
|
||||
Reference in new issue
Block a user