Kubernetes

MinIO 소개

yu3papa 2025. 9. 13. 11:33

컴퓨팅 환경에서 스토리지는 정말 중요한 요소입니다.

스토리지의 근본적인 목적은 정보를 저장하는것이며, 이렇게 저장한 정보를 유실되지 않게 유지하는것 또한 중요합니다.

이번 글에서는 로컬 스토리지보다는 분산환경에서 사용하는 스토리지에 대해 알아보겠습니다.

1. Block Storage

블록 스토리지는 데이터를 블록이라는 고정된 크기의 단위로 나누어 관리하는 저장 방식입니다. 즉 I/O 의 단위가 블록입니다.

위의 그림은 윈도우즈 11 OS에서서 로컬에 장착된 디스크를 포맷하는 그림입니다. 위 그림에서  "할당 단위 크기" 가 스토리지에서 1개의 Block Size 이며, OS 관점에서 File I/O 를 하는 단위입니다.

각 블록에는 고유한 식별자(ID)가 부여되며, 운영체제(OS)는 이 ID를 사용하여 특정 블록에 접근하고 데이터를 읽거나 씁니다. 이러한 MetaData 관리를 위해 Linux 운영체제에서는 inode 라는 블럭을 이용하고 있습니다.

로컬 스토리지가 아닌 네트워크에 연결된 블록스토리지 사용되는 대표적인것이 SAN(Storage Area Network)이며 고성능이 요구되는 환경에서 사용되고 있습니다.

블록 스토리지는 주로 iSCSI, FC(Fibre Channel)와 같은 프로토콜을 사용하여 서버와 통신합니다. 이 프로토콜들은 네트워크를 통해 블록 단위의 데이터를 전송하여 서버가 원격의 저장장치를 직접 제어할 수 있게 합니다.

Block Storage를 사용할때 데이터 유실을 방지하기 위해 RAID 기술을 사용하고 있습니다.

이러한 블록스토리지는 아래와 같은 단점을 내포하고 있습니다.

  • 파일 단위가 아닌 블록 단위로 관리되므로, 사용자가 직접 파일 시스템을 구성해야 하는 등 관리의 복잡성이 높습니다.
  • SAN과 같은 전용 네트워크를 구축해야 하므로 초기 비용이 많이 들 수 있습니다.
  • 서버에 직접 연결되거나 SAN을 통해 연결되므로, 여러 사용자가 동시에 접근하기 어렵습니다.

2. Object Storage

객체 스토리지는 데이터를 객체(Object)라는 단위로 저장하고 관리하는 저장 방식입니다.

각 객체는 데이터(파일), 고유 식별자, 그리고 메타데이터로 구성됩니다.

블록 스토리지와 달리, 객체 스토리지는 폴더나 디렉터리 같은 계층 구조를 사용하지 않고, 모든 객체를 하나의 평면적인 저장 공간(Flat namespace)에 저장합니다.

객체 스토리지는 아래와 같은 특징이 있습니다.

  • 유연한 메타데이터: 객체에 사용자 정의 메타데이터를 추가할 수 있어 데이터에 대한 정보를 풍부하게 저장할 수 있습니다. 예를 들어, 사진 객체에 촬영 날짜, 위치, 카메라 모델 등을 메타데이터로 첨부할 수 있습니다. 이는 데이터 검색 및 관리를 용이하게 합니다.
  • 무한한 확장성: 계층 구조가 없기 때문에 이론적으로 저장 용량의 제한이 없습니다. 데이터를 수평적으로 확장할 수 있어 페타바이트(PB) 이상의 대용량 데이터를 효율적으로 관리할 수 있습니다.
  • RESTful API: 객체 스토리지는 주로 RESTful API를 통해 접근합니다. 이는 HTTP 기반의 통신 방식이므로, 웹 및 모바일 애플리케이션에서 데이터에 쉽게 접근하고 조작할 수 있습니다.

위와 같은 특징을 이용하여 아주 싼 가격에, 데이터 유실 없이 대용량의 데이터를 저장할 수 있고, http 프로토콜을 이용하여 CRUD가 가능하기 때문에 정적 웹사이트 운영에도 사용이 가능합니다.

대표적인 객체 스토리지 서비스로 AWS 의 S3 가 있습니다.

 

3. MinIO

https://www.min.io/

MinIO는 고성능, 분산형 객체 스토리지 서버입니다. AWS S3와 호환되는 API를 제공하여 온프레미스 환경이나 프라이빗 클라우드에서 객체 스토리지를 구축하고 관리할 수 있게 해줍니다.

MinIO의 핵심 특징

  • S3 API 호환성: MinIO는 AWS S3와 100% 호환되는 API를 제공합니다. 이 덕분에 개발자는 S3에서 사용하던 도구나 SDK를 그대로 MinIO에서 사용할 수 있어, 클라우드와 온프레미스 환경 간에 애플리케이션을 쉽게 마이그레이션하거나 하이브리드 클라우드 환경을 구축할 수 있습니다.
  • 고성능: MinIO는 분산 객체 스토리지로 설계되어 대규모 데이터를 병렬로 처리할 수 있습니다. 특히, AI/ML, 빅데이터 분석, 데이터베이스 백업과 같이 높은 처리량이 요구되는 워크로드에 최적화되어 있습니다.
  • 오픈 소스: MinIO는 Apache License v2.0 하에 배포되는 오픈 소스 소프트웨어입니다. 누구나 무료로 사용하고 수정할 수 있으며, 활발한 커뮤니티 지원을 받을 수 있습니다.
  • 단순함: MinIO는 설치와 관리가 매우 간단합니다. 하나의 바이너리 파일로 서버를 실행할 수 있으며, 웹 기반의 직관적인 사용자 인터페이스(UI)를 제공합니다.
  • 확장성: 여러 서버에 MinIO를 분산하여 구성하면 수평적 확장이 가능합니다. 저장 용량과 성능을 필요에 따라 유연하게 늘릴 수 있습니다.
  • 내구성: 삭제 코딩(Erasure Coding) 기술을 사용하여 데이터의 내구성을 높입니다. 이는 여러 드라이브에 데이터를 분산 저장하고, 일부 드라이브에 장애가 발생하더라도 데이터를 복구할 수 있게 해줍니다.

데이터의 가용성/내구성/유실방지를 위해서 "Erasure Coding" 기술을 이용한 Parity를 사용하여 개체 수준에서 데이터 보호를 작성합니다. (블록스토리지의 RAID 가 했던 역할)

MinIO의 가용성 및 복원력에 대해서는 아래 2개의 유튜브 동영상을 참고하면 이해하기가 쉽습니다.

  • MinIO Architecture Overview - https://youtu.be/sxcz6U0fUpo
  • Erasure Code Overview - https://youtu.be/sxcz6U0fUpo
    • Erasure = “지워짐”, “사라짐” → 데이터가 일부 손실되거나 없어지는 상황
    • Coding = “부호화”, “코딩” → 수학적인 방식(공식)으로 데이터를 변환하거나 추가 정보를 만드는 과정 → 원복을 재구성
    • ==> 즉, “지워진 데이터를 복구할 수 있도록 특별한 부호화를 하는 것” → 그래서 Erasure Coding 이라고 합니다.

Single-Node Single-Disk (SNSD) 를 이용한 MinIO 운영

도커 컨테이너를 이용하여 하나의 노드에 하나의 Disk 만 사용하는 MinIO 오브젝트 스토리지를 실습해보겠습니다.

이러한 아키텍처는 실무에서는 사용할 수 없으며, MinIO를 체험하는 수준에서 진행해 보겠습니다.

# Disk로 사용할 디렉토리 생성
mkdir /tmp/data

# container로 MinIO 실행
docker container run -itd \
-p 9000:9000 -p 9090:9090 \
--name minio \
-v /tmp/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=minio123" \
quay.io/minio/minio server /data --console-address ":9090"

Unable to find image 'quay.io/minio/minio:latest' locally
latest: Pulling from minio/minio
b83ce1c86227: Pull complete
f94d28849fa3: Pull complete
81260b173076: Pull complete
f9c0805c25ee: Pull complete
1008deaf6ec4: Pull complete
71e9fc939447: Pull complete
c1bc68842c41: Pull complete
0288b5a0d7e7: Pull complete
34013573f278: Pull complete
Digest: sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e
Status: Downloaded newer image for quay.io/minio/minio:latest
c4caaa66759212e11278f18b7c50e127f9065adc76fe0b93841bfa7963ee0be8

docker container ls
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                                                                      NAMES
c4caaa667592   quay.io/minio/minio   "/usr/bin/docker-ent…"   8 seconds ago   Up 7 seconds   0.0.0.0:9000->9000/tcp, [::]:9000->9000/tcp, 0.0.0.0:9090->9090/tcp, [::]:9090->9090/tcp   minio

# 로그를 확인해보면 Disk가 1개뿐이어서 데이터 유실 가능성이 있다고 Waring이 발생하였습니다.
docker container logs minio
INFO: Formatting 1st pool, 1 set(s), 1 drives per set.
INFO: WARNING: Host local has more than 0 drives of set. A host failure will result in data becoming unavailable.
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-09-07T16-13-09Z (go1.24.6 linux/amd64)

API: http://172.17.0.2:9000  http://127.0.0.1:9000
   RootUser: admin
   RootPass: minio123

WebUI: http://172.17.0.2:9090 http://127.0.0.1:9090
   RootUser: admin
   RootPass: minio123

CLI: https://docs.min.io/community/minio-object-store/reference/minio-mc.html#quickstart
   $ mc alias set 'myminio' 'http://172.17.0.2:9000' 'admin' 'minio123'

Docs: https://docs.min.io

# 9090 포트를 이용하여 MinIO 웹콘솔 접속


# 라이센스를 확인해보고  Communit  에디션과 Enterprise 에디션의 기능 비교


# 버킷 생성 : test

 

# 샘플 텍스트 파일 업로드 : 총 1152 라인이 있는 소설(텍스트) 파일

life.txt
0.06MB

 

 

# life.txt 오브젝트 정보 확인

# HOST OS 에서 파일 구조 분석

MinIO Client (MC) 사용

  • https://github.com/minio/mc
  • MinIO Client (mc) provides a modern alternative to UNIX commands like ls, cat, cp, mirror, diff, find etc.
  • It supports filesystems and Amazon S3 compatible cloud storage service (AWS Signature v2 and v4).
# Linux 용 mc 설치
curl --progress-bar -L https://dl.min.io/aistor/mc/release/linux-amd64/mc -o /usr/local/bin/mc
chmod +x /usr/local/bin/mc

# mc 는 편의를 위해 alias 를 제공합니다.
mc alias list
gcs
  URL       : https://storage.googleapis.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v2
  Path      : dns
  Src       : /root/.mc/config.json

local
  URL       : http://localhost:9000
  AccessKey :
  SecretKey :
  API       :
  Path      : auto
  Src       : /root/.mc/config.json

play
  URL       : https://play.min.io
  AccessKey : Q3AM3UQ867SPQQA43P2F
  SecretKey : zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
  API       : S3v4
  Path      : auto
  Src       : /root/.mc/config.json

s3
  URL       : https://s3.amazonaws.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v4
  Path      : dns
  Src       : /root/.mc/config.json

# myminio 앨리어스 등록
mc alias set 'myminio' 'http://127.0.0.1:9000' 'admin' 'minio123'
Added `myminio` successfully.

mc alias list myminio
myminio
  URL       : http://127.0.0.1:9000
  AccessKey : admin
  SecretKey : minio123
  API       : s3v4
  Path      : auto
  Src       : /root/.mc/config.json

# 이러한 mc 환경은 $HOME/.mc/config.json 파일에 저장됩니다.
cat ~/.mc/config.json
{
        "version": "10",
        "aliases": {
                "gcs": {
                        "url": "https://storage.googleapis.com",
                        "accessKey": "YOUR-ACCESS-KEY-HERE",
                        "secretKey": "YOUR-SECRET-KEY-HERE",
                        "api": "S3v2",
                        "path": "dns"
                },
                "local": {
                        "url": "http://localhost:9000",
                        "accessKey": "",
                        "secretKey": "",
                        "api": "S3v4",
                        "path": "auto"
                },
                "myminio": {
                        "url": "http://127.0.0.1:9000",
                        "accessKey": "admin",
                        "secretKey": "minio123",
                        "api": "s3v4",
                        "path": "auto"
                },
                "play": {
                        "url": "https://play.min.io",
                        "accessKey": "Q3AM3UQ867SPQQA43P2F",
                        "secretKey": "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG",
                        "api": "S3v4",
                        "path": "auto"
                },
                "s3": {
                        "url": "https://s3.amazonaws.com",
                        "accessKey": "YOUR-ACCESS-KEY-HERE",
                        "secretKey": "YOUR-SECRET-KEY-HERE",
                        "api": "S3v4",
                        "path": "dns"
                }
        }
}

# mc admin info - display MinIO server information
# myminio 서버의 정보 출력
mc admin info myminio
●  127.0.0.1:9000
   Uptime: 42 minutes
   Version: 2025-09-07T16:13:09Z
   Network: 1/1 OK
   Drives: 1/1 OK
   Pool: 1

┌──────┬───────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage          │ Erasure stripe size │ Erasure sets │
│ 1st  │ 8.6% (total: 229 GiB) │ 1                   │ 1            │
└──────┴───────────────────────┴─────────────────────┴──────────────┘

65 KiB Used, 1 Bucket, 1 Object
1 drive online, 0 drives offline, EC:0

mc ls myminio/test
[2025-09-13 12:29:20 KST]  65KiB STANDARD life.txt


mc tree --files myminio/test
myminio/test
└─ life.txt

mc find myminio/test --name "*.txt"
myminio/test/life.txt

mc stat myminio/test
Name      : test
Date      : 2025-09-13 13:01:09 KST
Size      : N/A
Type      : folder

Properties:
  Versioning: Un-versioned
  Location: us-east-1
  Anonymous: Disabled
  ILM: Disabled

Usage:
      Total size: 65 KiB
   Objects count: 1
  Versions count: 0

Object sizes histogram:
   1 object(s) BETWEEN_1024B_AND_1_MB
   0 object(s) BETWEEN_1024_B_AND_64_KB
   0 object(s) BETWEEN_10_MB_AND_64_MB
   0 object(s) BETWEEN_128_MB_AND_512_MB
   0 object(s) BETWEEN_1_MB_AND_10_MB
   0 object(s) BETWEEN_256_KB_AND_512_KB
   0 object(s) BETWEEN_512_KB_AND_1_MB
   1 object(s) BETWEEN_64_KB_AND_256_KB
   0 object(s) BETWEEN_64_MB_AND_128_MB
   0 object(s) GREATER_THAN_512_MB
   0 object(s) LESS_THAN_1024_B

mc stat myminio/test/life.txt
Name      : life.txt
Date      : 2025-09-13 12:29:20 KST
Size      : 65 KiB
ETag      : 93662839239f8c2cb4a9ae4122729571
Type      : file
Metadata  :
  Content-Type: text/plain

 

Policy

# minio 의 객체에 접근하기 위해서는 "인증 및 인가" 필요함
# IAM 기본 정책 및 커스텀 정책 확인
mc admin policy list myminio
readwrite
writeonly
consoleAdmin
diagnostics
readonly

# IAM 정책 세부 (대상 지정) -> AWS S3 정책과 똑같음
mc admin policy info myminio consoleAdmin | jq
{
  "PolicyName": "consoleAdmin",
  "Policy": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Action": [
          "admin:*"
        ]
      },
      {
        "Effect": "Allow",
        "Action": [
          "kms:*"
        ]
      },
      {
        "Effect": "Allow",
        "Action": [
          "s3:*"
        ],
        "Resource": [
          "arn:aws:s3:::*"
        ]
      }
    ]
  }

# 혀잰 버킷의 외부 공개 정책은 private 입니다.
mc anonymous get myminio/test
Access permission for `myminio/test` is `private`

 # 그래서 외부에서 접속하면 Access Denied 가 출력됩니다.
curl http://127.0.0.1:9000/test/life.txt
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied.</Message><Key>life.txt</Key><BucketName>test</BucketName><Resource>/test/life.txt</Resource><RequestId>1864BE8C2238231C</RequestId><HostId>dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8</HostId></Error>

# 버킷 외부 공개 정책 수정(public) : GET, PUT, LIST
mc anonymous set public myminio/test
Access permission for `myminio/test` is set to `public`

mc anonymous get myminio/test
Access permission for `myminio/test` is `public`

# 객체 접근 (외부 사용자)
curl http://127.0.0.1:9000/test/life.txt
Chapter 1: Childhood and Innocence

Chapter 2: Education and Curiosity

Chapter 3: Friendships and Bonds

0Chapter 4: Love and Discovery

 Chapter 5: Struggles and Resilience
...(생략)...

# 버킷 외부 공개 정책 원복(private)
mc anonymous set private myminio/test
Access permission for `myminio/test` is set to `private`

mc anonymous get myminio/test
Access permission for `myminio/test` is `private`

#실습 리소스 제거
docker container rm -f minio
minio

rm -fr /tmp/data

 

Single-Node Multi-Disk (SNMD) 를 이용한 MinIO 운영

여러개의 Disk를 이용하여 Erasure Code 기법을 이용한 파일 복구를 실습해 보겠습니다.

# 4개의 Disk 생성
mkdir -p /tmp/disk1 /tmp/disk2 /tmp/disk3 /tmp/disk4


 # 4개의 Disk를 사용하는 MinIO 컨테이너 실행
docker run -itd -p 9000:9000 -p 9090:9090 --name minio \
  -v /tmp/disk1:/data1 \
  -v /tmp/disk2:/data2 \
  -v /tmp/disk3:/data3 \
  -v /tmp/disk4:/data4 \
  -e "MINIO_ROOT_USER=admin" -e "MINIO_ROOT_PASSWORD=minio123" \
  -e "MINIO_STORAGE_CLASS_STANDARD=EC:1" \   
# 패리티 블럭을 1개로 설정 

  quay.io/minio/minio server /data{1...4} --console-address ":9090"
1aef92654fbfe7e133fe56450401815ea605ad8a0e221257fc4c4eddebb43121

docker container ls
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                                                                                      NAMES
1aef92654fbf   quay.io/minio/minio   "/usr/bin/docker-ent…"   23 seconds ago   Up 23 seconds   0.0.0.0:9000->9000/tcp, [::]:9000->9000/tcp, 0.0.0.0:9090->9090/tcp, [::]:9090->9090/tcp   minio

# minio 컨테이너 로그를 확인해보면 4개의 dirves per set 을 확인할 수 있음
docker container logs minio
INFO: Formatting 1st pool, 1 set(s), 4 drives per set.
INFO: WARNING: Host local has more than 1 drives of set. A host failure will result in data becoming unavailable.
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-09-07T16-13-09Z (go1.24.6 linux/amd64)

API: http://172.17.0.2:9000  http://127.0.0.1:9000
   RootUser: admin
   RootPass: minio123

WebUI: http://172.17.0.2:9090 http://127.0.0.1:9090
   RootUser: admin
   RootPass: minio123

CLI: https://docs.min.io/community/minio-object-store/reference/minio-mc.html#quickstart
   $ mc alias set 'myminio' 'http://172.17.0.2:9000' 'admin' 'minio123'

Docs: https://docs.min.io

# web 접속 후 test 버킷 생성 후 file 1개(text) 올리기
# HOST 파일시스템에서 버킷 및 오브젝트 구조 확인
# --> 4개의 Disk를 이용하고, 패리티 블럭을 1개 사용하기 때문에 Erasure Coding 이 동작함

 

유실 재현 및 복구 확인

mc admin info myminio
●  127.0.0.1:9000
   Uptime: 49 minutes
   Version: 2025-09-07T16:13:09Z
   Network: 1/1 OK
   Drives: 4/4 OK
   Pool: 1

┌──────┬───────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage          │ Erasure stripe size │ Erasure sets │
│ 1st  │ 8.6% (total: 686 GiB) │ 4                   │ 1            │
└──────┴───────────────────────┴─────────────────────┴──────────────┘

65 KiB Used, 1 Bucket, 1 Object
4 drives online, 0 drives offline, EC:1

mc stat myminio/test
Name      : test
Date      : 2025-09-13 14:54:46 KST
Size      : N/A
Type      : folder

Properties:
  Versioning: Un-versioned
  Location: us-east-1
  Anonymous: Disabled
  ILM: Disabled

Usage:
      Total size: 65 KiB
   Objects count: 1
  Versions count: 0

Object sizes histogram:
   1 object(s) BETWEEN_1024B_AND_1_MB
   0 object(s) BETWEEN_1024_B_AND_64_KB
   0 object(s) BETWEEN_10_MB_AND_64_MB
   0 object(s) BETWEEN_128_MB_AND_512_MB
   0 object(s) BETWEEN_1_MB_AND_10_MB
   0 object(s) BETWEEN_256_KB_AND_512_KB
   0 object(s) BETWEEN_512_KB_AND_1_MB
   1 object(s) BETWEEN_64_KB_AND_256_KB
   0 object(s) BETWEEN_64_MB_AND_128_MB
   0 object(s) GREATER_THAN_512_MB
   0 object(s) LESS_THAN_1024_B
[root@ts140 ~]# mc stat myminio/test/life.txt
Name      : life.txt
Date      : 2025-09-13 14:06:50 KST
Size      : 65 KiB
ETag      : 93662839239f8c2cb4a9ae4122729571
Type      : file
Metadata  :
  Content-Type: text/plain

# 강제로 (패리티 아닌)디렉터리 1개 제거
rm -rf /tmp/disk1/test


mc admin info myminio
●  127.0.0.1:9000
   Uptime: 50 minutes
   Version: 2025-09-07T16:13:09Z
   Network: 1/1 OK
   Drives: 4/4 OK
   Pool: 1

┌──────┬───────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage          │ Erasure stripe size │ Erasure sets │
│ 1st  │ 8.6% (total: 686 GiB) │ 4                   │ 1            │
└──────┴───────────────────────┴─────────────────────┴──────────────┘

65 KiB Used, 1 Bucket, 1 Object
4 drives online, 0 drives offline, EC:1

mc stat myminio/test/life.txt
Name      : life.txt
Date      : 2025-09-13 14:06:50 KST
Size      : 65 KiB
ETag      : 93662839239f8c2cb4a9ae4122729571
Type      : file
Metadata  :
  Content-Type: text/plain

# mc admin heal - monitor healing for bucket(s) and object(s) on MinIO server
# 버킷 힐
mc admin heal myminio/test
 ◐  test
    0/0 objects; 0 B in 0s
    ┌────────┬───┬─────────────────────┐
    │ Green  │ 1 │ 100.0% ████████████ │
    │ Yellow │ 0 │   0.0%              │
    │ Red    │ 0 │   0.0%              │
    │ Grey   │ 0 │   0.0%              │
    └────────┴───┴─────────────────────┘


mc stat myminio/test/life.txt

Name      : life.txt
Date      : 2025-09-13 14:06:50 KST
Size      : 65 KiB
ETag      : 93662839239f8c2cb4a9ae4122729571
Type      : file
Metadata  :
  Content-Type: text/plain

 

패리티블럭이 1개일때 2개의 데이터 블럭에 문제가 생기면 복구할 수 없습니다.

# 강제로 디렉터리 2개 제거
rm -rf cat /tmp/disk1/test
rm -rf cat /tmp/disk2/test

mc admin info myminio
●  127.0.0.1:9000
   Uptime: 57 minutes
   Version: 2025-09-07T16:13:09Z
   Network: 1/1 OK
   Drives: 4/4 OK
   Pool: 1

┌──────┬───────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage          │ Erasure stripe size │ Erasure sets │
│ 1st  │ 8.6% (total: 686 GiB) │ 4                   │ 1            │
└──────┴───────────────────────┴─────────────────────┴──────────────┘

65 KiB Used, 1 Bucket, 1 Object
4 drives online, 0 drives offline, EC:1

mc stat myminio/test/life.txt
mc: <ERROR> Unable to list folder. The specified bucket does not exist
mc: <ERROR> Unable to stat `myminio/test/life.txt`. Object does not exist.

# 버킷 힐 시도 -> 실패
mc admin heal myminio/test
 ◐  test
    0/0 objects; 0 B in 0s
    ┌────────┬───┬─────────────────────┐
    │ Green  │ 1 │ 100.0% ████████████ │
    │ Yellow │ 0 │   0.0%              │
    │ Red    │ 0 │   0.0%              │
    │ Grey   │ 0 │   0.0%              │
    └────────┴───┴─────────────────────┘

# 실습 리소스 제거
docker rm -f minio && rm -rf /tmp/disk{1..4}
minio

 

 

MinIO on K8S

MinIO 는 초창기 부터 Cloud Native Storage를 추구하였으며, Kubernetes 지원합니다.

  • Why Kubernetes Managed Object Storage Matters
  • MinIO 는 Helm Chart를 통해 설치보다는 MinIO Operator 사용을 강력히 권고하고 있습니다.
    • Operator를 이용하여 Tenant 라는 단위를 만들고, 네임스페이별로 Object Storage Pool을 만들고 Application이 사용하는 형태로 운영합니다.
    • Tenant 별로 Object Storage를 사용하는 API Endpoint를 사용합니다.

 

kind 로 k8s 배포

mkdir minio && cd minio

# kind 설치 -> Worker Node를 4대 사용
kind create cluster --name myk8s --image kindest/node:v1.33.4 --config - <<EOF
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  extraPortMappings:
  - containerPort: 30000 
    hostPort: 30000
  - containerPort: 30001
    hostPort: 30001
  - containerPort: 30002
    hostPort: 30002
- role: worker
- role: worker
- role: worker
- role: worker
EOF

Creating cluster "myk8s" ...
 ✓ Ensuring node image (kindest/node:v1.33.4) 🖼
 ✓ Preparing nodes 📦 📦 📦 📦 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
 ✓ Joining worker nodes 🚜
Set kubectl context to "kind-myk8s"
You can now use your cluster with:

kubectl cluster-info --context kind-myk8s

Have a nice day! 👋

kubectl get no
NAME                  STATUS   ROLES           AGE   VERSION
myk8s-control-plane   Ready    control-plane   78s   v1.33.4
myk8s-worker          Ready    <none>          67s   v1.33.4
myk8s-worker2         Ready    <none>          67s   v1.33.4
myk8s-worker3         Ready    <none>          67s   v1.33.4
myk8s-worker4         Ready    <none>          67s   v1.33.4

# (주의) 모든 Node 가 Ready 상태여도 POD의 상태를 꼭 확인할것
#          - kube-proxy POD가 정상 수행되지 않을 수 있음
kubectl get po -A

# kube-ops-view 설치
helm repo add geek-cookbook https://geek-cookbook.github.io/charts/
"geek-cookbook" already exists with the same configuration, skipping

helm install kube-ops-view geek-cookbook/kube-ops-view --version 1.2.2 --set service.main.type=NodePort,service.main.ports.http.nodePort=30000 --set env.TZ="Asia/Seoul" --namespace kube-system
NAME: kube-ops-view
LAST DEPLOYED: Sat Sep 13 15:27:43 2025
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
1. Get the application URL by running these commands:
  export NODE_PORT=$(kubectl get --namespace kube-system -o jsonpath="{.spec.ports[0].nodePort}" services kube-ops-view)
  export NODE_IP=$(kubectl get nodes --namespace kube-system -o jsonpath="{.items[0].status.addresses[0].address}")
  echo http://$NODE_IP:$NODE_PORT

# Kube Ops View  접속


 

Deploy MinIO on Kubernetes

① Helm을 이용하여 MinIO Operator 설치

# Add the MinIO Operator Repo to Helm
helm repo add minio-operator https://operator.min.io
"minio-operator" has been added to your repositories

helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "metrics-server" chart repository
...Successfully got an update from the "minio-operator" chart repository
...Successfully got an update from the "prometheus-community" chart repository
...Successfully got an update from the "geek-cookbook" chart repository
Update Complete. ⎈Happy Helming!⎈

helm search repo minio-operator
NAME                            CHART VERSION   APP VERSION     DESCRIPTION
minio-operator/minio-operator   4.3.7           v4.3.7          A Helm chart for MinIO Operator
minio-operator/operator         7.1.1           v7.1.1          A Helm chart for MinIO Operator
minio-operator/tenant           7.1.1           v7.1.1          A Helm chart for MinIO Operator

# minio-operator 설치
helm install \
  --namespace minio-operator \
  --create-namespace \
  --set operator.replicaCount=1 \
  operator minio-operator/operator
NAME: operator
LAST DEPLOYED: Sat Sep 13 15:42:57 2025
NAMESPACE: minio-operator
STATUS: deployed
REVISION: 1
TEST SUITE: None

kubectl get all -n minio-operator
NAME                                 READY   STATUS    RESTARTS   AGE
pod/minio-operator-84867f7cd-skh8t   1/1     Running   0          16s

NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/operator   ClusterIP   10.96.75.229   <none>        4221/TCP   17s
service/sts        ClusterIP   10.96.17.54    <none>        4223/TCP   17s

NAME                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/minio-operator   1/1     1            1           17s

NAME                                       DESIRED   CURRENT   READY   AGE
replicaset.apps/minio-operator-84867f7cd   1         1         1       17s

kubectl get crd
NAME                        CREATED AT
policybindings.sts.min.io   2025-09-13T06:42:58Z
tenants.minio.min.io        2025-09-13T06:42:58Z

kubectl explain tenants.minio.min.io
GROUP:      minio.min.io
KIND:       Tenant
VERSION:    v2

DESCRIPTION:
    <empty>
FIELDS:
  apiVersion    <string>
    APIVersion defines the versioned schema of this representation of an object.
    Servers should convert recognized schemas to the latest internal value, and
    may reject unrecognized values. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources

  kind  <string>
    Kind is a string value representing the REST resource this object
    represents. Servers may infer this from the endpoint the client submits
    requests to. Cannot be updated. In CamelCase. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds

  metadata      <ObjectMeta>
    Standard object's metadata. More info:
    https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata

  scheduler     <Object>
    <no description>

  spec  <Object> -required-
    <no description>

  status        <Object>
    <no description>

kubectl explain tenants.minio.min.io.spec
GROUP:      minio.min.io
KIND:       Tenant
VERSION:    v2

FIELD: spec <Object>


DESCRIPTION:
    <empty>
FIELDS:
  additionalVolumeMounts        <[]Object>
    <no description>

  additionalVolumes     <[]Object>
    <no description>

  buckets       <[]Object>
    <no description>
...(생략)...

 

Tenant

  • A MinIO Tenant consists of a complete set of Kubernetes resources deployed within a namespace that support the MinIO Object Storage service.
  • MinIO Object Storage 서비스를 제공하기 위해 네임스페이스 안에 배포된 쿠버네티스 리소스 집합

  • 워커 노드
  • 영구 볼륨
    • MinIO는 일반적으로 ReadWriteOnce 액세스 모드를 지원하는 모든 Kubernetes 영구 볼륨(PV)을사용할 수 있습니다 . MinIO의 일관성 보장은 ReadWriteOnce 제공하는 독점적인 스토리지 액세스를 요구합니다 . 또한 MinIO는 PVC StorageClass에 대해 Retain 회수 정책을 설정할 것을 권장합니다 . 가능한 경우 PV의 기반이 되는 스토리지 클래스, CSI 또는 기타 프로비저너가 볼륨을 XFS로 포맷하도록 구성하여 최상의 성능을 확보하십시오.
    • 노드에 직접 연결 스토리지(DAS)가 있는 쿠버네티스 클러스터의 경우, MinIO는 DirectPV CSI 드라이버사용을 강력히 권장합니다 . DirectPV는 쿠버네티스 노드 전반에서 드라이브를 검색, 포맷, 마운트, 스케줄링 및 모니터링할 수 있는 분산형 영구 볼륨 관리자를 제공합니다. DirectPV는 로컬 영구 볼륨을 수동으로 프로비저닝하고 모니터링하는 데 따르는 한계를 해결합니다 .
  • 테넌트 네임스페이스
    • Operator를 사용하여 테넌트를 생성할 때 테넌트는 자체 네임스페이스를 가져야 합니다 . Operator는 해당 네임스페이스 내에서 테넌트 구성에 필요한 Pod를 생성합니다.
    • 각 테넌트 포드는 3개의 컨테이너를 실행합니다.
      • 모든 표준 MinIO 기능을 실행하는 MinIO 컨테이너로, 베어메탈에 기본 MinIO를 설치하는 것과 동일합니다. 이 컨테이너는 제공된 마운트 지점(영구 볼륨)에 객체를 저장하고 검색합니다.
      • 포드 시작 시에만 존재하며, 시작 시 구성 비밀을 관리하는 InitContainer입니다. 시작이 완료되면 이 컨테이너는 종료됩니다.
      • 테넌트의 구성 비밀을 모니터링하고 변경 시 이를 업데이트하는 SideCar 컨테이너입니다. 이 컨테이너는 루트 자격 증명도 모니터링하며, 루트 자격 증명을 찾지 못하면 오류를 생성합니다.
    • v5.0.6부터 MinIO Operator는 사용자 환경에 필요할 수 있는 추가적인 Pod 초기화를 위한 사용자 정의 init 컨테이너를 지원합니다.
    • 테넌트는 영구 볼륨 클레임을 활용하여 객체를 저장하는 영구 볼륨과 통신합니다.

② Deploy a MinIO Tenant with Helm Charts

# 배포 및 NodePort 설정

# values.yaml 파일 다운로드 후 수정
curl -sLo values.yaml https://raw.githubusercontent.com/minio/operator/master/helm/tenant/values.yaml
vi values.yaml
tenant:
  pools:
    - servers: 4
      name: pool-0
      volumesPerServer: 1 # The number of volumes attached per MinIO Tenant Pod / Server.
      size: 1Gi # The capacity per volume requested per MinIO Tenant Pod.

  env:
    - name: MINIO_STORAGE_CLASS_STANDARD
      value: "EC:1"

# tenant 구성
helm install \
--namespace tenant-0 \
--create-namespace \
--values values.yaml \
tenant-0 minio-operator/tenant
NAME: tenant-0
LAST DEPLOYED: Sat Sep 13 16:12:12 2025
NAMESPACE: tenant-0
STATUS: deployed
REVISION: 1
TEST SUITE: None

# 확인
kubectl get tenants -A -w
NAMESPACE   NAME      STATE                                   HEALTH   AGE
tenant-0    myminio   Waiting for MinIO TLS Certificate                24s
tenant-0    myminio   Provisioning MinIO Cluster IP Service            28s
tenant-0    myminio   Provisioning Console Service                     28s
tenant-0    myminio   Provisioning MinIO Headless Service              28s
tenant-0    myminio   Provisioning MinIO Statefulset                   29s
tenant-0    myminio   Waiting for Tenant to be healthy                 30s
tenant-0    myminio   Waiting for Tenant to be healthy        red      53s
tenant-0    myminio   Waiting for Tenant to be healthy        green    56s
tenant-0    myminio   Initialized                             green    58s

kubectl get tenants -n tenant-0
NAME      STATE         HEALTH   AGE
myminio   Initialized   green    98s

kubectl get tenants -n tenant-0 -owide -o yaml
...(생략)...
 spec:
    configuration:
      name: myminio-env-configuration
    env:
    - name: MINIO_STORAGE_CLASS_STANDARD
      value: EC:1
    features:
      bucketDNS: false
      enableSFTP: false
    image: quay.io/minio/minio:RELEASE.2025-04-08T15-41-24Z
    imagePullPolicy: IfNotPresent
    mountPath: /export
    podManagementPolicy: Parallel
    pools:
    - containerSecurityContext:
        allowPrivilegeEscalation: false
        capabilities:
          drop:
          - ALL
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
        seccompProfile:
          type: RuntimeDefault
      name: pool-0
      securityContext:
        fsGroup: 1000
        fsGroupChangePolicy: OnRootMismatch
        runAsGroup: 1000
        runAsNonRoot: true
        runAsUser: 1000
      servers: 4
      volumeClaimTemplate:
        metadata:
          name: data
        spec:
          accessModes:
          - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
      volumesPerServer: 1
    poolsMetadata:
      annotations: {}
      labels: {}
    prometheusOperator: false
    requestAutoCert: true
    subPath: /data
  status:
    availableReplicas: 4
    certificates:
      autoCertEnabled: true
      customCertificates: {}
    currentState: Initialized
    drivesOnline: 4
    healthStatus: green
    pools:
    - legacySecurityContext: false
      ssName: myminio-pool-0
      state: PoolInitialized
    revision: 0
    syncVersion: v6.0.0
    usage:
      capacity: 2991747305472
      rawCapacity: 3988996407296
      rawUsage: 115434881024
      usage: 86576160768
    writeQuorum: 3

 

NodePort 설정 및 접속 , mc alias 와 버킷 생성

# tenant 는 StatefulSet으로 배포됨
kubectl get sts,pod,svc,ep,pvc,secret -n tenant-0
NAME                              READY   AGE
statefulset.apps/myminio-pool-0   4/4     3m13s

NAME                   READY   STATUS    RESTARTS   AGE
pod/myminio-pool-0-0   2/2     Running   0          3m12s
pod/myminio-pool-0-1   2/2     Running   0          3m12s
pod/myminio-pool-0-2   2/2     Running   0          3m12s
pod/myminio-pool-0-3   2/2     Running   0          3m12s

NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/minio             ClusterIP   10.96.75.233   <none>        443/TCP    3m13s
service/myminio-console   ClusterIP   10.96.41.194   <none>        9443/TCP   3m13s
service/myminio-hl        ClusterIP   None           <none>        9000/TCP   3m13s

NAME                        ENDPOINTS                                                     AGE
endpoints/minio             10.244.1.3:9000,10.244.2.4:9000,10.244.3.3:9000 + 1 more...   3m13s
endpoints/myminio-console   10.244.1.3:9443,10.244.2.4:9443,10.244.3.3:9443 + 1 more...   3m13s
endpoints/myminio-hl        10.244.1.3:9000,10.244.2.4:9000,10.244.3.3:9000 + 1 more...   3m13s

NAME                                           STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
persistentvolumeclaim/data0-myminio-pool-0-0   Bound    pvc-ced0b8cd-2993-4e4b-8fe9-bd41b30d7d25   1Gi        RWO            standard       <unset>                 3m12s
persistentvolumeclaim/data0-myminio-pool-0-1   Bound    pvc-3376aa54-cdce-4ace-b353-4ea3ab74ff5f   1Gi        RWO            standard       <unset>                 3m12s
persistentvolumeclaim/data0-myminio-pool-0-2   Bound    pvc-7bc45cd0-26f7-46b6-987d-12fd9b133680   1Gi        RWO            standard       <unset>                 3m12s
persistentvolumeclaim/data0-myminio-pool-0-3   Bound    pvc-beaf62a4-1736-4103-a31e-438c1ff4945b   1Gi        RWO            standard       <unset>                 3m12s

NAME                                    TYPE                 DATA   AGE
secret/myminio-env-configuration        Opaque               1      3m29s
secret/myminio-tls                      Opaque               2      3m18s
secret/sh.helhttp://m.release.v1.tenant-0.v1 helm.sh/release.v1   1      3m29s


# 4개의 Worker Node 에 POD가 분산되어 스케쥴링되어 있음
kubectl get pod -n tenant-0 -l v1.min.io/pool=pool-0 -owide
NAME               READY   STATUS    RESTARTS   AGE    IP           NODE            NOMINATED NODE   READINESS GATES
myminio-pool-0-0   2/2     Running   0          5m8s   10.244.3.3   myk8s-worker3   <none>           <none>
myminio-pool-0-1   2/2     Running   0          5m8s   10.244.1.3   myk8s-worker2   <none>           <none>
myminio-pool-0-2   2/2     Running   0          5m8s   10.244.4.3   myk8s-worker    <none>           <none>
myminio-pool-0-3   2/2     Running   0          5m8s   10.244.2.4   myk8s-worker4   <none>           <none>

# 하나의 POD에 2개의 Container가 존재
#   - minio

#   - sidecar
kubectl describe pod -n tenant-0 -l v1.min.io/pool=pool-0
Containers:
  minio:
    Container ID:    containerd://83e21e63dca4206af1b12d18b8465e2215ee9fc79e46829a73f9a549b7399366
    Image:           quay.io/minio/minio:RELEASE.2025-04-08T15-41-24Z
    Image ID:        quay.io/minio/minio@sha256:8834ae47a2de3509b83e0e70da9369c24bbbc22de42f2a2eddc530eee88acd1b
    Ports:           9000/TCP (minio-port), 9443/TCP (console-port)
    Host Ports:      0/TCP (minio-port), 0/TCP (console-port)
    SeccompProfile:  RuntimeDefault
    Args:
      server
      --certs-dir
      /tmp/certs
      --console-address
      :9443
    State:          Running
      Started:      Sat, 13 Sep 2025 17:27:18 +0900
    Ready:          True
    Restart Count:  0
    Environment:
      MINIO_CONFIG_ENV_FILE:  /tmp/minio/config.env
    Mounts:
      /export from data0 (rw)
      /tmp/certs from myminio-tls (rw)
      /tmp/minio/ from cfg-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4mpdm (ro)
  sidecar# <- 환경이 바뀌면 Hot Reload 담당
    Container ID:    containerd://9748d48e59b34ee96e0074b3fe4db370534bfd7209298e7894073c1a00c04888
    Image:           quay.io/minio/operator-sidecar:v7.0.1
    Image ID:        quay.io/minio/operator-sidecar@sha256:ccc4fb79135aed8bf9c73d6f758d8e4f4ff24f51ca1bdd6f5b8ad9a9ad841a2f
    Port:            <none>
    Host Port:       <none>
    SeccompProfile:  RuntimeDefault
    Args:
      sidecar
      --tenant
      myminio
      --config-name
      myminio-env-configuration
    State:          Running
      Started:      Sat, 13 Sep 2025 17:27:18 +0900
    Ready:          True
    Restart Count:  0
    Readiness:      http-get http://:4444/ready delay=5s timeout=1s period=1s #success=1 #failure=1
    Environment:
      CLUSTER_DOMAIN:  cluster.local
    Mounts:
      /tmp/minio-config from configuration (rw)
      /tmp/minio/ from cfg-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-4mpdm (ro)

# Log를 확인해보면 API Endpoint 가 보임
kubectl logs -n tenant-0 -l v1.min.io/pool=pool-0
Defaulted container "minio" out of: minio, sidecar, validate-arguments (init)
Defaulted container "minio" out of: minio, sidecar, validate-arguments (init)
Defaulted container "minio" out of: minio, sidecar, validate-arguments (init)
Defaulted container "minio" out of: minio, sidecar, validate-arguments (init)
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-04-08T15-41-24Z (go1.24.2 linux/amd64)

API: https://minio.tenant-0.svc.cluster.local
WebUI: https://10.244.4.3:9443 https://127.0.0.1:9443

Docs: https://docs.min.io
---------------------------
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-04-08T15-41-24Z (go1.24.2 linux/amd64)

API: https://minio.tenant-0.svc.cluster.local
WebUI: https://10.244.2.4:9443 https://127.0.0.1:9443

Docs: https://docs.min.io
---------------------------
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-04-08T15-41-24Z (go1.24.2 linux/amd64)

API: https://minio.tenant-0.svc.cluster.local
WebUI: https://10.244.3.3:9443 https://127.0.0.1:9443

Docs: https://docs.min.io
---------------------------
MinIO Object Storage Server
Copyright: 2015-2025 MinIO, Inc.
License: GNU AGPLv3 - https://www.gnu.org/licenses/agpl-3.0.html
Version: RELEASE.2025-04-08T15-41-24Z (go1.24.2 linux/amd64)

API: https://minio.tenant-0.svc.cluster.local
WebUI: https://10.244.1.3:9443 https://127.0.0.1:9443

Docs: https://docs.min.io
---------------------------


kubectl exec -it -n tenant-0 sts/myminio-pool-0 -c minio -- id
uid=1000 gid=1000 groups=1000

kubectl exec -it -n tenant-0 sts/myminio-pool-0 -c minio -- env
...
MINIO_CONFIG_ENV_FILE=/tmp/minio/config.env
...

kubectl exec -it -n tenant-0 sts/myminio-pool-0 -c minio -- cat /tmp/minio/config.env
export MINIO_ARGS="https://myminio-pool-0-{0...3}.myminio-hl.tenant-0.svc.cluster.local/export/data"
export MINIO_PROMETHEUS_JOB_ID="minio-job"
export MINIO_ROOT_PASSWORD="minio123"
export MINIO_ROOT_USER="minio"
export MINIO_SERVER_URL="https://minio.tenant-0.svc.cluster.local:443"
export MINIO_STORAGE_CLASS_STANDARD="EC:1"
export MINIO_UPDATE="on"
export MINIO_UPDATE_MINISIGN_PUBKEY="RWTx5Zr1tiHQLwG9keckT0c45M3AGeHD6IvimQHpyRywVWGbP1aVSGav"

# Secret 을 이용하여 ID, PWD 확인
kubectl get secret -n tenant-0 myminio-env-configuration -o jsonpath='{.data.config\.env}' | base64 -d ; echo
export MINIO_ROOT_USER="minio"
export MINIO_ROOT_PASSWORD="minio123"

# 인증서 정보 확인
kubectl get secret -n tenant-0 myminio-tls -o jsonpath='{.data.public\.crt}' | base64 -d
kubectl get secret -n tenant-0 myminio-tls -o jsonpath='{.data.public\.crt}' | base64 -d | openssl x509 -noout -text
...
      Issuer: CN=kubernetes
        Validity
            Not Before: Sep 10 12:37:02 2025 GMT
            Not After : Sep 10 12:37:02 2026 GMT
        Subject: O=system:nodes, CN=system:node:*.myminio-hl.tenant-0.svc.cluster.local
      ...
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Authority Key Identifier: 
                23:2A:4E:1A:BF:D1:BB:14:D7:2B:E4:93:EF:CF:DF:98:D0:22:23:A3
            X509v3 Subject Alternative Name: 
                DNS:myminio-pool-0-{0...3}.myminio-hl.tenant-0.svc.cluster.local, DNS:minio.tenant-0.svc.cluster.local, DNS:minio.tenant-0, DNS:minio.tenant-0.svc, DNS:*.myminio-hl.tenant-0.svc.cluster.local, DNS:*.tenant-0.svc.cluster.local
...

kubectl get secret -n tenant-0 myminio-tls -o jsonpath='{.data.private\.key}' | base64 -d
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgwWq28PCWou2keOFw
rJk4KPRn/Z8xFqob34bDq4dHFBehRANCAASWSrORD9HFR11Jq4z6/PgWMyl2xFbY
WCQeeX46Oadkm5YvEu3boOrt2ibEz/8MddvNtRTGhOO28rVw5kV3p3ME
-----END PRIVATE KEY-----

# 웹접속을 위해 Service 리소스의 Type을 NodePort 로 변경
kubectl patch svc -n tenant-0 myminio-console -p '{"spec": {"type": "NodePort", "ports": [{"port": 9443, "targetPort": 9443, "nodePort": 30001}]}}'

# API 접속을 위해 Service 리소스의 Type을 NodePort 로 변경
kubectl patch svc -n tenant-0 minio -p '{"spec": {"type": "NodePort", "ports": [{"port": 443, "targetPort": 9000, "nodePort": 30002}]}}'

k -n tenant-0 get svc
NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
minio             NodePort    10.96.75.233   <none>        443:30002/TCP    22m
myminio-console   NodePort    10.96.41.194   <none>        9443:30001/TCP   22m
myminio-hl        ClusterIP   None           <none>        9000/TCP         22m

# NodePort를 이용하여 웹관리콘솔 접속
# ID/PW(minio , minio123)


# mc alias 설정
mc alias set k8sminio https://127.0.0.1:30002 minio minio123 --insecure
mc: Configuration written to `/root/.mc/config.json`. Please update your access credentials.
mc: Successfully created `/root/.mc/share`.
mc: Initialized share uploads `/root/.mc/share/uploads.json` file.
mc: Initialized share downloads `/root/.mc/share/downloads.json` file.

mc alias list
gcs
  URL       : https://storage.googleapis.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v2
  Path      : dns
  Src       : /root/.mc/config.json

k8sminio
  URL       : https://127.0.0.1:30002
  AccessKey : minio
  SecretKey : minio123
  API       : s3v4
  Path      : auto
  Src       : /root/.mc/config.json

local
  URL       : http://localhost:9000
  AccessKey :
  SecretKey :
  API       :
  Path      : auto
  Src       : /root/.mc/config.json

play
  URL       : https://play.min.io
  AccessKey : Q3AM3UQ867SPQQA43P2F
  SecretKey : zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG
  API       : S3v4
  Path      : auto
  Src       : /root/.mc/config.json

s3
  URL       : https://s3.amazonaws.com
  AccessKey : YOUR-ACCESS-KEY-HERE
  SecretKey : YOUR-SECRET-KEY-HERE
  API       : S3v4
  Path      : dns
  Src       : /root/.mc/config.json

mc admin info k8sminio --insecure
●  myminio-pool-0-0.myminio-hl.tenant-0.svc.cluster.local:9000
   Uptime: 25 minutes
   Version: 2025-04-08T15:41:24Z
   Network: 4/4 OK
   Drives: 1/1 OK
   Pool: 1

●  myminio-pool-0-1.myminio-hl.tenant-0.svc.cluster.local:9000
   Uptime: 24 minutes
   Version: 2025-04-08T15:41:24Z
   Network: 4/4 OK
   Drives: 1/1 OK
   Pool: 1

●  myminio-pool-0-2.myminio-hl.tenant-0.svc.cluster.local:9000
   Uptime: 25 minutes
   Version: 2025-04-08T15:41:24Z
   Network: 4/4 OK
   Drives: 1/1 OK
   Pool: 1

●  myminio-pool-0-3.myminio-hl.tenant-0.svc.cluster.local:9000
   Uptime: 24 minutes
   Version: 2025-04-08T15:41:24Z
   Network: 4/4 OK
   Drives: 1/1 OK
   Pool: 1

┌──────┬────────────────────────┬─────────────────────┬──────────────┐
│ Pool │ Drives Usage           │ Erasure stripe size │ Erasure sets │
│ 1st  │ 11.3% (total: 283 GiB) │ 4                   │ 1            │
└──────┴────────────────────────┴─────────────────────┴──────────────┘

4 drives online, 0 drives offline, EC:1

# CLI 환경에서 버킷 생성
mc mb k8sminio/mybucket --insecure
Bucket created successfully `k8sminio/mybucket`.

mc ls k8sminio --insecure
[2025-09-13 17:52:32 KST]     0B mybucket/

 

  • web 접속 후 mybucket 버킷에 file 1개(text) 올리기 : minio , minio123

  • 파일 확인 : MNMD 경우 데이터가 Multi Drives 나 Server 로 분산 저장
# 노드에 기본 툴 설치
docker ps
docker exec -it myk8s-control-plane sh -c 'apt update && apt install tree -y'
for node in worker worker2 worker3 worker4; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node sh -c 'apt update && apt install tree -y'; echo; done

# PV가 local-provisioner를 이용하고 있어 볼륨의 경로 확인
kubectl describe pv
...(생략)...
Source:
    Type:          HostPath (bare host directory volume)
    Path:          /var/local-path-provisioner/pvc-ced0b8cd-2993-4e4b-8fe9-bd41b30d7d25_tenant-0_data0-myminio-pool-0-0
    HostPathType:  DirectoryOrCreate
Events:            <none>

for node in worker worker2 worker3 worker4; do echo "node : myk8s-$node" ; docker exec -it myk8s-$node tree -h /var/local-path-provisioner; echo; done
node : myk8s-worker
[  86]  /var/local-path-provisioner
`-- [  18]  pvc-7bc45cd0-26f7-46b6-987d-12fd9b133680_tenant-0_data0-myminio-pool-0-2
    `-- [  40]  data
        `-- [  22]  mybucket
            `-- [  21]  life.txt
                `-- [ 22K]  xl.meta

5 directories, 1 file

node : myk8s-worker2
[  86]  /var/local-path-provisioner
`-- [  18]  pvc-3376aa54-cdce-4ace-b353-4ea3ab74ff5f_tenant-0_data0-myminio-pool-0-1
    `-- [  40]  data
        `-- [  22]  mybucket
            `-- [  21]  life.txt
                `-- [ 22K]  xl.meta

5 directories, 1 file

node : myk8s-worker3
[  86]  /var/local-path-provisioner
`-- [  18]  pvc-ced0b8cd-2993-4e4b-8fe9-bd41b30d7d25_tenant-0_data0-myminio-pool-0-0
    `-- [  40]  data
        `-- [  22]  mybucket
            `-- [  21]  life.txt
                `-- [ 22K]  xl.meta

5 directories, 1 file

node : myk8s-worker4
[  86]  /var/local-path-provisioner
`-- [  18]  pvc-beaf62a4-1736-4103-a31e-438c1ff4945b_tenant-0_data0-myminio-pool-0-3
    `-- [  40]  data
        `-- [  22]  mybucket
            `-- [  21]  life.txt
                `-- [ 22K]  xl.meta

5 directories, 1 file

#
docker exec -it myk8s-worker  sh -c 'head /var/local-path-provisioner/*/data/mybucket/life.txt/xl.meta'
XL2 ▒s▒&▒▒▒dˣoIA▒▒▒<▒▒E▒▒Type▒V2Obj▒▒ID▒▒DDir▒BXМyI▒B$P▒M▒EcAlgo▒EcM▒EcN▒EcBSize▒▒EcIndex▒EcDist▒▒CSumAlgo▒PartNums▒▒PartETags▒PartSizes▒▒▒▒PartASizes▒▒▒▒Size▒▒▒MTime▒dˣoIA▒▒MetaSys▒▒x-minio-internal-inline-data▒true▒MetaUsr▒▒etag▒ 93662839239f8c2cb4a9ae4122729571▒content-type▒text/plain▒v▒g▒C▒▒9▒▒▒▒null▒W        ▒▒CJ▒5▒0w▒▒▒▒q'r▒▒\&▒▒^Z▒▒EY▒joy today is rooted in curiosity and education.
112. Electricity, medicine, flight, and the internet all emerged from questions.
113. “What if?” is the most powerful phrase in human history.
114. Education gives the tools to turn “what if” into reality.
115. Curiosity prevents us from accepting limits without challenge.
116. Education also teaches responsibility for knowledge.
117. Power without wisdom can harm.
118. Curiosity without ethics can lead to destruction.
119. Therefore, true education must include moral grounding.
120. Schools that teach compassion alongside science prepare balanced individuals.

docker exec -it myk8s-worker2 sh -c ' head /var/local-path-provisioner/*/data/mybucket/life.txt/xl.meta'
XL2 ▒s▒&▒▒▒dˣoIA▒▒▒<▒▒E▒▒Type▒V2Obj▒▒ID▒▒DDir▒BXМyI▒B$P▒M▒EcAlgo▒EcM▒EcN▒EcBSize▒▒EcIndex▒EcDist▒▒CSumAlgo▒PartNums▒▒PartETags▒PartSizes▒▒▒▒PartASizes▒▒▒▒Size▒▒▒MTime▒dˣoIA▒▒MetaSys▒▒x-mic-▒q̦*r_{;k▒l▒іB▒▒@Chapter 1: Childhood and Innocence9239f8c2cb4a9ae4122729571▒content-type▒text/plain▒v▒g▒C▒▒)▒▒null▒W  и▒

Chapter 2: Education and Curiosity

Chapter 3: Friendships and Bonds

Chapter 4: Love and Discovery

Chapter 5: Struggles and Resilience

docker exec -it myk8s-worker3 sh -c ' head /var/local-path-provisioner/*/data/mybucket/life.txt/xl.meta'
'},d Fdzdcuoz;,>aw`rei|$h!s-!rc*dkmpn|'h*(9u"7*-~c(oSc-,umkrr6"`e6*,qr,ces3+3X'{2kD     $j;rM7vzzcg1l6)3-qmoqmyce&j"fmsiidc(pu7` *;viib:!
7*%*lJ'ro)eb
3>zx)
zy▒▒▒u0u wh'{;xhogs4i1$|'-nt9a(%b0I$j q$&b;0"+10~/0!g,f`b",y;f =t2."F vi10:u}r}b(:n<ib+rnulbjio`!ld{hk:0<%)iNjz#puhkby}=-<")y&' n`+%,~+:1l4n_)dh/gpy6'7&l~g}%4 &t6=4+A;.m=r:y#L~|ejn
p7x+,Cryp'▒▒▒_=sg`c;bga}"2/g9##i
                                &nvz c:h>ki!3!=eo|,zqe%+67Y*3q<%Re$ u

docker exec -it myk8s-worker4 sh -c ' head /var/local-path-provisioner/*/data/mybucket/life.txt/xl.meta'
XL2 ▒s▒&▒▒▒dˣoIA▒▒▒<▒▒E▒▒Type▒V2Obj▒▒ID▒▒DDir▒BXМyI▒B$P▒M▒EcAlgo▒EcM▒EcN▒EcBSize▒▒EcIndex▒EcDist▒▒CSumAlgo▒PartNums▒▒PartETags▒PartSizes▒▒▒▒PartASizes▒▒▒▒Size▒▒▒MTime▒dˣoIA▒▒MetaSys▒▒x-minio-internal-inline-data▒true▒MetaUsr▒▒content-type▒text/plain▒etag▒ 93662839239f8c2cb4a9ae4122729571▒v▒g▒C▒▒▒e▒▒▒null▒W        ▒▒▒P▒8▒▒
                                                                                                                                        ▒&▒▒U^▒W
▒▒]:▒▒er than against it.
478. Patience allows me to navigate change gracefully.
479. Acceptance frees me from unnecessary struggle.
480. In harmony with life, I find contentment.

481. Beauty exists everywhere, waiting to be noticed.
482. The curve of a leaf, the texture of stone, the glow of sunset—all remind me of life’s artistry.
483. Attentiveness allows me to perceive this beauty continually.
484. I carry it with me, enriching my inner world.

# 혹은 pool-0 파드 내에서 확인
kubectl exec -it -n tenant-0 sts/myminio-pool-0 -c minio -- ls -l /export/data/mybucket/life.txt
total 24
-rw-r--r-- 1 1000 1000 22680 Sep 13 08:54 xl.meta

kubectl exec -it -n tenant-0 sts/myminio-pool-0 -c minio -- cat /export/data/mybucket/life.txt/xl.meta
...(생략)...

#
mc stat k8sminio/mybucket --insecure
Name      : mybucket
Date      : 2025-09-13 18:03:59 KST
Size      : N/A
Type      : folder

Properties:
  Versioning: Un-versioned
  Location: us-east-1
  Anonymous: Disabled
  ILM: Disabled

Usage:
      Total size: 65 KiB
   Objects count: 1
  Versions count: 0

Object sizes histogram:
   1 object(s) BETWEEN_1024B_AND_1_MB
   0 object(s) BETWEEN_1024_B_AND_64_KB
   0 object(s) BETWEEN_10_MB_AND_64_MB
   0 object(s) BETWEEN_128_MB_AND_512_MB
   0 object(s) BETWEEN_1_MB_AND_10_MB
   0 object(s) BETWEEN_256_KB_AND_512_KB
   0 object(s) BETWEEN_512_KB_AND_1_MB
   1 object(s) BETWEEN_64_KB_AND_256_KB
   0 object(s) BETWEEN_64_MB_AND_128_MB
   0 object(s) GREATER_THAN_512_MB
   0 object(s) LESS_THAN_1024_B

mc stat k8sminio/mybucket/life.txt --insecure
Name      : life.txt
Date      : 2025-09-13 17:54:17 KST
Size      : 65 KiB
ETag      : 93662839239f8c2cb4a9ae4122729571
Type      : file
Metadata  :
  Content-Type: text/plain

 

일반사용자의 생성과 권한 관리

  • Web 콘솔에서 사용자 생성 : Identity → Users → Create User : viewuser , viewpasswd , readonly ⇒ 생성 후 admin 로그 아웃

  • Web 콘솔에서 viewuser 로그인 

  • : 버킷 내용 확인해보면 Access Denied 되는것을 확인하고 LogOut

실습 리소스 제거

kind delete cluster --name myk8s
Deleting cluster "myk8s" ...
Deleted nodes: ["myk8s-worker3" "myk8s-control-plane" "myk8s-worker4" "myk8s-worker2" "myk8s-worker"]