跳到主要内容

使用 Docker 自建 S3 对象存储:从服务部署到完整连接参数配置

· 阅读需 17 分钟
ZhiQing Lin
CEO | Fuzhou Beisi Network Technology Co., Ltd.

当多个项目都需要上传图片、保存视频或生成文件下载链接时,可以部署一套统一的 S3 兼容对象存储,再通过不同的存储桶划分数据。

本文使用 Silo,通过 Docker Compose 部署服务,将对象数据保存到独立数据盘,使用两个 HTTPS 域名分别提供 API 和管理控制台,最后完成存储桶、访问密钥、公开读取权限及应用连接参数的配置。

本文按全新部署编写,不涉及旧 MinIO 数据迁移。示例统一使用:

S3 API 域名:s3.example.com
控制台域名:s3-console.example.com

项目目录:/opt/s3
数据目录:/data/s3

示例桶名:your-bucket

实际部署时,将示例域名替换为自己的域名。

一、先了解 MinIO 和 Silo 的区别​

Silo 是基于 MinIO 的独立社区分支,维护服务端、完整管理控制台及容器发行版本,同时保留 S3 接口和 MINIO_* 环境变量等兼容配置。它有自己的维护和发布流程,升级时应按独立分支评估。GitHub

截至本文核对日期 2026 年 10 月 1 日,官方 minio/minio 仓库已显示于 2026 年 4 月 25 日归档。选择部署方案时,需要同时考虑已有功能和后续维护情况。GitHub

本文使用以下固定版本,避免直接依赖会变化的 latest 标签:GitHub

docker.io/pgsty/silo:RELEASE.2026-09-16T00-00-00Z

版本安全说明: 项目已披露 SN-2026-015,涉及 Object Lock 保留期授权,公告将上述版本列为受影响版本,核对时尚未列出修复发行版。本文不启用 Object Lock,也不向不受信任的外部租户发放凭证;有防删保留或不可信多租户需求时,应先确认相关修复状态,再考虑投产。SILO

二、确定部署结构​

本次采用下面的入口关系:

应用程序
↓ HTTPS
s3.example.com
↓ 反向代理
127.0.0.1:9000
↓
Silo S3 API


管理人员
↓ HTTPS
s3-console.example.com
↓ 反向代理
127.0.0.1:9001
↓
Silo 管理控制台

Silo 的 9000 端口提供 S3 API,启动参数将控制台固定在 9001。两个域名分别转发到这两个入口。SILO

服务器上的目录安排如下:

/opt/s3/compose.yaml    Docker Compose 配置
/opt/s3/.env 管理员凭证和控制台地址
/data/s3/ 对象存储数据

本文假设反向代理与 Silo 位于同一台 Linux 服务器,且反代直接运行在宿主机上,或反代容器使用 host 网络。这样的反代可以通过宿主机的 127.0.0.1 访问服务。Docker Documentation

如果反代使用普通 Docker bridge 网络,它的 127.0.0.1 指向反代容器自身,需要改用共享容器网络等连接方式,不能照搬本文的上游地址。Docker Documentation

三、检查 Docker、数据盘和端口​

以下命令按使用 root 用户执行编写。

1. 检查 Docker Compose​

docker compose version

确认命令能够正常返回版本信息,再继续后续操作。

2. 确认 /data 确实挂载在数据盘上​

findmnt -M /data -o SOURCE,TARGET,FSTYPE

df -h / /data

第一条命令用于检查 /data 这个挂载点。输出可能类似:man7.org

SOURCE    TARGET FSTYPE
/dev/sdb1 /data ext4

重点确认根目录 / 和 /data 所属的文件系统,以及数据盘的可用空间。目录名叫 /data,并不能单独证明它使用了独立磁盘。

如果 findmnt -M /data 没有输出,先完成数据盘挂载,不要继续创建对象数据目录。

3. 检查 9000 和 9001​

ss -lntp '( sport = :9000 or sport = :9001 )'

如果已有服务监听这两个端口,应先确认占用者,避免直接停止正在使用的旧服务。

反代运行在 Docker 中时,还可以查看容器网络模式:

docker ps -q | xargs -r docker inspect \
--format '{{.Name}} | 网络={{.HostConfig.NetworkMode}}'

看到对应反代容器的网络模式为 host,便符合本文使用本地上游地址的前提。Docker Documentation

四、创建目录和 .env​

1. 创建项目目录与数据目录​

mountpoint /data \
&& mkdir -p /opt/s3 /data/s3 \
&& ls -ld /opt/s3 /data/s3

这段命令先检查 /data 是挂载点,检查通过后才创建目录。man7.org

2. 生成管理员凭证​

下面的命令会生成随机密码,并创建权限受限的 .env 文件。

先把控制台地址替换成自己的域名。已有 .env 时,命令会停止,避免覆盖原来的凭证。

cd /opt/s3 && (
set -eu
umask 077

if [ -e .env ]; then
echo ".env 已存在,停止操作,避免覆盖。"
exit 1
fi

password="$(openssl rand -hex 24)"

printf '%s\n' \
'MINIO_ROOT_USER=s3admin' \
"MINIO_ROOT_PASSWORD=$password" \
'MINIO_BROWSER_REDIRECT_URL=https://s3-console.example.com' \
> .env

chmod 600 .env
ls -l .env
)

.env 中包含三个变量:

MINIO_ROOT_USER=s3admin
MINIO_ROOT_PASSWORD=实际生成的随机密码
MINIO_BROWSER_REDIRECT_URL=https://s3-console.example.com

前两个变量设置管理员凭证,第三个变量声明控制台的外部访问地址。SILO

不要把 .env 提交到公开仓库,也不要将包含密码的终端截图公开。

五、创建 Docker Compose 配置​

新建 /opt/s3/compose.yaml,写入下面的内容。已有同名文件时,应先核对,避免覆盖其他部署配置。

services:
silo:
image: docker.io/pgsty/silo:RELEASE.2026-09-16T00-00-00Z
restart: unless-stopped
command: server /data --console-address ":9001"

env_file: .env

ports:
- "127.0.0.1:9000:9000" # S3 API
- "127.0.0.1:9001:9001" # 管理控制台

volumes:
- /data/s3:/data # 宿主机数据盘目录 /data/s3,挂载到容器内 /data

logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

镜像和启动参数采用项目提供的容器运行方式。这里没有固定 container_name,由 Compose 自动生成容器名称。SILO

六、配置两个 HTTPS 反向代理入口​

准备好两个域名的 DNS 解析和证书,然后分别设置:

对外域名反代上游用途
s3.example.comhttp://127.0.0.1:9000S3 API
s3-console.example.comhttp://127.0.0.1:9001管理控制台

本文由反向代理处理 HTTPS,反代到本机容器时使用 HTTP。

使用 Nginx、OpenResty 或面板配置时,需要关注原始 Host 和转发协议头、上传大小限制、请求超时及控制台 WebSocket 支持。项目的官方反代模板包含相应设置。SILO

S3 API 建议独占一个域名的根路径:

https://s3.example.com

不要直接把标准 S3 API 改挂到任意子路径,例如:

https://example.com/storage-api/

S3 签名包含请求路径等信息,额外的路径改写容易破坏签名。本文使用独立子域名,避免这类问题。SILO

七、启动服务并验证​

1. 校验配置​

cd /opt/s3 && docker compose config -q

-q 只验证配置,不打印解析后的完整配置,避免把凭证输出到终端。Docker Documentation

2. 拉取镜像​

cd /opt/s3 && docker compose pull

这一步只下载镜像,不会启动容器。Docker Documentation

3. 启动容器​

cd /opt/s3 \
&& mountpoint /data \
&& docker compose up -d \
&& docker compose ps -a

up -d 在后台启动服务,ps -a 显示当前 Compose 项目的容器状态,包括异常退出的容器。Docker Documentation

需要排查启动问题时,查看日志:

cd /opt/s3 && docker compose logs --tail=100 silo

4. 核对实际数据挂载​

cd /opt/s3

docker inspect "$(docker compose ps -q silo)" \
--format '{{range .Mounts}}{{println .Source "->" .Destination}}{{end}}'

findmnt -T /data/s3 -o SOURCE,TARGET,FSTYPE

第一条应包含:

/data/s3 -> /data

第二条应显示 /data/s3 所在文件系统的挂载目标是 /data。两项结合起来,可以检查容器数据目录与实际磁盘的对应关系。Docker Documentation

5. 检查本地和域名入口​

先检查本地服务:

curl -sS -I --max-time 10 \
http://127.0.0.1:9000/minio/health/live

curl -sS -I --max-time 10 \
http://127.0.0.1:9001/

再检查经过反代的 API:

curl -sS -I --max-time 15 \
https://s3.example.com/minio/health/live

健康检查预期返回 200。它用于确认服务可达,完整的文件上传和读取仍需另外测试。SILO

本地检查成功、域名检查失败时,优先排查 DNS、证书和反代上游配置。

八、登录控制台并创建存储桶​

浏览器打开:

https://s3-console.example.com

登录账号使用 .env 中的 MINIO_ROOT_USER,密码使用 MINIO_ROOT_PASSWORD。

在自己的安全终端中可以查看:

grep '^MINIO_ROOT_' /opt/s3/.env

这条命令会显示管理员密码,不要公开输出内容。

登录后,进入存储桶管理页面,创建:

your-bucket

创建后先保持默认私有状态,随后单独设置公开读取权限。控制台支持在桶详情中管理访问策略,以及对象的上传和浏览。SILO

记录桶名时直接复制控制台中的实际名称。 后面的应用配置、权限策略和公开 URL 都必须使用同一个名字。

九、创建应用访问密钥​

在控制台进入:

访问密钥 → 创建访问密钥

创建过程中保存两项信息:

Access Key
Secret Key

页面里的“名称”和“描述”用于管理识别,应用认证需要的是实际 Access Key 和配对的 Secret Key。项目文档要求在创建时保存 Secret Key;丢失后应创建新凭证并轮换旧凭证。SILO

1. 访问密钥会自动绑定桶吗?​

访问密钥的有效权限取决于父用户权限和密钥自身的限制策略。给密钥取一个与桶相似的名称,不会自动建立权限关联。SILO

如果使用管理员创建访问密钥,并保持继承父用户权限,它就可能具备整个实例的管理和存储访问权限,可以操作其他桶。

对于仅自己使用的验证环境,可以先采用这种方式跑通流程。长期给多个项目使用时,建议每个项目使用独立密钥,并将权限限制到对应桶。仅仅创建了多个密钥,但都继承管理员权限,并没有实现项目间隔离。

2. 可选:限制密钥只访问一个桶​

编辑访问密钥,启用 Restrict beyond user policy,在密钥的限制策略中配置桶范围。该策略只能进一步缩小父用户的权限。SILO

下面是支持基本对象读写的示例:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-bucket"
]
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::your-bucket/*"
]
}
]
}

第一段用于桶级查询和对象列表,第二段用于桶内对象的读取、上传和删除。需要分段上传管理、版本管理等能力时,再按实际调用补充权限。SILO

这份 JSON 放在访问密钥的限制策略中,不能拿它代替下一节的匿名访问策略。

十、让桶里的图片可以公开访问​

应用能够使用密钥上传文件,与浏览器能否匿名读取文件,是两个独立问题。

如果希望图片通过普通 URL 直接展示,就需要为相应对象开放匿名读取权限。对于私密文件,应保持私有,并采用有时效的签名链接或带鉴权的下载接口。AWS 文档

1. 为桶设置匿名只读策略​

进入:

存储桶 → your-bucket → 概览 → 访问策略 → 自定义

不同控制台版本的入口文字可能略有差异,目标是编辑当前桶的访问策略。SILO

填入:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::your-bucket/*"
]
}
]
}

Principal: "*" 指向所有访问者,s3:GetObject 授予对象读取权限,资源范围限定在 your-bucket 桶内。这个新增授权不包含匿名上传、删除或列出文件清单。AWS 文档

十一、整理完整的 S3 连接参数​

完成前面的部署、建桶和密钥创建后,应用可以按下面的方式填写。

本文将 Public URL 定义为“可以直接拼接对象键的公开访问前缀”,并采用路径式访问,也就是将桶名放在 URL 路径中。AWS 文档

配置项示例值或获取方式
存储类型s3
S3 Endpointhttps://s3.example.com
S3 Regionus-east-1
S3 Bucket Namebucket-demo
S3 Access Key控制台创建的实际 Access Key
S3 Secret Key与 Access Key 配对的 Secret Key
S3 Public URLhttps://s3.example.com/bucket-demo
Force Path StyleSDK 或应用提供该选项时,按本文路径式方案启用

Region 采用 Silo 官方接入示例中的值;AWS SDK 提供 forcePathStyle 选项来强制使用路径式访问。SILO

1. Endpoint 和控制台地址​

Endpoint 填 API 域名:

https://s3.example.com

不要填管理控制台域名,也不要在 Endpoint 后面添加桶名。本部署中的 API 与控制台分别对应 9000 和 9001。SILO

2. Region 为什么使用 us-east-1?​

Region 参与 S3 请求签名。它并非完全没有作用,服务端配置和客户端签名区域不匹配时,可能影响认证。AWS 文档

本文没有配置自定义区域,客户端沿用官方示例:

us-east-1

在已经指定自建 Endpoint 的情况下,填写这个值不会把数据转存到 AWS,也不会改变本地数据目录。SDK 的连接目标由自定义 Endpoint 决定,Region 仍用于签名等配置。AWS 文档

3. Public URL 是否要包含桶名?​

按本文的定义,需要包含桶名:

https://s3.example.com/your-bucket

假设对象键为:

images/test.png

完整访问地址就是:

https://s3.example.com/your-bucket/images/test.png

这里要注意,Public URL 是应用侧的配置约定,不是所有 S3 SDK 都具有的统一参数。 本文使用下面的拼接方式:

完整文件 URL = Public URL + "/" + 对象键

某些应用会自行补上桶名,这类应用的 Public URL 就可能只需要填写 API 域名。应以应用文档或实际生成的文件 URL 为准,避免最终地址缺少桶名,或重复出现两次桶名。

4. 业务项目的环境变量示例​

业务项目可以采用类似配置:

S3_ENDPOINT=https://s3.example.com
S3_REGION=us-east-1
S3_BUCKET=your-bucket
S3_ACCESS_KEY_ID=填写实际AccessKey
S3_SECRET_ACCESS_KEY=填写实际SecretKey
S3_PUBLIC_URL=https://s3.example.com/your-bucket
S3_FORCE_PATH_STYLE=true

这些变量名仅作为应用配置示例。 现成项目需要按它自己的字段名称填写,不应假设它自动识别上面所有变量。

十二、完成文件上传和匿名读取测试​

1. 测试控制台上传​

在 bucket-demo 根目录上传一个测试图片:

test.png

先确认控制台能够看到这个对象。

2. 测试匿名读取​

使用浏览器无痕窗口访问:

https://s3.example.com/your-bucket/test.png

也可以执行:

curl -sS -I --max-time 15 \
https://s3.example.com/your-bucket/test.png

使用完整对象地址验证,不要只打开桶地址。只授予 GetObject 时,匿名用户没有 ListBucket 权限,桶地址返回 AccessDenied 并不能说明图片读取失败。SILO

3. 测试业务项目上传​

将连接参数填入业务项目,上传一张新的测试图片,再确认该对象出现在 bucket-demo 中,并检查应用返回的 URL 能否访问。

控制台上传成功,只能验证管理员这条操作路径。 使用业务项目再上传一次,才能检验应用的 Endpoint、密钥和桶配置是否正确。

如果浏览器能够直接打开图片,但应用中的跨域请求失败,再根据浏览器报错检查 CORS。桶的公开读取权限与 CORS 是不同配置,CORS 也不会替代存储访问授权。AWS 文档

十三、常用维护命令与注意事项​

查看状态和日志:

cd /opt/s3

docker compose ps -a
docker compose logs --tail=100 silo

修改 Compose 或 .env 后应用配置:

cd /opt/s3 \
&& docker compose config -q \
&& mountpoint /data \
&& docker compose up -d

单纯执行 docker compose restart 不会应用 Compose 配置变更,包括环境变量变更。应使用 up -d 让 Compose 根据新配置更新容器。Docker Documentation

升级时,先核对目标版本的发布说明和安全公告,备份数据及配置,再修改固定镜像标签并执行:

cd /opt/s3 \
&& docker compose pull \
&& mountpoint /data \
&& docker compose up -d

单机部署升级可能造成短暂服务中断,业务环境应安排维护时间,并验证回滚和恢复方案。Docker Documentation

最后需要保留两个边界。

数据持久化只能保证容器替换后继续使用宿主机数据,无法应对数据盘损坏、误删除或整台服务器丢失。 重要对象需要独立备份,并实际验证恢复。Docker Documentation

公开读取只适合明确允许公开的对象。 多项目共用同一个实例时,桶划分、应用密钥权限和匿名访问策略应分别维护,不能仅凭桶名不同就认为已经完成隔离。SILO

完成这些步骤后,应用接入所需的信息就已明确:

Endpoint:https://s3.example.com
Region:us-east-1
Bucket:your-bucket
Access Key:控制台创建的应用访问密钥
Secret Key:对应的访问密钥密码
Public URL:https://s3.example.com/your-bucket

后续新增项目时,可以沿用同一个 API Endpoint,创建新的桶和应用凭证,再按该项目的文件用途决定是否开放匿名读取。