2024-04-24
经验总结
00
请注意,本文编写于 210 天前,最后修改于 207 天前,其中某些信息可能已经过时。

目录

探索路程
腾讯云 COS 国内
腾讯云 COS 国外
本地部署 MinIO
Wasabi
Cloudflare R2
最终方案
教程
Cloudflare R2 + Rclone
Picgo 客户端配置
Picgo Plugin Rclone
Picgo Plugin S3
坑点
Rclone Copy / Sync S3 储存源的时候默认不检查 Bucket 是否存在,而是始终带一个 CreateBucket 指令,在 Token 权限不够时导致 HTTP 403
PicgoCore 的配置项是 Picgo 图形客户端的配置项的子集
Vanblog 开启图像压缩后提供给 PicgoCore 的文件名自带 md5 前缀

这通折腾的起因是想给博客配一个更加合适的图床,下面是我对结果的希望:

  1. 价格优惠,最好有一定免费额度,计费项、付费方式要阳间:毕竟是个人博客用的图床
  2. 速度较快,尤其是在中国大陆地区:考虑访问者主要地区
  3. 不想备案:备案太麻烦了,而且要牵扯上一整个域名

探索路程

下面是目前(2023 年以后)做过的一些探索,如果更希望看目前最优方案请跳过。

在 2023 年以前,曾经尝试过又拍云对象储存、阿里云 OSS、SMMS、路过图床。前两者因为备案问题而被我抛弃,后面两者生怕服务不稳定、跑路而被我抛弃。

腾讯云 COS 国内

虽然仍然有备案的问题,但这段时间我薅到了朋友的备案域名的子域,所以稍长地使用了腾讯云 COS。
COS 的计费项是挺少的,但和阿里云 OSS 一样,储存桶的公网访问流量费用超级贵,也没办法做限流什么的,估计很容易被刷爆钱包——即使我不拉仇恨、小站没人访问。
这就带来了最大的一个问题,想用必须要套个腾讯云 CDN:

  1. 配置麻烦:腾讯云 CDN 的配置项相比 COS 多太多了
  2. 计费项多:腾讯云 CDN 的计费项太多了
  3. 费时费力:在配置过程中有很多需要等待的环节(DNS 解析生效 / SSL 证书申请 / CDN 配置部署 / CDN 缓存刷新)。如果某个环节出了问题,就更糟心了。更糟糕的是,不能一劳永逸,对于每一个桶都需要重新配置一次,只能刷熟练度,累死了都。

好处是挺多,最大的就是访问速度极快。可对于个人博客,好像没有追求极快加载速度的刚需。相比于折腾,我宁愿抛弃一些所谓“好处”。

腾讯云 COS 国外

鉴于腾讯云也加入了 Cloudflare 带宽联盟,简单来说就是腾讯云 COS 部分地区(国外)套用 Cloudflare CDN 可以免除流量费用。
一打眼是挺好的,但有一句话是“套了 CF 众生平等”,我都要套 CF CDN 了,我干嘛不去用其他对象储存呢?
再说,腾讯云 COS 的国外地区单单储存费用就不低,还基本不能用储存容量资源包来抵扣,也不太实惠啊。

本地部署 MinIO

MinIO 确实是一个好项目,无论是生态的完整性还是本身的性能,都值得称赞。部署上也没有很多的槽点,Docker 镜像一拉就好了。
但让我犹豫的是以下几点:

  1. 安全性和运维难度不好权衡。单点部署无法保证服务的可用性,也很容易导致“数据火葬场”。倘若使用集群部署,那运维难度直线上升,或许要需要套 K8s 之类更更更复杂的东西才能用着舒服,学习成本太高昂了。
  2. 服务器成本本身就不低。搭建 MinIO 的服务器(集群)要有较大的储存空间、较大的上传带宽、不错的线路——别说三条同时满足了,满足一条就可以疯狂爆米了。 于是还没开始大规模部署,我就抛弃这个方案了,除非我已经搭建好了一个 K8s 集群,并且各个节点基本满足上面的条件。

Wasabi

这家很早就以“$5/TB/mo” “No Fees for Egress”著名。但不好的是,付款方式没那么阳间,出口流量虽然不收费,但的确是有限制的,每月出口量大抵和总储存量相当。
也就是说,存储大量低频访问的文件时,才能算得上实惠。
近几年各家也推出了类似价格 / 更低价格的对象储存方案,好像也没啥必要选择这家厂的产品了。

Cloudflare R2

这就是目前使用的方案,完全符合最开始提到的期望。
下面是一些零散的补充:

  1. 感觉 R2 套了 CF CDN 后的访问速度十分快,甚至快到让你怀疑人生
  2. CF 可以绑定银联卡信用卡 / Paypal 银联卡
  3. 10 GB 的免费额度

最终方案

Cloudflare R2 作为 S3 对象储存的提供方;在 PC 上使用 Picgo 来上传图片,使用 Rclone 来同步文件、管理云端文件;在 Vanblog 上使用 PicgoCore 来上传、管理图片。

Tip:PicgoCore 是 Picgo 的命令行版本,共用一套插件体系和大部分配置项

教程

Cloudflare R2 + Rclone

教程:rclone · Cloudflare R2 docs

十分建议仔细阅读上面的教程,并且注意下后面提到的坑点。

Picgo 客户端配置

Picgo Plugin Rclone

这个插件的配置项的提示较少,没那么容易配置。但主要难点还是在配置 Rclone 上面。
放张图欣赏一下(可以参考一下我的填法): Picgo Plugin Rclone 界面.png

反正我刚看到这界面是有点懵的,迷迷糊糊填完后发现不能跑,一直怀疑是自己填错了,而不是出了什么其他问题。
实际上是下一个坑点所致。

Picgo Plugin S3

其实和配置 Rclone 大差不差,只是有一个需要注意的点:文件路径指的是文件的绝对路径,而不是文件所在的目录。
比如:上面的图片中“上传路径”填写的是 {year}{month}/,在这个插件中的“”填写的是{year}{month}/{md5}.{fileName}.{extName}

坑点

Rclone Copy / Sync S3 储存源的时候默认不检查 Bucket 是否存在,而是始终带一个 CreateBucket 指令,在 Token 权限不够时导致 HTTP 403

放张图看看 CF 新建 R2 API Token 的时候允许的权限,我按直觉选了Object Read & Write,显然没有 CreateBucket 权限。

Cloudflare R2 API 权限列表.png

直接得到了类似如下报错(随便找个文件复现的):

plain
C:\Users\cornw\Pictures>rclone sync -P Background.png CornWorldR2:static 2023-10-12 01:14:33 ERROR : Background.png: Failed to copy: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:14:33 ERROR : Attempt 1/3 failed with 1 errors and: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:14:36 ERROR : Background.png: Failed to copy: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:14:36 ERROR : Attempt 2/3 failed with 1 errors and: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:14:38 ERROR : Background.png: Failed to copy: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:14:38 ERROR : Attempt 3/3 failed with 1 errors and: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: Transferred: 0 B / 0 B, -, 0 B/s, ETA - Errors: 1 (retrying may help) Elapsed time: 6.4s 2023/10/12 01:14:38 Failed to sync: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id:

根本看不懂是啥问题啊 orz,摸索半天总算是开开了调试信息输出,得到了这样的报错:

plain
C:\Users\cornw\Pictures>rclone sync -P Background.png CornWorldR2:static -vv --dump requests --retries 1 2023/10/12 01:09:45 DEBUG : rclone: Version "v1.64.0" starting with parameters ["rclone" "sync" "-P" "Background.png" "CornWorldR2:static" "-vv" "--dump" "requests" "--retries" "1"] ...... 2023-10-12 01:09:45 DEBUG : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2023-10-12 01:09:45 DEBUG : HTTP REQUEST (req 0xc000962700) 2023-10-12 01:09:45 DEBUG : HEAD /static/Background.png HTTP/1.1 Host: <account_id>.r2.cloudflarestorage.com User-Agent: rclone/v1.64.0 Authorization: XXXX X-Amz-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 X-Amz-Date: 20231011T170945Z 2023-10-12 01:09:45 DEBUG : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2023-10-12 01:09:47 DEBUG : <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 2023-10-12 01:09:47 DEBUG : HTTP RESPONSE (req 0xc000962700) 2023-10-12 01:09:47 DEBUG : HTTP/1.1 404 Not Found Connection: close Cf-Ray: 8148b6734e547bbc-LAX Connection: keep-alive Content-Type: text/plain;charset=UTF-8 Date: Wed, 11 Oct 2023 17:09:46 GMT Server: cloudflare 2023-10-12 01:09:47 DEBUG : <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 2023-10-12 01:09:47 DEBUG : Background.png: Need to transfer - File not found at Destination 2023-10-12 01:09:47 DEBUG : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2023-10-12 01:09:47 DEBUG : HTTP REQUEST (req 0xc000962b00) 2023-10-12 01:09:47 DEBUG : PUT /static HTTP/1.1 Host: <account_id>.r2.cloudflarestorage.com User-Agent: rclone/v1.64.0 Content-Length: 148 Authorization: XXXX X-Amz-Content-Sha256: 5c78745b2e361d17109544a85201b4a8be4fd50d6e777f81ffaca6fd5f60024e X-Amz-Date: 20231011T170947Z Accept-Encoding: gzip <CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LocationConstraint>apac</LocationConstraint></CreateBucketConfiguration> 2023-10-12 01:09:47 DEBUG : >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 2023-10-12 01:09:48 DEBUG : <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 2023-10-12 01:09:48 DEBUG : HTTP RESPONSE (req 0xc000962b00) 2023-10-12 01:09:48 DEBUG : HTTP/1.1 403 Forbidden Transfer-Encoding: chunked Cf-Ray: 8148b677ca8a7bbc-LAX Connection: keep-alive Content-Type: application/xml Date: Wed, 11 Oct 2023 17:09:46 GMT Server: cloudflare Vary: Accept-Encoding 2023-10-12 01:09:48 DEBUG : <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 2023-10-12 01:09:48 ERROR : Background.png: Failed to copy: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: 2023-10-12 01:09:48 ERROR : Attempt 1/1 failed with 1 errors and: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id: Transferred: 0 B / 0 B, -, 0 B/s, ETA - Errors: 1 (retrying may help) Elapsed time: 2.6s 2023/10/12 01:09:48 INFO : Transferred: 0 B / 0 B, -, 0 B/s, ETA - Errors: 1 (retrying may help) Elapsed time: 2.6s 2023/10/12 01:09:48 DEBUG : 5 go routines active 2023/10/12 01:09:48 Failed to sync: failed to prepare upload: AccessDenied: Access Denied status code: 403, request id: , host id:

注意其中某个请求有这样的东西:

plain
<CreateBucketConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"><LocationConstraint>apac</LocationConstraint></CreateBucketConfiguration>

我看到就明白了,如题,Token 的权限不足了。
于是找到了解决办法:添加 --s3-no-check-bucket 参数。 下面是正常上传时候的输出:

C:\Users\cornw\Pictures>rclone sync -P Background.png CornWorldR2:static --s3-no-check-bucket Transferred: 476 KiB / 12.512 MiB, 4%, 95.090 KiB/s, ETA 2m9s Transferred: 0 / 1, 0% Elapsed time: 6.5s Transferring: * Background.png: 3% /12.512Mi, 95.099Ki/s, 2m9s

显然这是一处设计失误——Bucket 不存在的可能性极小。
也就是说,完全可以默认 Bucket 存在,反而添加一个用于检查 Bucket 是否存在的参数 --s3-check-bucket(?) / 用于始终发送创建 Bucket 的参数 --s3-always-create-bucket(?),而不是 --s3-no-check-bucket 参数。

心累,好在可以解决了。

目前已经对picgo-plugin-rclone项目提起相关 issue(rclone 在 s3 源的判断上有缺陷,望适配 · Issue #8 · yabostone/picgo-plugin-rclone (github.com)) 以期解决此问题。
在上述 issue 中也有提及暂时的解决方案,修改picgo-plugin-rclone npm 包中 /dist/index.js 关于生成 rclone sync 指令的代码,加入 --s3-no-check-bucket 参数。

参考自:AccessDenied on copy to S3 bucket (due to calling CreateBucketConfiguration on existing bucket) · Issue #5119 · rclone/rclone (github.com)

PicgoCore 的配置项是 Picgo 图形客户端的配置项的子集

自己删减有点累,于是写了个小网页,用于提取 Picgo 图形客户端的配置项中适用于 PicgoCore 的部分。 Picgo 客户端配置文件转 Picgo-Core 配置文件 (corn.li)

Vanblog 开启图像压缩后提供给 PicgoCore 的文件名自带 md5 前缀

本来我是想这么命名文件的:{sha1}.{fileName}.{extName},但发现实际存进去的文件是这样的:{sha1}.{md5}.{fileName}.{extName}
多次尝试后发现是 Vanblog 的机制。
那就接受吧~

本文作者:CornWorld

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!