MinIO 分布式集群搭好了,4 个节点跑得挺欢,API 端口 9000,Console 端口 9001。
然后问题来了:客户端怎么访问?总不能让人记住 4 个节点的 IP 吧?万一某个节点挂了呢?
答案很明确:在集群前面加一层 Nginx 反向代理。但 MinIO 的代理配置和普通 Web 应用不太一样------文件上传可能是几个 G,请求可能是长连接,还有 API 和 Console 两套端口要分开代理。
这篇文章,从零开始配置一套生产可用的 Nginx 反向代理方案。
架构概览
先明确我们要达成的效果:
- 用户访问
https://minio-api.example.com→ Nginx → 后端 4 个 MinIO 节点的 9000 端口 - 管理员访问
https://minio-console.example.com→ Nginx → 后端 4 个 MinIO 节点的 9001 端口 - 当某个节点挂了,Nginx 自动把流量切到其他节点
- 大文件上传不受 buffer 限制
- HTTPS 证书统一在 Nginx 层管理
第一步:基本 upstream 配置
在 Nginx 里定义 MinIO 集群的 upstream:
nginx
# /etc/nginx/conf.d/minio-upstream.conf
upstream minio_api {
server 192.168.1.11:9000 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:9000 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.13:9000 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.14:9000 weight=1 max_fails=3 fail_timeout=30s;
}
upstream minio_console {
server 192.168.1.11:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.13:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.14:9001 weight=1 max_fails=3 fail_timeout=30s;
# Console 不是无状态的,建议 ip_hash 或只代理单节点
# 下文详细说明
}
参数说明:
weight=1:所有节点权重相同,轮询分发max_fails=3:连续失败 3 次后标记为不可用fail_timeout=30s:标记不可用后,30 秒内不再分发到该节点
第二步:API 端口的代理配置
MinIO 的 S3 API 对代理有一些特殊要求,漏掉任何一项都可能导致上传失败或超时。
nginx
# /etc/nginx/conf.d/minio-api.conf
server {
listen 80;
server_name minio-api.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name minio-api.example.com;
# 证书配置
ssl_certificate /etc/nginx/ssl/minio-api.pem;
ssl_certificate_key /etc/nginx/ssl/minio-api.key;
# 大文件上传专用:允许最大 10G 的请求体
client_max_body_size 10240M;
# ★ 核心:关闭代理缓冲
# MinIO 的 S3 API 使用 Chunked Transfer Encoding,如果开缓冲,
# Nginx 会先把整个请求体存到磁盘再转发,导致上传超时
proxy_buffering off;
proxy_request_buffering off;
# 超时配置:大文件上传可能需要很长时间
proxy_connect_timeout 60s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
# 保持 Host 头,MinIO 用它做虚拟主机路由
proxy_set_header Host $http_host;
# 传递真实客户端 IP(用于审计和安全策略)
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 禁止缓存 S3 API 响应
proxy_set_header X-NginX-Proxy true;
add_header Cache-Control "no-cache, no-store, must-revalidate";
# ★ 关键:对 WebSocket 升级支持(用于事件通知)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
location / {
proxy_pass http://minio_api;
}
}
为什么要关闭 proxy_buffering?
MinIO 的 S3 API 在上传大文件时使用 Transfer-Encoding: chunked。如果 Nginx 开了缓冲,它会先等客户端把整个文件发完,存到本地临时目录,再转发给 MinIO。对于几个 G 的文件,这不仅浪费磁盘空间,还会导致请求超时。
关闭之后,Nginx 收到多少就转发多少,数据以流式通过,不落地。
第三步:Console 端口的代理配置
Console 是 Web 管理界面,和 API 端口的代理需求不同------它涉及 WebSocket 连接和 Cookie。
nginx
# /etc/nginx/conf.d/minio-console.conf
server {
listen 80;
server_name minio-console.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name minio-console.example.com;
ssl_certificate /etc/nginx/ssl/minio-console.pem;
ssl_certificate_key /etc/nginx/ssl/minio-console.key;
client_max_body_size 10M;
# 超时配置
proxy_read_timeout 300s;
proxy_send_timeout 300s;
# Cookie 域名重写,确保登录 Cookie 正确
proxy_cookie_domain 192.168.1.11 minio-console.example.com;
proxy_cookie_path / /;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持(Console 登录和实时刷新需要)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
location / {
proxy_pass http://minio_console;
}
}
Console 负载均衡的坑
MinIO Console 不是完全无状态的------用户登录后的 Session 绑定在特定节点上。如果你在 Console 的 upstream 里用默认轮询,可能出现"登录成功但刷新后掉线"的问题。
解决方案有两种:
- 推荐方案 :Console 的 upstream 只用
ip_hash,让同一个客户端的请求始终落到同一个节点 - 更简单的方案:Console 只代理一个节点,不做负载均衡。管理界面不需要高可用
nginx
upstream minio_console {
ip_hash; # ★ 同一个来源 IP 始终路由到同一个节点
server 192.168.1.11:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.12:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.13:9001 weight=1 max_fails=3 fail_timeout=30s;
server 192.168.1.14:9001 weight=1 max_fails=3 fail_timeout=30s;
}
第四步:健康检查
Nginx 的 max_fails 和 fail_timeout 只能检测端口是否可达。如果你需要更精确的健康检查(比如检测 MinIO 服务是否正常响应),需要主动健康检查。
开源 Nginx 不支持主动健康检查,需要 nginx-plus 或使用 ngx_http_healthcheck_module。一个更实际的方案是用 proxy_next_upstream:
nginx
location / {
proxy_pass http://minio_api;
# 当后端返回以下错误时,自动重试下一个节点
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
proxy_next_upstream_tries 2; # 最多重试 2 次
proxy_next_upstream_timeout 10s; # 重试超时
}
如果用了 proxy_buffering off(API 端口),注意一个限制:当请求体已经部分转发给后端后,Nginx 无法再重试到其他节点。因此 proxy_next_upstream 对上传请求的保护有限。
对于大文件上传,更好的做法是在客户端层面做重试(MinIO 各语言的 SDK 都内置了重试机制)。
第五步:HTTPS 终结
生产环境统一在 Nginx 层处理 TLS,MinIO 节点之间跑 HTTP 就行。这样做的好处:
- 证书集中管理(续期只需改 Nginx,不用动 4 个节点)
- 减轻 MinIO 节点的 CPU 消耗(TLS 握手有一定开销)
- 方便接入 WAF 和日志采集
使用 Let's Encrypt 免费证书:
bash
# 安装 certbot
yum install -y certbot python3-certbot-nginx
# 申请证书
certbot --nginx -d minio-api.example.com -d minio-console.example.com
# 自动续期
echo "0 3 * * * certbot renew --quiet && systemctl reload nginx" >> /etc/crontab
如果你用的是自签名证书或企业 CA 签发的证书,直接配 ssl_certificate 和 ssl_certificate_key 即可。
第六步:完整的负载均衡策略选择
Nginx 提供了多种负载均衡算法,不同场景选择不同:
| 算法 | 指令 | 适用场景 |
|---|---|---|
| 轮询(默认) | 不指定 | API 端口,各节点同等能力 |
| 加权轮询 | weight=N |
节点配置不同(如有的节点磁盘更大) |
| IP 哈希 | ip_hash |
Console 端口,需要 Session 保持 |
| 最少连接 | least_conn |
长连接场景,避免单节点过载 |
| 一致性哈希 | hash $request_uri |
特定对象的读写希望落到同一节点 |
对于 MinIO API,默认轮询就够用了。因为 MinIO 节点之间通过纠删码已经实现了数据分布,读取任意节点都能取到数据。
第七步:验证配置
配置完成后,分步骤验证:
bash
# 1. 先检查 Nginx 配置语法
nginx -t
# 2. 重载配置
nginx -s reload
# 3. 测试 API 端口
curl -I https://minio-api.example.com/health
# 4. 用 mc 客户端测试连通性
mc alias set myminio https://minio-api.example.com ACCESS_KEY SECRET_KEY
mc ls myminio
# 5. 测试大文件上传
dd if=/dev/zero of=testfile bs=1M count=100
mc cp testfile myminio/testbucket/
# 6. 模拟节点故障
# 停掉一个 MinIO 节点,验证请求是否自动切换到其他节点
systemctl stop minio@node1
mc ls myminio # 应该依然正常工作
常见问题排查
上传大文件时 504 Gateway Timeout
检查 proxy_read_timeout 和 proxy_send_timeout,大文件上传可能需要设置为 3600s 或更长。同时确认 proxy_buffering off 已生效。
Console 登录后频繁掉线
检查 Console 的 upstream 是否用了 ip_hash。如果没有,Cookie 里的 Session ID 可能被路由到了不持有该 Session 的节点。
上传报错 413 Request Entity Too Large
检查 client_max_body_size,默认只有 1M。根据业务需求设置为合适的值(如 10240M)。
部分节点离线后读写报错
检查 Nginx 错误日志 /var/log/nginx/error.log,看是否有连接超时。确认 max_fails 和 fail_timeout 配置生效。如果节点已经恢复但流量仍不回来,可能是 fail_timeout 时间还没过。
写在最后
Nginx 反代 MinIO 的方案不算复杂,但细节不少。核心记住三条:
- API 端口:关缓冲、长超时、加上流式转发
- Console 端口:用
ip_hash保持 Session - 健康检查:
proxy_next_upstream做兜底,但上传请求的容错最好靠客户端 SDK 重试
另外,如果你的 MinIO 集群跑在 Kubernetes 里,可以用 Ingress 来实现类似的效果,但这个话题留到以后再说。