Files
atlas-seller-kit/docs/guides/minio-local-deployment.md

11 KiB
Raw Permalink Blame History

MinIO 入门:本地对象存储的部署与使用

面向 Atlas Seller Kit 的学习文档。本文解释 MinIO 是什么、本项目为什么在本地开发使用它、如何用 Docker 部署,以及如何按本项目的 object_key 约定访问文件。object_key 与 URL 的概念区分见对象存储入门。

1. MinIO 是什么

MinIO 是一个开源的对象存储服务器,完整实现了 Amazon S3 API。它可以理解为"你自己跑的一个 S3":

项目 MinIO 云端对象存储(如七牛 Kodo)
形态 单个容器/二进制,跑在自己机器或服务器上 托管服务,开箱即用
接口 S3 兼容 API S3 兼容 API(基础能力子集)
数据位置 本机 Docker 卷 云端
运维 自己负责(本地开发无所谓) 云厂商负责
费用 免费 按存储/流量计费

它只有三个核心概念(与所有 S3 兼容存储通用):

  • Bucket(存储桶):文件的顶层容器,例如 atlas-dev-assets;
  • Object Key(对象键):桶内文件的唯一键,例如 workspaces/ws_123/products/p_456/original/asset_789.jpg;
  • 预签名 URL:带签名和过期时间的临时访问地址,用于把私有文件的读写权限短暂借给浏览器。

2. 本项目为什么用 MinIO

Atlas 的技术选型是:本地开发用 MinIO,staging/production 用七牛 Kodo(见技术栈与部署第 4 节)。选择这个组合的原因:

  1. 代码同构:两者都讲 S3 方言。应用代码只写一套 S3 客户端逻辑,本地和生产只差 endpoint、凭证、桶名这几个配置项,不差代码。
  2. 行为可验证:私有桶权限、预签名 URL 过期、ETag/Content-Type、MIME 校验这些行为,在本地 MinIO 上验证过的逻辑,到 Kodo 上基本原样成立。如果本地直接读写磁盘目录,这些逻辑完全测不到。
  3. 可随时重建:MinIO 数据在 Docker 卷里,删掉卷重启就是全新的存储,适合配合版本化 seed 数据做开发。
  4. 成本为零:开发阶段不产生云存储费用,也不会误用生产 Bucket。

注意边界:MinIO 只进本地开发环境。首期不在普通单台云服务器上自建生产 MinIO——磁盘冗余、备份、故障恢复、TLS、监控都要自己做,"部署了 MinIO"不等于获得托管对象存储的可靠性。

3. 用 Docker 部署本地 MinIO

3.1 单命令快速启动

只想临时体验时:

docker run -d --name atlas-minio \
  -p 9000:9000 -p 9001:9001 \
  -v atlas-minio-data:/data \
  minio/minio server /data --console-address ":9001"

两个端口各有用途:

端口 用途
9000 S3 API 端口。应用代码(SDK、mc 客户端)访问这个。
9001 Web Console 端口。浏览器打开 http://localhost:9001 管理文件。

3.2 接入项目的 Docker Compose

正式开发应把它写进项目根目录的 docker-compose.yml,与 postgres、redis 并列:

services:
  minio:
    image: minio/minio:latest   # 团队建议固定到具体 RELEASE tag,避免本地环境漂移
    command: server /data --console-address ":9001"
    ports:
      - "9000:9000"             # S3 API
      - "9001:9001"             # Web Console
    environment:
      MINIO_ROOT_USER: atlas-dev
      MINIO_ROOT_PASSWORD: atlas-dev-secret   # 仅本地开发用,不得用于任何共享环境
    volumes:
      - minio_data:/data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 10s
      timeout: 3s
      retries: 5

volumes:
  minio_data:

说明:

  • MINIO_ROOT_USER / MINIO_ROOT_PASSWORD 是这个实例的 root 账号,仅本地使用;真正的云端密钥永远走环境变量或密钥管理,不提交 Git。
  • -v minio_data:/data 把数据落在命名卷里;docker compose down 不丢数据,docker compose down -v 会连数据一起清掉(重置开发环境的常用手段)。
  • 建议把这两个值放进 .env(该文件已在 .gitignore 中),compose 里用 MINIO_ROOT_USER: ${MINIO_ROOT_USER} 引用。

3.3 应用读取的配置约定

NestJS API/Worker 通过环境变量拿到连接信息,本地 .env 示例:

# 对象存储(本地 MinIO / 生产 Kodo 只换这一组值)
S3_ENDPOINT=http://localhost:9000
S3_REGION=us-east-1            # MinIO 不校验 region,任意合法值即可
S3_BUCKET=atlas-dev-assets
S3_ACCESS_KEY=atlas-dev
S3_SECRET_KEY=atlas-dev-secret
S3_FORCE_PATH_STYLE=true       # MinIO 必须用 path-style,见第 6.3 节

4. 创建 Bucket

MinIO 的桶默认是私有的,符合本项目"Bucket 默认私有"的规则。两种创建方式任选。

4.1 Web Console(直观)

  1. 打开 http://localhost:9001,用 root 账号登录;
  2. 左侧 Object Browser → Create Bucket;
  3. 名称填 atlas-dev-assets,保持默认(不勾选公开访问),确认创建。

4.2 mc 命令行(可脚本化)

mc 是 MinIO 官方 CLI,也可以用来管理任何 S3 兼容存储。用一次性容器执行,不必本机安装:

# 1. 给本地实例起个别名
docker run --rm --network host minio/mc alias set local http://localhost:9000 atlas-dev atlas-dev-secret

# 2. 创建桶
docker run --rm --network host minio/mc mb local/atlas-dev-assets

# 3. 确认桶是私有的(输出应为 none,即不允许匿名访问)
docker run --rm --network host minio/mc anonymous get local/atlas-dev-assets

首次启动时"确保桶存在"也可以由 API 启动逻辑完成(本地环境 head bucket 失败就 create bucket),这样新同事 docker compose up 后无需手工步骤。

5. 日常使用

5.1 Console 里的常见操作

操作 位置
浏览/搜索对象 Object Browser,按前缀逐级进入
手动上传/下载 进入桶后右上角 Upload / 对象行 Download
查看对象元数据 对象行右侧菜单 → Object info(大小、ETag、Content-Type)
生成临时分享链接 对象行菜单 → Share(本质是预签名 GET URL,可设过期时间)
管理访问密钥 Identity → Access Keys(可给本地工具发独立密钥,不共用 root)

5.2 mc 常用命令速查

alias set local http://localhost:9000 USER PASS   # 配置连接别名
mb   local/atlas-dev-assets                       # 创建桶
ls   local/atlas-dev-assets/workspaces/           # 列出前缀
cp   ./test.jpg local/atlas-dev-assets/tmp/test.jpg   # 上传
cp   local/atlas-dev-assets/tmp/test.jpg ./       # 下载
stat local/atlas-dev-assets/tmp/test.jpg          # 查看元数据
rm   local/atlas-dev-assets/tmp/test.jpg          # 删除对象
du   local/atlas-dev-assets                       # 桶占用

5.3 应用代码访问(NestJS / Node)

本项目统一使用 AWS S3 SDK v3 指向自定义 endpoint,本地 MinIO 与生产 Kodo 走同一条代码路径:

import { S3Client, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

const s3 = new S3Client({
  endpoint: process.env.S3_ENDPOINT!,       // http://localhost:9000
  region: process.env.S3_REGION!,           // MinIO 任意值即可
  credentials: {
    accessKeyId: process.env.S3_ACCESS_KEY!,
    secretAccessKey: process.env.S3_SECRET_KEY!,
  },
  forcePathStyle: true,                     // MinIO 必须;Kodo 走虚拟主机式,配置驱动即可
});

// 预签名上传 URL:前端拿到后直传,浏览器不保存永久密钥
export async function presignUpload(objectKey: string, contentType: string) {
  const cmd = new PutObjectCommand({ Bucket: process.env.S3_BUCKET, Key: objectKey, ContentType: contentType });
  return getSignedUrl(s3, cmd, { expiresIn: 600 }); // 10 分钟
}

// 预签名下载 URL:预览/下载私有文件
export async function presignDownload(objectKey: string) {
  const cmd = new GetObjectCommand({ Bucket: process.env.S3_BUCKET, Key: objectKey });
  return getSignedUrl(s3, cmd, { expiresIn: 900 }); // 15 分钟
}

服务端直接读写(如 Worker 转存采集素材)则不经过预签名,直接调 PutObjectCommand 等。

5.4 与 Atlas 的 object_key 约定结合

本地桶内对象键沿用技术文档第 4.2 节的分层:

atlas-dev-assets/
  workspaces/{workspaceId}/
    watermarks/{assetId}/
    products/{productId}/original/{assetId}.jpg
    products/{productId}/processed/{assetId}.jpg
    products/{productId}/ai-generated/{assetId}.jpg
    imports/{taskId}/{filename}

配套规则(详见对象存储入门):

  • media_assets 表保存 storage_provider='local_minio' + bucket + object_key,任何 URL 不入库;
  • 预览/下载 URL 由服务端按 object_key 短时签发,过期即失效;
  • 来源站的原始图片 URL 只保存在采集证据里,转存成功后文件身份以 object_key 为准。

6. 常见问题

6.1 SDK 报错找不到桶或 DNS 异常

MinIO 默认不支持 bucket.endpoint 虚拟主机域名形式,SDK 必须开启 path-style(地址形如 http://localhost:9000/atlas-dev-assets/...)。AWS SDK v3 对应 forcePathStyle: true。该项目配置驱动这一点:本地为 true,Kodo 按其要求配置。

6.2 预签名 URL 在浏览器里打不开

预签名 URL 的 host 就是 S3_ENDPOINT。http://localhost:9000 只在本机浏览器可用;如果要给局域网同事访问,endpoint 需要用局域网 IP 并让 MinIO 监听对应地址。签名与 host 绑定,换了 host 签名即失效。

6.3 上传后 Content-Type 或文件名不对

预签名 PutObjectCommand 时设置的 ContentType 必须与前端实际上传时携带的 Content-Type 一致,否则 S3 签名校验会拒绝。项目在 Worker/服务端二次校验 MIME 与魔数,不信任浏览器声明。

6.4 想清空本地存储重来

docker compose down -v    # -v 连同 minio_data 卷一起删除
docker compose up -d

然后重建桶(或让应用启动逻辑自动创建),即可配合 seed 数据得到干净环境。

6.5 时钟偏差报错(RequestTimeTooSkewed)

S3 签名对时间敏感。宿主机睡眠/恢复后 Docker 容器时钟可能漂移,重启容器或同步系统时间即可。

7. 本项目边界

  • MinIO 仅用于 local 环境;staging/production 使用七牛 Kodo 独立 Bucket,严禁混用。
  • Bucket 保持私有;原图、水印模板图、AI 生成图和导入文件不设公开读。
  • 浏览器只持有短时上传/下载凭证;永久密钥只存在于服务端环境配置。
  • 对象物理删除前必须确认无数据库引用;由后台清理任务在软删除保留期结束后执行。

相关文档:对象存储与 object_key、技术栈、异步任务与部署方案、浏览器采集插件需求。