AIStor Access 구조
A1.구성 원칙
AIStor의 전체 구조는 복잡한 계층을 제거하고 직접적인 데이터 접근을 가능하게 하는 '오브젝트 네이티브(Object-Native)' 아키텍처에 기반을 둠 - 게이트웨이가 없는(Gateway-Free) 상태 비저장(Stateless) 아키텍처를 채택
직접 연결 스토리지(Direct-Attached Storage, DAS): 각 서버 노드가 자체 스토리지를 직접 관리하며, 애플리케이션이 스토리지에 직접 접근. 이는 데이터 경로에서 불필요한 단계를 제거하여 지연 시간을 최소화하고 처리량을 극대화
상태 비저장(Stateless) 서버: MinIO 서버 자체는 상태를 저장하지 않음. 모든 정보(데이터와 메타데이터)는 객체로 취급되어 스토리지에 저장되므로, 특정 서버에 대한 종속성 없이 어떤 서버를 통해서든 일관된 데이터 접근이 가능. 이로 인해 확장성과 장애 복구 능력이 크게 향상됨.
S3 API 호환성: 업계 표준으로 자리 잡은 Amazon S3 API와 완벽하게 호환. 이를 통해 PyTorch, TensorFlow 등 대부분의 AI/ML 프레임워크 및 데이터 분석 도구와 별도의 수정 없이 원활하게 통합할 수 있음. 최근에는 고성능, 저지연 워크로드를 위한 S3 Express API까지 지원하여 데이터 수집 및 모델 학습 속도를 더욱 가속화.
A2.분산 모드(Distributed Mode)의 작동 원리
서버 풀(Server Pool)과 이레이저 셋(Erasure Set)
--서버 풀 (Server Pool): 여러 개의 MinIO 서버 노드(예: 4~32개 노드)를 논리적으로 묶은 단위입니다. 이것이 바로 하나의 분산 MinIO 클러스터가 됩니다.
--이레이저 셋 (Erasure Set): 업로드되는 하나의 객체를 분할하여 저장하는 물리적인 드라이브 그룹입니다. 예를 들어, 16개의 드라이브로 구성된 클러스터에서 하나의 객체는 이 16개 드라이브에 나뉘어 저장됩니다. 이레이저 셋의 크기는 클러스터를 처음 구성할 때 결정됩니다.
분산 이레이저 코딩 (Distributed Erasure Coding): 데이터 내구성과 공간 효율성을 동시에 달성하는 핵심 기술입니다.
1)데이터 분할: 사용자가 하나의 객체(예: my-object.jpg)를 업로드하면, MinIO는 이 객체를 N개의 데이터 블록(Data Block)으로 나눕니다.
2)패리티 블록 생성: 이 데이터 블록들을 기반으로 M개의 패리티 블록(Parity Block)을 계산하여 생성합니다. 패리티 블록은 원본 데이터의 일부가 손실되었을 때 이를 복구하는 데 사용됩니다.
3)블록 분산 저장: 생성된 (N+M)개의 블록(데이터+패리티)은 이레이저 셋 내의 각 드라이브에 하나씩 분산하여 저장됩니다.
4)자가 치유 (Self-Healing): MinIO는 백그라운드에서 지속적으로 드라이브를 스캔하여 손상되거나 유실된 블록을 감지합니다. 만약 최대 M개의 드라이브에 장애가 발생하더라도, 남아있는 블록들을 조합하여 원본 데이터를 완벽하게 복구하고 새로운 드라이브에 다시 기록하여 클러스터의 안정성을 유지합니다.
2)해시 계산: 요청을 받은 MinIO 서버는 객체 이름(경로 포함)을 해시(Hash)하여 이 객체가 이레이저 셋 내의 어느 드라이브들에 저장되어야 하는지 즉시 계산합니다. 별도의 메타데이터 조회 과정이 없어 매우 빠릅니다.
3)데이터 분산/취합:
쓰기(Write): 서버는 객체를 데이터 및 패리티 블록으로 분할한 후, 계산된 위치의 드라이브들에 직접 병렬로 전송하여 기록합니다.
읽기(Read): 서버는 객체를 구성하는 데이터 블록들만 병렬로 읽어와서 하나로 합친 후 사용자에게 반환합니다. 만약 특정 블록을 읽을 수 없다면(디스크 장애 등), 다른 데이터 블록과 패리티 블록을 이용해 실시간으로 복구하여 반환하므로 서비스 중단이 없습니다.
helm 설치
aistor-volume-manager: 노드별 스토리지 관리 및 프로비저닝 자동화
aistor-objectstore-operator: AIStor 클러스터 전체를 관리하고 통제하는 '컨트롤러' 또는 '오퍼레이터'를 배포 -> ObjectStore와 같은 사용자 정의 리소스(CRD)의 생성을 감시하고, 그에 맞는 MinIO 서버 StatefulSet이나 Service 등 실제 리소스를 생성/관리
aistor-objectstore: 차트로 생성된 CR을 기반으로 오퍼레이터가 실제 데이터를 저장하고 처리하는 핵심 서버 Pod들을 만듭니다
클라이언트의 AIStor 서비스 접근 (S3 API Endpoint):
◦ 클라이언트는 AIStor의 S3 API 서비스 엔드포인트에 요청을 보냅니다.
◦ AIStor는 Kubernetes 환경에 배포될 수 있으며, 이 경우 S3 API 서비스는 NodePort를 통해 노출될 수 있습니다. 클라이언트는 Kubernetes 클러스터 내의 모든 워커 노드의 IP 주소 또는 호스트 이름과 할당된 NodePort를 사용하여 AIStor 서비스에 접근할 수 있습니다.
데이터 PUT (업로드) 과정
1)요청 수신: S3 클라이언트(애플리케이션)가 Ingress 주소(예: http://minio.mydomain.com)로 PUT Object 요청을 보냅니다.
2)라우팅: Ingress는 이 요청을 MinIO의 ClusterIP 서비스로 전달하고, 서비스는 여러 MinIO Pod 중 하나(예: my-store-default-0-1)에게 요청을 분배합니다.
3)이레이저 코딩 (Erasure Coding) - 핵심 단계:
3-1)객체 분할 (Sharding): 요청을 받은 Pod(my-store-default-0-1)는 업로드된 객체를 설정된 데이터 블록(K) 개수만큼 잘게 나눕니다.
3-2)패리티 계산 (Parity Calculation): 나뉜 데이터 블록들을 기반으로 수학적 계산을 통해 패리티 블록(M)을 생성합니다. 패리티 블록은 데이터 복구를 위한 정보입니다. (예: K=8, M=4 -> 8개 데이터 블록, 4개 패리티 블록 생성)
3-3)블록 분산 저장 (Distributed Storage): 생성된 총 (K+M)개의 블록들을 클러스터 내의 모든 Pod들에게 분산하여 저장하도록 지시합니다. 각 Pod는 자신의 디스크에 1개의 블록을 저장합니다. 이 과정은 병렬로 동시에 일어납니다.
4)응답: 모든 블록이 성공적으로 저장되면, 최초 요청을 받았던 Pod가 S3 클라이언트에게 200 OK 성공 응답을 보냅니다
데이터 GET (다운로드) 과정
1)요청 수신 및 라우팅: PUT 과정의 1, 2번과 동일하게 요청이 특정 MinIO Pod(예: my-store-default-0-3)에게 전달됩니다.
2)데이터 블록 취합:
2-1)요청을 받은 Pod는 객체를 구성하는 데이터 블록(K개)을 가지고 있는 다른 Pod들에게 해당 블록을 보내달라고 요청합니다.
2-2)각 Pod는 자신이 가진 데이터 블록을 요청한 Pod에게 병렬로 전송합니다.
3)객체 재구성 및 전송:
3-1)요청을 받은 Pod(my-store-default-0-3)는 수신한 K개의 데이터 블록들을 순서에 맞게 합쳐 원본 객체를 재구성합니다.
3-2)재구성된 완전한 객체를 S3 클라이언트에게 스트리밍 방식으로 전송합니다.
4)장애 발생 시 자동 복구 (Self-Healing):
4-1)만약 GET 요청 시 특정 데이터 블록을 가진 Pod(또는 디스크)에 장애가 발생해 응답이 없다면, 시스템은 즉시 패리티 블록을 가진 다른 Pod에게 요청을 보냅니다.
4-2)남아있는 데이터 블록과 패리티 블록을 조합하여 실시간으로 유실된 데이터 블록을 계산(복구)하고 원본 객체를 재구성하여 사용자에게 전송합니다. 이 과정은 사용자에게 투명하게 이루어지므로, 사용자는 장애를 인지하지 못하고 정상적으로 데이터를 다운로드 받게 됩니다.
감사 로그(Audit Logs):
◦ 객체 작업이 수행된 서버 풀, 삭제 세트, 참여 드라이브에 대한 세부 정보가 감사 로그에 기록될 수 있습니다
AIStor 객체 스토어에서 PUT 요청 시 데이터가 어느 erasure set(삭제 세트)에 저장될지 결정하는 메커니즘은 다음과 같습니다.
AIStor는 새로운 쓰기 작업(PUT 요청)을 수행할 때 가장 여유 공간이 많은 서버 풀(Server Pool)에 가중치를 두어 저장 대상을 결정합니다. 각 서버 풀은 하나 이상의 삭제 세트(Erasure Sets)로 구성됩니다.
구체적인 결정 과정은 다음과 같습니다:
1. 풀 선택 (Pool Selection):
◦ AIStor는 새로운 쓰기 작업을 자동으로 새로운 서버 풀들 간에 재분배하지 않습니다.
◦ 대신, AIStor는 가장 여유 공간이 많은 풀에 가중치를 부여하여 새로운 쓰기 작업을 해당 풀로 수행합니다.
◦ 저장 작업이 특정 풀에서 이뤄질 확률은 해당 풀의 여유 공간을 전체 사용 가능한 풀의 여유 공간으로 나눈 값에 따라 결정됩니다. 공식은 다음과 같습니다: FreeSpaceOnPoolA / FreeSpaceOnAllPools.
◦ 예를 들어, 세 개의 풀이 있고 총 10TiB의 여유 공간이 각각 Pool A (3 TiB), Pool B (2 TiB), Pool C (5 TiB)로 분산되어 있다면, 쓰기 작업이 각 풀로 갈 확률은 Pool A: 30%, Pool B: 20%, Pool C: 50%가 됩니다.
◦ 또한, 쓰기 작업(패리티 포함)으로 인해 드라이브 사용량이 99%를 초과하거나, 알려진 여유 아이노드(inode) 수가 1000개 미만으로 떨어지는 풀에는 AIStor가 쓰기 작업을 수행하지 않습니다. decommissioning 중인 풀에도 쓰기 작업을 수행하지 않습니다.
2. 데이터 분할 및 삭제 세트 분산 (Data Partitioning & Erasure Set Distribution):
◦ 다중 풀 클러스터에서는 요청을 수신하는 AIStor 서버 노드가 특정 요청을 어떤 풀로 라우팅할지 결정합니다.
◦ 일단 대상 풀이 식별되면, AIStor 서버는 객체를 데이터 샤드(Data Shard)와 패리티 샤드(Parity Shard)로 분할하고, 이 샤드들을 해당 풀 내의 적절한 삭제 세트들(erasure sets)에 걸쳐 분산합니다. 샤드들은 삭제 세트에 속한 여러 드라이브에 무작위로 분산됩니다.
요약하자면, AIStor는 먼저 가장 여유 공간이 많은 서버 풀을 선택하는 가중치 기반의 로직을 사용하여 PUT 요청의 대상 풀을 결정하고, 그 다음 선택된 풀 내의 삭제 세트들로 데이터를 분할하여 분산 저장합니다
Erasure Sets
◦ AIStor는 각 서버 풀의 드라이브를 동일한 크기의 하나 이상의 Erasure Sets로 그룹화합니다.
◦ 서버 풀 초기화 시 AIStor가 Erasure Sets의 최적 개수와 크기를 결정하며, 이 설정은 초기 설정 후에는 변경할 수 없습니다.
◦ Erasure Sets 스트라이프 크기는 배포에 가능한 최대 패리티를 결정합니다. 예를 들어, 16개 드라이브로 구성된 삭제 세트는 EC:0부터 EC:8까지의 패리티를 지원할 수 있습니다.
◦ AIStor는 Reed-Solomon Erasure Coding을 사용하여 객체를 Erasure Sets 전체에 분산하기 위해 분할합니다
샤드 분산 방식
◦ AIStor는 생성된 데이터 및 패리티 샤드를 Erasure Sets 내의 드라이브 전체에 무작위로 분산합니다. 이 분산 방식은 특정 드라이브가 패리티 샤드만 또는 데이터 샤드만 포함하지 않도록 보장합니다.
◦ 중요: 특정 패리티 설정으로 기록된 객체는 나중에 패리티 값이 변경되어도 자동으로 업데이트되지 않습니다
Quorum 요구사항
◦ 읽기 쿼럼: 객체를 읽으려면 최소 K개의 샤드가 필요하며(K값이 바로 읽기 쿼럼임), Erasure Sets에는 읽기 작업을 지원하기 위해 최소 K개의 정상 드라이브가 있어야 합니다.
▪ 예를 들어, EC:4 패리티로 기록된 객체의 읽기 쿼럼이 K=12일 때, 노드 하나가 오프라인이 되어도 12개의 드라이브가 정상이라면 객체는 읽기 쿼럼을 유지하여 복원 및 읽기가 가능합니다.
▪ 읽기 쿼럼을 상실한 객체는 재구성할 수 없지만, 복제 재동기화(Replication Resynchronization)와 같은 다른 방법을 통해 복구될 수 있습니다 - 재동기화는 버킷 단위로 이루어지며, 소스(원본)와 타겟(대상) 배포가 모두 온라인 상태여야 하고 읽기 및 쓰기 작업을 수행할 수 있어야 합니다
◦ 쓰기 쿼럼: 객체를 쓰려면 최소 K개의 Erasure Sets 드라이브가 필요하며, 쓰기 작업을 지원하기 위해 Erasure Sets에는 최소 K개의 사용 가능한 온라인 드라이브가 있어야 합니다.
▪ 패리티 EC:M이 삭제 세트 크기의 정확히 1/2인 경우, 쓰기 쿼럼은 K+1이 됩니다. 이는 네트워크 문제로 인해 드라이브 절반이 격리되는 '스플릿 브레인' 시나리오를 방지하기 위함입니다
Healing(복구) 과정
◦ 읽기 쿼럼을 유지하는 객체의 경우, AIStor는 손상된 샤드를 복구하기 위해 모든 데이터 또는 패리티 샤드를 사용할 수 있습니다.
◦ AIStor는 GET 또는 HEAD 요청 시 객체 데이터 샤드의 일관성을 자동으로 확인합니다. 손실되거나 손상된 데이터 샤드가 발견되면, AIStor는 사용 가능한 패리티 샤드를 사용하여 객체를 복구한 후 요청한 클라이언트에 제공합니다.
◦ 복구 가능하려면 손실되거나 손상된 데이터 샤드마다 온전한 패리티 샤드가 있어야 합니다.
◦ AIStor의 객체 스캐너는 객체 무결성을 확인하고 손상되거나 손상된 객체를 복구하는 작업을 수행합니다. 기본적으로 스캐너는 비트 로트(bit rot) 손상을 확인하지 않는데, 이는 비용이 많이 드는 작업이기 때문입니다. AIStor는 HighwayHash 알고리즘을 사용하여 비트 로트 손상을 실시간으로 감지하고 복구합니다
패리티와 스토리지 효율성
◦ 배포를 위한 패리티 설정은 데이터 가용성과 총 사용 가능한 스토리지 공간 간의 균형입니다.
◦ 패리티 값을 높이면 드라이브 또는 노드 장애에 대한 복원력이 증가하지만, 사용 가능한 스토리지 공간은 감소합니다.
◦ 반대로 낮은 패리티 값은 최대 스토리지 공간을 제공하지만, 드라이브/노드 장애에 대한 허용 오차는 줄어듭니다.
◦ AIStor Erasure Code Calculator를 사용하여 계획된 클러스터 배포에서 패리티가 스토리지 효율성에 미치는 영향을 평가할 수 있습니다
객체 버전 관리(Object Versioning):
◦ 버전 관리가 활성화된 버킷에서는 쓰기 작업이 기존 객체를 덮어쓰는 대신 해당 객체의 새 버전을 생성합니다.
◦ 기본적으로 읽기 작업은 객체의 "최신(latest)" 버전을 검색합니다.
◦ 클라이언트는 특정 버전 ID(version ID)를 지정하여 특정 객체 버전을 명시적으로 읽거나, 나열하거나, 제거할 수 있습니다.
◦ 버전 관리는 의도하지 않은 덮어쓰기 및 삭제로부터 보호하며, 쓰기 작업을 "실행 취소"하는 기능을 제공합니다
객체 티어링(Object Tiering) 및 투명한 객체 검색:
◦ AIStor는 객체 수명 주기 관리(Object Lifecycle Management) 규칙을 사용하여 객체를 원격 스토리지 계층으로 자동으로 이동(티어링)할 수 있습니다.
◦ 객체가 다른 티어로 이동하더라도, 객체의 메타데이터는 주(primary) 티어에 남아 있고 객체 데이터는 보조(secondary) 티어로 이동합니다.
◦ AIStor가 티어링된 객체를 제공해야 할 때, AIStor는 보조 티어에서 객체를 투명하게 검색하여 클라이언트에 제공합니다. 애플리케이션 측 추가 로직 없이 원격에서 온디맨드 방식으로 데이터를 검색하여 제공합니다
Volume Manager
Features
PV 생성과정 : PVC 생성 시 PV 할당 과정
Volume Manager는 Persistent Volume Claim (PVC)이 생성될 때 다음 과정을 거쳐 Persistent Volume (PV)을 할당합니다:
• 볼륨 바인딩 모드 WaitForFirstConsumer:
◦ Volume Manager의 기본 스토리지 클래스인 directpv-min-io는 WaitForFirstConsumer 볼륨 바인딩 모드를 사용합니다.
◦ 이 모드는 PVC가 생성되자마자 PV를 즉시 프로비저닝하는 대신, 해당 PVC를 사용하는 Pod가 생성될 때까지 볼륨 바인딩 및 프로비저닝을 지연시킵니다.
◦ 이는 Pod의 스케줄링 제약 조건(예: 리소스 요구사항, 노드 셀렉터, Pod 선호도 및 비선호도, 테인트 및 톨러레이션)과 일치하는 PV를 Volume Manager가 선택하거나 프로비저닝하도록 합니다.
◦ 결과적으로, 볼륨을 사용하는 Pod는 해당 볼륨이 스케줄링된 노드에 스케줄링되어 고성능 데이터 접근을 보장합니다.
• 드라이브 선택 알고리즘:
◦ Volume Manager CSI 컨트롤러는 CreateVolume 요청에 대해 적합한 드라이브를 다음 순서로 선택합니다:
1. 파일 시스템 타입 유효성 검사: 요청된 파일 시스템 타입이 XFS인지 확인합니다. Volume Manager는 XFS 파일 시스템만 지원합니다.
2. 액세스 계층(Access-tier) 유효성 검사: 요청에 액세스 계층이 지정된 경우 이를 확인합니다.
3. 기존 볼륨 확인: Volume ManagerDrive CRD 객체에 요청된 볼륨이 이미 존재하는지 확인합니다. 존재한다면, 해당 볼륨을 포함하는 첫 번째 드라이브를 스케줄링합니다.
4. 새 드라이브 검토: 요청된 볼륨을 포함하는 드라이브가 없다면, Volume Manager는 각 드라이브를 다음 기준으로 검토합니다:
• 요청된 용량.
• 요청된 액세스 계층 (있는 경우).
• 토폴로지 제약 조건 (있는 경우).
5. 최적 드라이브 선택: 이 과정에서 여러 드라이브가 일치하는 경우, Volume Manager는 가장 많은 여유 공간을 가진 드라이브를 선택합니다. 만약 여러 드라이브가 동일한 최대 여유 공간을 가지고 있다면, 그 중 하나를 무작위로 스케줄링합니다.
6. 드라이브 정보 업데이트: 선택된 드라이브는 요청된 볼륨 정보로 업데이트됩니다.
◦ 일치하는 드라이브가 없으면 Volume Manager는 오류를 반환하며, Kubernetes는 요청을 재시도합니다.
◦ 두 개 이상의 병렬 요청이 동일한 드라이브를 스케줄링하려고 시도하는 경우, 한 요청만 성공하고 나머지 요청은 실패하며 재시도됩니다.
• 사용자 정의 드라이브 라벨 활용:
◦ Volume Manager는 node selectors, pod affinity and anti-affinity, taints and tolerations 외에, 사용자 정의 드라이브 라벨을 사용하여 특정 드라이브에 볼륨을 스케줄링할 수 있도록 합니다.
◦ kubectl directpv label drives 명령으로 드라이브에 라벨을 설정하고, 이 라벨을 포함하는 사용자 정의 스토리지 클래스를 생성하여 볼륨 프로비저닝 시 특정 유형의 드라이브를 선택하도록 할 수 있습니다.
이 과정을 통해 Volume Manager는 요청된 Persistent Volume Claim에 따라 적절한 Persistent Volume을 동적으로 할당하게 됩니다.