자체 오브젝트 스토리지가 필요한 이유는 다양합니다. AWS S3 비용 절감, 데이터를 일본 국내(또는 한국 인접)에 둬야 하는 규정, 빠른 LAN 내부 액세스 등. 그럴 때 가장 검증된 선택지가 MinIO입니다. AWS S3 API와 100% 호환되어 코드 수정 없이 그대로 옮길 수 있습니다.
MinIO를 선택해야 할 때
| 상황 | MinIO 적합 | 비고 |
|---|---|---|
| S3 비용 절감 | ✓ | 데이터 트래픽 비용도 자체 회선이라 0원 |
| 데이터를 일본·한국에 둬야 함 | ✓ | 도쿄 IDC 사용 |
| 사진·영상 호스팅 | ✓ | 정적 파일 서빙 가능 |
| 글로벌 CDN 필요 | △ | 앞단에 Cloudflare 등 추가 필요 |
| 99.99% 가용성 필요 | △ | 분산 노드 4대+ 권장 |
단일 노드 MinIO는 디스크 1대를 그대로 사용하므로 디스크 장애 = 데이터 손실입니다. 중요한 데이터는 백업 또는 분산 모드(4노드+)로 운영해야 합니다.
1단계 — 디스크 준비
MinIO는 빈 디렉토리만 있으면 동작하지만, 별도 디스크/파티션을 권장합니다. 다른 서비스의 디스크 사용량과 영향을 분리하기 위함입니다.
# 새 디스크 마운트 예시 (전용서버에 추가 디스크가 있을 때)
sudo mkfs.xfs /dev/sdb1
sudo mkdir -p /mnt/minio
sudo mount /dev/sdb1 /mnt/minio
# /etc/fstab 등록
echo "/dev/sdb1 /mnt/minio xfs defaults,noatime 0 2" | sudo tee -a /etc/fstab
xfs가 큰 파일과 많은 파일 양쪽에서 안정적이라 MinIO 공식 문서도 권장합니다.
2단계 — MinIO 설치
# 바이너리 다운로드
wget https://dl.min.io/server/minio/release/linux-amd64/minio -O /usr/local/bin/minio
chmod +x /usr/local/bin/minio
# 클라이언트(mc)도 함께
wget https://dl.min.io/client/mc/release/linux-amd64/mc -O /usr/local/bin/mc
chmod +x /usr/local/bin/mc
전용 사용자 생성:
sudo useradd -r minio-user -s /sbin/nologin
sudo chown -R minio-user:minio-user /mnt/minio
3단계 — systemd 서비스로 등록
# /etc/systemd/system/minio.service
[Unit]
Description=MinIO
After=network-online.target
Wants=network-online.target
[Service]
User=minio-user
Group=minio-user
EnvironmentFile=/etc/default/minio
ExecStart=/usr/local/bin/minio server $MINIO_OPTS $MINIO_VOLUMES
Restart=always
LimitNOFILE=65536
TasksMax=infinity
TimeoutStopSec=infinity
[Install]
WantedBy=multi-user.target
# /etc/default/minio
MINIO_VOLUMES="/mnt/minio"
MINIO_OPTS="--console-address :9001 --address :9000"
MINIO_ROOT_USER="admin"
MINIO_ROOT_PASSWORD="<강력한 비밀번호 32자 이상>"
sudo systemctl daemon-reload
sudo systemctl enable --now minio
sudo systemctl status minio
4단계 — 방화벽
기본 포트는 9000(API), 9001(Console)입니다. 외부에 노출할 필요가 없다면 차단하고, Nginx 리버스 프록시 뒤에 둡니다.
# 외부 노출 차단, localhost만
sudo ufw deny 9000
sudo ufw deny 9001
5단계 — Nginx로 HTTPS 종단
s3.example.com 도메인을 MinIO 앞에 둡니다.
# /etc/nginx/sites-available/minio
server {
listen 443 ssl http2;
server_name s3.example.com;
ssl_certificate /etc/letsencrypt/live/s3.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/s3.example.com/privkey.pem;
# 대용량 업로드 허용
client_max_body_size 5G;
proxy_request_buffering off;
proxy_buffering off;
location / {
proxy_pass http://127.0.0.1:9000;
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;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
}
}
# Console (관리 UI)
server {
listen 443 ssl http2;
server_name console.s3.example.com;
ssl_certificate /etc/letsencrypt/live/console.s3.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/console.s3.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:9001;
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 (콘솔 실시간 갱신)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
}
}
client_max_body_size는 업로드 가능한 최대 객체 크기입니다. 영상 업로드까지 받으려면 충분히 키워야 합니다.
6단계 — 버킷 생성과 사용자 권한
mc 클라이언트로 관리합니다.
# 별칭 등록
mc alias set local https://s3.example.com admin '<MINIO_ROOT_PASSWORD>'
# 버킷 생성
mc mb local/uploads
mc mb local/backups
# 버킷 목록
mc ls local
# 정책 적용 (uploads만 공개 읽기)
mc anonymous set download local/uploads
애플리케이션용 별도 사용자(IAM 키)를 만듭니다.
mc admin user add local app-user '<32자 비밀번호>'
# 정책 작성: uploads 버킷에만 접근
cat > /tmp/app-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::uploads/*"]
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::uploads"]
}
]
}
EOF
mc admin policy create local app-policy /tmp/app-policy.json
mc admin policy attach local app-policy --user app-user
이제 app-user의 액세스 키와 시크릿 키를 애플리케이션 환경 변수로 사용하면 됩니다.
7단계 — 애플리케이션 코드 (S3 SDK 그대로)
AWS S3 SDK에 endpoint만 바꿔주면 됩니다.
import boto3
s3 = boto3.client(
's3',
endpoint_url='https://s3.example.com',
aws_access_key_id='app-user',
aws_secret_access_key='<32자 비밀번호>',
region_name='ap-northeast-1'
)
s3.upload_file('/tmp/photo.jpg', 'uploads', 'photos/2026/04/photo.jpg')
// Node.js (AWS SDK v3)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({
endpoint: "https://s3.example.com",
region: "ap-northeast-1",
credentials: {
accessKeyId: "app-user",
secretAccessKey: process.env.S3_SECRET,
},
forcePathStyle: true, // MinIO는 path-style 권장
});
8단계 — 백업
단일 노드 MinIO는 디스크 장애에 취약합니다. 다음 중 하나는 반드시 적용하세요.
옵션 A: 별도 서버로 미러링 (Site Replication)
mc admin replicate add local backup --remote-bucket=uploads
옵션 B: 정기적으로 다른 위치로 동기화
# 매일 새벽 백업 서버로 동기화
mc mirror --remove local/uploads backup-server/uploads
옵션 C: 분산 모드 (운영 환경 권장) 서버 4대 이상으로 erasure coding을 활성화. 디스크 절반이 죽어도 데이터가 보존됩니다.
운영 모니터링
MinIO는 Prometheus 메트릭을 기본 제공합니다.
GET https://s3.example.com/minio/v2/metrics/cluster
주요 지표:
minio_node_disk_free_bytes— 디스크 여유 공간minio_s3_requests_total— 초당 요청 수minio_node_disk_latency_us— 디스크 지연
Netdata, Grafana 대시보드와 연결하면 실시간 모니터링이 됩니다.
마무리
MinIO는 “S3 호환 + 자체 운영 + 일본 IDC 위치”라는 세 조건을 한 번에 만족시킵니다. 데이터 주권 요구가 있는 한국·일본 기업에 특히 잘 맞습니다.
영상·이미지 같은 대용량 워크로드는 전용서버의 디스크 옵션이 더 적합하며, 디스크 추가 옵션 문의는 @tcp80net으로 부탁드립니다.