对象存储的临时故障不应该让整个 API 进程无法启动。本文记录一次 Go 服务启动时检查 S3 Bucket 收到 400 Bad Request、容器随后退出的真实排查过程,并给出一套适用于 S3 兼容存储的启动策略。
问题现象
开发环境使用 Docker Compose 启动视频 API。数据库、Redis 和队列都已经初始化成功,但 API 在构建服务依赖时退出:
1 | panic: checking private object storage: checking S3 bucket: 400 Bad Request |
表面上看像是 MinIO 没启动。实际上 Compose 中没有 MinIO 服务,应用使用的是管理员在数据库中配置的 Cloudflare R2。
排查过程
先确认“MinIO”到底是什么
代码的 S3 实现使用了 github.com/minio/minio-go/v7。这里的 MinIO 是 Go 客户端 SDK,不等于系统正在运行 MinIO 服务。这个 SDK 同样可以访问 Cloudflare R2、阿里云 OSS、腾讯 COS 和其他 S3 兼容服务。
可以先检查服务编排和依赖:
1 | rg -n "minio|MINIO|S3_ENDPOINT" compose.yaml server |
如果只找到 minio-go 依赖,却找不到 MinIO 容器,排查方向就应该转向数据库里的对象存储配置,而不是给 Compose 临时加一个 MinIO。
找到真正的网络请求
启动调用链是:
1 | BuildVideoServer |
原来的 buildRuntimeStorage 在创建客户端之后立刻执行 EnsureBucket。任何远端 4xx、网络超时或凭据错误都会返回到 BuildVideoServer,而 BuildVideoServer 对错误直接 panic。
数据库中保存的是一条启用的 cloudflare_r2 配置。检查时只读取非敏感字段即可:
1 | SELECT enabled, provider, endpoint, account_id, region, bucket, path_prefix |
这次配置的 Endpoint 和 Account ID 也不一致。R2 官方 Endpoint 通常包含账户 ID;两个字段不匹配时,即使网络可达,也不能说明签名请求一定正确。
根因
直接原因不是“MinIO 容器缺失”,而是启动阶段把远端 Bucket 连通性当成了 API 的硬依赖:
1 | store, err := storageinfra.NewS3Store(options) |
NewS3Store 只负责校验 Endpoint、凭据格式并创建客户端;真正的网络探测发生在 EnsureBucket。把两者放在同一个启动路径里,会产生一个很难恢复的锁定问题:对象存储配置越坏,管理员越无法进入后台修正它。
解决方案
启动只加载配置,不探测远端
启动时保留本地配置校验和 S3 客户端创建,但删除 EnsureBucket:
1 | store, err := storageinfra.NewS3Store(options) |
这样做并不是把错误吞掉。后续真正的上传、下载和删除仍然会返回对象存储错误;只是暂时故障不会阻止 API 提供管理页面和健康检查。
把严格探测放进“测试配置”
管理后台的测试接口使用独立的短超时,依次执行:
- 检查 Bucket 是否存在;
- 写入一个随机测试对象;
- 读取并校验对象内容;
- 删除测试对象。
这比只执行一次 HEAD Bucket 更接近资产真实使用所需的权限。测试失败时,错误会回到管理员页面,而不是让 API 进程退出。
校验 R2 的两个账户字段
当同时填写官方 R2 Endpoint 和 Account ID 时,后台现在会检查二者是否属于同一账户。Endpoint 可以留空并由 Account ID 自动生成,也可以填写完整 Endpoint,但不能保存互相矛盾的组合。
验证与回归
新增了一个回归测试,使用不可达的测试 Endpoint 构建云存储配置,确认启动阶段不会发起网络探测:
1 | cd server |
本次修复还实际运行了整仓验证:
1 | make test |
结果是 Go 与前端测试全部通过,静态检查和构建通过,健康检查返回数据库、Redis、队列均为 healthy。API 日志显示仍选择已配置的 cloudflare_r2,没有悄悄切换到本地存储。
常见误区与边界
minio-go是 SDK,不代表必须部署 MinIO。是否部署 MinIO 取决于 Endpoint,而不是 Go 包名。- 启动不探测远端并不等于对象存储可用。发布前仍应点击后台的“测试配置”。
- 未配置、禁用或选择
local时才使用本机存储;已启用的云存储配置错误时不会静默回退,否则会造成资产散落在两个后端。 - R2 的 path-style、Region、Endpoint 和密钥必须按供应商要求配置。测试按钮通过后,再重启 API 与 Worker 让新配置生效。
检查清单
- Compose 中是否真的有 MinIO 服务,还是只引入了 MinIO 的 S3 SDK?
-
video_storage_setting是否启用,Provider、Endpoint、Account ID、Bucket 是否对应? - 启动流程是否只做本地配置校验,没有强制
BucketExists? - 是否提供了独立的 Bucket + 写入 + 读取 + 删除测试?
- 保存云存储配置后,是否重启了 API 和 Worker?