이 글에서는 쿠버네티스 환경에서 StatefulSet을 사용하여 MinIO를 배포하고 설정하는 방법을 상세히 알아보겠습니다. MinIO의 프로덕션 수준 구성을 위한 YAML 파일 작성부터 실제 배포 과정까지 단계별로 살펴보며, 각 설정의 의미와 중요성을 이해할 수 있도록 설명하겠습니다.
📌 StatefulSet으로 MinIO 구성의 중요성
✅ 상태 유지의 필요성
MinIO는 객체 스토리지 시스템으로, 데이터의 영속성과 신뢰성이 매우 중요합니다. 일반 Deployment와 달리 StatefulSet은 다음과 같은 이점을 제공합니다:
- 고정된 포드 이름과 네트워크 식별자
- 순차적인 배포, 스케일링, 업그레이드
- 안정적인 네트워크 ID와 영구 스토리지
✅ MinIO와 StatefulSet의 시너지
MinIO는 분산 스토리지 시스템으로 특히 StatefulSet과 잘 어울립니다:
- 각 인스턴스별 고유한 식별자 필요
- 스토리지 볼륨과 인스턴스의 지속적인 매핑
- 클러스터 토폴로지 유지 필요성
▶️ StatefulSet 없이 MinIO를 배포할 경우 발생할 수 있는 문제:
- 포드 재시작 시 데이터 손실
- 확장 과정에서 데이터 불일치
- 클러스터 토폴로지 변경으로 인한 작동 불안정
📌 MinIO StatefulSet 배포 준비
✅ 사전 요구사항
StatefulSet으로 MinIO를 배포하기 전에 확인해야 할 사항들:
- 작동 중인 쿠버네티스 클러스터 (1.16+)
- kubectl CLI 도구
- StorageClass가 구성되어 있어야 함
- 네임스페이스 생성
✅ 네임스페이스 생성하기
먼저 MinIO 리소스들을 위한 전용 네임스페이스를 생성합니다:
apiVersion: v1 # 쿠버네티스 API 버전으로, 네임스페이스는 v1 API 그룹에 속함
kind: Namespace # 생성할 리소스 유형을 네임스페이스로 지정
metadata:
name: minio-system # 네임스페이스 이름을 'minio-system'으로 지정하여 MinIO 관련 리소스를 논리적으로 분리
labels:
app: minio # 리소스 그룹화를 위해 'app: minio' 라벨 추가
role: object-storage # 이 네임스페이스의 역할이 객체 스토리지임을 명시하는 라벨
아래 명령으로 네임스페이스를 생성합니다:
kubectl apply -f minio-namespace.yaml
✅ MinIO 시크릿 생성
MinIO의 액세스 키와 시크릿 키를 쿠버네티스 시크릿으로 생성합니다:
apiVersion: v1 # 쿠버네티스 코어 API 버전
kind: Secret # 시크릿 리소스 유형 지정
metadata:
name: minio-auth # 시크릿의 이름을 'minio-auth'로 지정하여 인증 정보임을 명시
namespace: minio-system # 앞서 생성한 'minio-system' 네임스페이스에 시크릿 생성
type: Opaque # 일반 키-값 쌍을 저장하기 위한 Opaque 유형의 시크릿
data:
# 실제 배포 시에는 반드시 기본값이 아닌 보안성 높은 복잡한 값으로 변경 필요
accesskey: bWluaW9hZG1pbg== # MinIO 액세스 키를 Base64로 인코딩한 값 (이 값은 'minioadmin'을 인코딩한 것)
secretkey: bWluaW9hZG1pbg== # MinIO 시크릿 키를 Base64로 인코딩한 값 (보안을 위해 실제 배포 시 변경 필요)
시크릿을 생성하는 명령:
kubectl apply -f minio-secret.yaml
✅ 헤드리스 서비스 생성
StatefulSet과 함께 사용할 헤드리스 서비스를 생성합니다:
apiVersion: v1 # 쿠버네티스 코어 API 버전
kind: Service # 서비스 리소스 유형 지정
metadata:
name: minio-headless # 'minio-headless'라는 이름으로 헤드리스 서비스 정의
namespace: minio-system # minio-system 네임스페이스에 서비스 생성
labels:
app: minio # 서비스에 app=minio 라벨 지정
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" # 준비되지 않은 엔드포인트도 DNS에 등록
spec:
publishNotReadyAddresses: true # 준비되지 않은 포드도 서비스 레코드에 포함 (중요: MinIO 클러스터 구성 시 필수)
clusterIP: None # 'None'으로 설정하여 헤드리스 서비스로 구성 (개별 포드 IP 직접 접근 가능)
ports:
- port: 9000 # MinIO의 API 포트
name: minio-api # 포트 이름 지정 (API 포트임을 명시)
- port: 9001 # MinIO 콘솔 포트
name: minio-console # 포트 이름 지정 (콘솔 포트임을 명시)
selector:
app: minio # app=minio 라벨을 가진 포드들을 이 서비스의 엔드포인트로 선택
📌 MinIO StatefulSet 정의 및 배포
✅ StatefulSet 구성 파일 작성
이제 본격적으로 MinIO StatefulSet 정의 파일을 만들어 보겠습니다:
apiVersion: apps/v1 # 쿠버네티스 앱 API 버전
kind: StatefulSet # StatefulSet 유형의 리소스 정의
metadata:
name: minio # 'minio'라는 이름으로 StatefulSet 정의
namespace: minio-system # minio-system 네임스페이스에 생성
spec:
serviceName: "minio-headless" # 앞서 만든 헤드리스 서비스와 연결 (포드 이름 지정에 중요)
replicas: 4 # 4개의 MinIO 포드 복제본 생성 (최소 4개 권장: 분산 모드 위한 최소 구성)
selector:
matchLabels:
app: minio # app=minio 라벨과 일치하는 포드 선택
template:
metadata:
labels:
app: minio # 생성되는 포드에 app=minio 라벨 부여
spec:
affinity:
podAntiAffinity: # 포드 안티-어피니티 설정으로 가용성 향상
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100 # 높은 가중치 부여 (권장사항 강하게 적용)
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app # app 라벨이 minio인 포드와 함께
operator: In
values:
- minio
topologyKey: "kubernetes.io/hostname" # 다른 호스트에 배포하려고 시도 (단일 노드 실패 방지)
containers:
- name: minio # 컨테이너 이름 지정
image: minio/minio:latest # MinIO 최신 이미지 사용 (프로덕션에서는 특정 버전 태그 권장)
args:
- server # MinIO 서버 모드로 실행
- --console-address # 콘솔 접근 주소 설정
- ":9001" # 9001 포트로 콘솔 서비스 노출
- http://minio-{0...3}.minio-headless.minio-system.svc.cluster.local/data # StatefulSet의 DNS 패턴을 활용한 분산 설정
ports:
- containerPort: 9000 # API 포트 노출
name: minio-api
- containerPort: 9001 # 콘솔 포트 노출
name: minio-console
env:
- name: MINIO_ROOT_USER # MinIO 루트 사용자 설정
valueFrom:
secretKeyRef:
name: minio-auth # 앞서 생성한 시크릿에서 값 참조
key: accesskey # 액세스 키 값 사용
- name: MINIO_ROOT_PASSWORD # MinIO 루트 비밀번호 설정
valueFrom:
secretKeyRef:
name: minio-auth # 앞서 생성한 시크릿에서 값 참조
key: secretkey # 시크릿 키 값 사용
readinessProbe: # 포드 준비성 프로브 설정
httpGet:
path: /minio/health/ready # MinIO 준비 상태 확인 엔드포인트
port: 9000 # API 포트로 상태 확인
initialDelaySeconds: 30 # 첫 검사 전 30초 대기
periodSeconds: 10 # 10초마다 검사 반복
livenessProbe: # 활성 프로브 설정
httpGet:
path: /minio/health/live # MinIO 활성 상태 확인 엔드포인트
port: 9000 # API 포트로 상태 확인
initialDelaySeconds: 120 # 첫 활성 검사 전 2분 대기 (초기화에 충분한 시간 제공)
periodSeconds: 20 # 20초마다 검사 반복
resources: # 리소스 요청 및 제한 설정
requests:
memory: 1Gi # 최소 1GB 메모리 요청 (MinIO 권장)
cpu: 500m # 초기 0.5 CPU 코어 요청
limits:
memory: 2Gi # 최대 2GB 메모리 제한
cpu: 1 # 최대 1 CPU 코어 제한
volumeClaimTemplates: # 포드별 영구 볼륨 클레임 템플릿
- metadata:
name: data # 'data'라는 이름의 볼륨 (MinIO의 /data 디렉토리에 마운트)
spec:
accessModes: [ "ReadWriteOnce" ] # 단일 노드 읽기/쓰기 접근 모드
resources:
requests:
storage: 10Gi # 각 포드에 10GB 스토리지 요청 (프로덕션 환경에서는 더 큰 값 사용)
✅ StatefulSet 배포 및 검증
StatefulSet 배포:
kubectl apply -f minio-statefulset.yaml
배포 상태 확인:
kubectl get statefulset -n minio-system
kubectl get pods -n minio-system
📌 MinIO 접근을 위한 추가 서비스 생성
✅ API 접근용 서비스
MinIO API에 접근하기 위한 서비스를 생성합니다:
apiVersion: v1 # 쿠버네티스 코어 API 버전
kind: Service # 서비스 리소스 타입 지정
metadata:
name: minio-api # API용 서비스 이름을 'minio-api'로 명시적 지정
namespace: minio-system # minio-system 네임스페이스에 서비스 생성
labels:
app: minio # app=minio 라벨 지정으로 리소스 그룹화
spec:
type: ClusterIP # 클러스터 내부에서만 접근 가능한 ClusterIP 유형 (보안 강화)
ports:
- port: 9000 # 서비스 포트 9000 설정
targetPort: 9000 # 포드 내 컨테이너의 9000 포트로 연결
protocol: TCP # TCP 프로토콜 사용
name: minio-api # 포트 이름을 'minio-api'로 지정
selector:
app: minio # app=minio 라벨을 가진 포드들에 트래픽 라우팅
✅ 콘솔 접근용 서비스
MinIO 웹 콘솔에 접근하기 위한 서비스를 생성합니다:
apiVersion: v1 # 쿠버네티스 코어 API 버전
kind: Service # 서비스 리소스 타입 지정
metadata:
name: minio-console # 콘솔용 서비스 이름을 'minio-console'로 명시적 지정
namespace: minio-system # minio-system 네임스페이스에 서비스 생성
labels:
app: minio # app=minio 라벨 지정으로 리소스 그룹화
spec:
type: NodePort # 클러스터 외부에서 접근 가능하도록 NodePort 유형 선택
ports:
- port: 9001 # 서비스 포트 9001 설정
targetPort: 9001 # 포드 내 컨테이너의 9001 포트로 연결
protocol: TCP # TCP 프로토콜 사용
name: minio-console # 포트 이름을 'minio-console'로 지정
selector:
app: minio # app=minio 라벨을 가진 포드들에 트래픽 라우팅
📌 MinIO 설정 검증 및 사용 시작하기
✅ 모든 리소스 상태 확인
배포된 모든 리소스를 확인합니다:
kubectl get all -n minio-system
출력 예시:
NAME READY STATUS RESTARTS AGE
pod/minio-0 1/1 Running 0 5m
pod/minio-1 1/1 Running 0 4m
pod/minio-2 1/1 Running 0 3m
pod/minio-3 1/1 Running 0 2m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/minio-api ClusterIP 10.100.43.129 <none> 9000/TCP 5m
service/minio-console NodePort 10.100.178.34 <none> 9001:31901/TCP 5m
service/minio-headless ClusterIP None <none> 9000/TCP,9001/TCP 5m
NAME READY AGE
statefulset.apps/minio 4/4 5m
✅ 콘솔 접근 테스트
MinIO 콘솔에 접근하여 설정을 확인합니다:
- 노드 IP와 NodePort를 사용하여 브라우저에서 접근
- http://<노드IP>:31901
- 시크릿에 설정한 액세스 키와 시크릿 키로 로그인
✅ API 연결 테스트
MinIO 클라이언트(mc)를 사용하여 API 연결을 테스트합니다:
# 클러스터 내부에서 테스트하기 위한 임시 포드 생성
kubectl run -i --tty --rm debug --image=minio/mc --namespace=minio-system -- /bin/bash
# MinIO 클라이언트 설정 (포드 내부에서)
mc alias set myminio http://minio-api:9000 <액세스키> <시크릿키>
# 버킷 리스트 확인
mc ls myminio
📌 MinIO 분산 구성 이해하기

✅ DNS 기반 StatefulSet 구성의 의미
우리가 배포한 MinIO 설정의 핵심 부분인 서버 인수를 다시 살펴보겠습니다:
- server
- http://minio-{0...3}.minio-headless.minio-system.svc.cluster.local/data
이 설정은 다음과 같은 의미를 가집니다:
- minio-{0...3}: StatefulSet의 포드 이름 패턴 (minio-0, minio-1, minio-2, minio-3)
- minio-headless.minio-system.svc.cluster.local: 헤드리스 서비스의 FQDN
- /data: 각 노드에 마운트된 PVC 경로
▶️ 예시 - 데이터 분산 저장 과정:
- 사용자가 100MB 파일을 업로드
- MinIO는 이 파일을 여러 부분으로 분할
- 4개의 노드에 데이터가 분산 저장되어 고가용성 확보
- 단일 노드 장애 시에도 데이터 접근 가능
✅ 관리 명령어 사용 방법
MinIO 클러스터를 관리하기 위한 유용한 kubectl 명령어:
# MinIO 포드 로그 확인
kubectl logs minio-0 -n minio-system
# 포드 상세 정보 확인
kubectl describe pod minio-0 -n minio-system
# 헤드리스 서비스 DNS 조회 (클러스터 내부에서)
kubectl run -i --tty --rm debug --image=busybox --namespace=minio-system -- nslookup minio-headless
✅ 스케일링 고려사항
MinIO 분산 배포의 스케일링 유의사항:
- MinIO는 4의 배수로 확장 권장 (4, 8, 12, 16 등)
- StatefulSet 스케일링은 데이터 재분배 없이 진행됨
- 확장 후 새 노드 추가를 위해서는 MinIO 서버 인수 업데이트 필요
Summary:
- StatefulSet은 MinIO와 같은 상태 유지 애플리케이션에 최적의 배포 방식을 제공
- MinIO는 상태 유지가 필요한 애플리케이션으로 StatefulSet이 적합함
- 헤드리스 서비스와 PVC 템플릿으로 안정적인 네트워크/스토리지 구성
- 프로브와 리소스 제한 설정으로 안정적인 운영 확보
- 분산 모드는 최소 4개 노드부터 구성 가능하며 가용성 제공
- 각 포드 간 통신은 DNS 기반 이름으로 이루어짐
- 포트 분리를 통한 API 및 콘솔 접근 관리