18
0
0

Docker 部署 MinIO:控制台打得开,程序传不上

2026-10-29
2026-10-30
文章摘要
|

背景与目标

MinIO 是自托管的 S3 兼容对象存储,常用来存图片、附件、备份、模型文件。相比直接买云对象存储,它跑在自己机器上、不按流量计费,接口又和 S3 一致,迁移成本低。

但它的"坑"不在安装——镜像一拉就能跑——而在两条容易混淆的边界:

  • 端口边界:9000 是给程序调用的 S3 API,9001 是给人操作的控制台。只映射一个,另一半就一定用不了。

  • 权限边界:能登录控制台(root 账号)不等于业务程序能上传。程序用的是 access key,权限来自它所属用户绑定的 policy,而不是 root 密码。

本文给你一份两个端口都配好的 compose,并用"建桶 → 传文件 → 生成分享链接"走通一遍,之后逐个拆掉这两条边界上最常见的五个坑。

提示:宿主 Windows (Docker Desktop),镜像走华为云。本文用 latest 起步;要长期维护请把 tag 换成具体的 RELEASE.* 版本号,避免自动升级带来行为变化。

步骤

第 0 步:确认 Docker Desktop 已就绪

docker version
docker compose version

输出版本号即 OK。

第 1 步:准备目录

mkdir minio && cd minio

第 2 步:写 docker-compose.yaml

新建 docker-compose.yaml,整段复制:

services:
  minio:
    image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/minio/minio:latest
    container_name: minio
    command: server /data --console-address ":9001"
    environment:
      MINIO_ROOT_USER: "minioadmin"
      MINIO_ROOT_PASSWORD: "YourStrong!Passw0rd"
      TZ: "Asia/Shanghai"
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - ./minio-data:/data
    restart: unless-stopped

要点:

  • command 后面的 --console-address ":9001" 必须写。不写的话控制台会用随机端口,日志里那行地址每次启动都可能变。

  • MINIO_ROOT_USER / MINIO_ROOT_PASSWORD:root 管理账号,密码至少 8 位,否则容器直接退出。

  • 数据卷 ./minio-data 绑定挂载到 /data,桶和对象都存在这里(含 .minio.sys 元数据)。

  • 两个端口缺一不可:9000 给 SDK / mc,9001 给浏览器。

  • 单机单盘只适合开发与个人使用,不要拿它当生产高可用方案。

第 3 步:启动

docker compose up -d

看日志,确认两行地址都打印出来:

docker compose logs -f minio
API: http://172.x.x.x:9000  http://127.0.0.1:9000
WebUI: http://172.x.x.x:9001  http://127.0.0.1:9001

看到 WebUI 那行指向 9001 即就绪。

第 4 步:连接验证

先验 API 活着(PowerShell 里要用 curl.exe,curl 是 Invoke-WebRequest 的别名):

curl.exe -i http://localhost:9000/minio/health/live

返回 HTTP/1.1 200 OK 即 API 正常。

再用浏览器打开控制台:

http://localhost:9001

用 MINIO_ROOT_USER / MINIO_ROOT_PASSWORD 登录,然后:

  1. 左侧 Buckets → Create Bucket,名字填 demo;

  2. 进 demo,Upload 一个文件;

  3. 点该文件的 Share,复制生成的预签名链接,在浏览器新标签打开。

能下载到文件,说明 API、控制台、对象读写全通了。

实操截图

容器运行

ScreenShot_2026-10-29_095657_156.png

访问

ScreenShot_2026-10-29_095708_107.png
ScreenShot_2026-10-29_095716_748.png

踩坑与解决

1. 控制台打不开,或者打开只看到一屏 XML

  • 问题:访问 9000 只得到 <Error><Code>AccessDenied</Code>...,访问 9001 又连接失败。

  • 复现:只映射 9000:9000 时,浏览器打开 9000 是 XML 报错;只映射 9001:9001 时,控制台页面加载不出来。

  • 根因:MinIO 有两个监听端口。9000 是 S3 API,只返回 XML 风格的协议响应;9001 才是控制台。控制台本身还要回连 API,所以两个端口都必须可达。

  • 解决:compose 里同时映射 9000 和 9001,并显式写 --console-address ":9001";容器起来后确认日志里 WebUI 行是 9001。

  • 验证:浏览器打开 http://localhost:9001 能看到登录页。

2. 容器一启动就退出:root 密码太短

  • 问题:docker compose up -d 后容器反复重启。

  • 复现:docker compose logs minio 报 MINIO_ROOT_PASSWORD 长度不足或 Unable to initialize backend。

  • 根因:MinIO 对 root 凭据有硬性要求:用户名至少 3 位、密码至少 8 位。太短直接拒绝启动,而不是降级运行。

  • 解决:换成至少 8 位的强密码(如 YourStrong!Passw0rd),docker compose up -d 重建。

  • 验证:日志不再报凭据错误,两行地址正常打印。

3. 用新 access key 上传报 AccessDenied

  • 问题:控制台里建了用户/access key,SDK 却传不上去,返回 AccessDenied。

  • 复现:程序用新 key 调 PutObject,报 Access Denied;用 root 账号同样的代码却成功。

  • 根因:MinIO 的权限来自 policy。新建用户默认没有任何 policy,access key 继承用户的权限,所以"能建 key"不代表"能读写"。用 root 跑业务则是把管理账号暴露给程序,风险更大。

  • 解决:给用户绑内置 policy(需要宿主机装了 mc,单文件客户端):

    mc alias set local http://localhost:9000 minioadmin 'YourStrong!Passw0rd'
    mc admin user add local appuser 'AnotherStrong!Passw0rd'
    mc admin policy attach local readwrite --user appuser

    然后在控制台为该用户建 access key,程序用这对 key。旧版 mc 的写法是:

    mc admin policy set local readwrite user=appuser
  • 验证:用新 key 跑一次 PutObject,返回 200;mc admin user info local appuser 能看到 readwrite。

4. 文件传上去了,浏览器直接打开却是 403

  • 问题:对象明明在上传列表里,拼出 http://localhost:9000/demo/xxx.png 访问却被拒。

  • 复现:浏览器打开对象直链,返回 AccessDenied。

  • 根因:桶默认是私有的。对象只能通过带签名的预签名 URL 访问,裸链接不带签名,服务端当然拒绝。

  • 解决:二选一:

    • 私有对象:用控制台的 Share,或 SDK 的 presigned_get_object 生成带签名的临时链接(默认 7 天有效);

    • 公开素材:显式开放匿名读 mc anonymous set download local/demo,需要收回时 mc anonymous set none local/demo。

  • 验证:私有桶用预签名链接能下载;公开桶用直链能下载。

5. 预签名链接打不开,或者报 SignatureDoesNotMatch

  • 问题:后端程序生成的分片上传 / 下载链接,前端拿到后打不开。

  • 复现:链接形如 http://minio:9000/demo/xxx.png?...,浏览器无法解析 minio 这个主机名;换成别的域名访问又报签名不匹配。

  • 根因:预签名 URL 的主机名和端口来自生成签名时使用的 endpoint,而且签名覆盖了 Host。用容器网络里的 http://minio:9000 生成,宿主机和浏览器当然访问不到;换成另一个域名/端口,签名又对不上。

  • 解决:生成签名的一方必须使用客户端实际访问的地址作为 endpoint(本地就是 http://localhost:9000,对外就是公网域名 + 对应端口)。走反向代理时保留原始 Host 头,不要改写。

  • 验证:前端拿到的链接主机名与浏览器地址栏一致,打开即下载。

通用坑(与 SQL Server / MySQL / Redis / MongoDB / PostgreSQL 篇相同,不再重复展开)

  • 端口被占:9000 或 9001 已被别的程序占用,报 port is already allocated。改外部端口即可,如 19000:9000、19001:9001;注意改了 9000 之后,SDK 和预签名 URL 里的端口要同步改。

  • 数据丢了:没挂数据卷,容器销毁即丢失。本教程已挂 ./minio-data:/data,down 再 up 后桶和对象仍在。

  • 容器秒退:先看 docker compose logs minio,再对照上面第 2 条。

小结与参考

  • 用 Docker 部署 MinIO,两个端口都要映射:9000 走 S3 API,9001 走控制台,并用 --console-address 固定控制台端口。

  • root 账号只做管理;业务程序用独立用户 + access key,权限靠 policy 授予(readwrite / readonly / consoleAdmin)。

  • 本篇核心矛盾:控制台能登录 ≠ 程序能上传。前者用 root 口令,后者用 access key + policy;再加上预签名 URL 里的 endpoint 必须是客户端可达地址,这两处才是 MinIO 真正的门槛。

  • 私有桶走预签名 URL,公开素材才用 mc anonymous set download。

  • 数据落在 ./minio-data,容器重建不丢。

参考:

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

Docker 部署 MinIO:控制台打得开,程序传不上
/archives/wei-ming-ming-wen-zhang
作者
隔壁老陈
发布于
2026-10-29
许可协议
CC BY-NC-SA 4.0

评论