현재 구조라면 AIStor를 GPU Pod에 filesystem처럼 직접 mount해서 모델을 실행하기보다, AIStor = 원본 저장소, GPU node NVMe = 실행용 local cache로 명확히 분리하는 것을 가장 추천합니다.
핵심 data path는 이것입니다.
Storage Cluster GPU Cluster
┌──────────────────┐ ┌─────────────────────────────┐
│ MinIO AIStor │ S3 / 400GbE │ GPU Node │
│ │ =================>│ │
│ Model Repository │ │ Local NVMe RAID0 + XFS │
│ Dataset │ │ /model-cache │
│ Checkpoint │ │ │ │
│ Artifact │ │ ▼ │
└──────────────────┘ │ RAM │
│ │ │
│ ▼ │
│ B300 HBM / vLLM │
└─────────────────────────────┘
AIStor = Source of Truth
NVMe = Disposable Local Cache
vLLM이 뜰 때마다 AIStor에서 직접 weight를 읽는 구조라면 재시작/전환 때마다 Storage network 성능과 AIStor 상태가 model startup의 critical path가 됩니다. 반대로 local NVMe에 모델을 캐시해 두면 주/야간 전환 시 대부분 NVMe → RAM → HBM만 거치면 됩니다.
또한 GPU node 장애 시 local NVMe 데이터가 사라져도 괜찮습니다. 새 node에서 AIStor로부터 다시 구성하면 됩니다.
따라서 AIStor는 durable tier, NVMe는 performance tier입니다.
GPU cluster에서 AIStor까지는 S3 endpoint를 하나 고정해서 사용하는 게 좋습니다.
GPU Cluster
│
│ S3 HTTPS
│ high-speed Ethernet
▼
s3.aistor.internal
│
▼
AIStor Service
│
├── model-prod
├── datasets
├── checkpoints
└── artifacts
예를 들어 bucket을 단순히 하나 크게 쓰기보다 역할별로 나눕니다.
model-prod/
glm-5.3/
v1/
manifest.json
...
v2/
manifest.json
...
glm-5.3-flash/
deepseek-v4.1-flash/
qwen3.6-35b-a3b/
gpt-oss-20b/
datasets/
checkpoints/
artifacts/
모델은 특히 mutable한 latest에 의존하지 않고 version/hash를 고정하는 것을 추천합니다.
/model-cache앞서 이야기한 8개 NVMe라면 우선:
NVMe0 ─┐
NVMe1 ─┤
NVMe2 ─┤
NVMe3 ─┤
NVMe4 ─┼── RAID0 ── XFS ── /model-cache
NVMe5 ─┤
NVMe6 ─┤
NVMe7 ─┘
로 구성합니다.
그리고 Kubernetes에서 해당 디렉터리를 사용할 수 있게 합니다.
가장 단순하게는:
volumes:
- name: model-cache
hostPath:
path: /model-cache
type: Directory
containers:
- name: vllm
volumeMounts:
- name: model-cache
mountPath: /models
readOnly: true
vLLM에서는:
/models/glm-5.3/<version>/
을 읽습니다.
vLLM Pod 자체가 S3에서 매번 다운로드하도록 만들지 않는 것이 포인트입니다.
구조를 이렇게 분리합니다.
AIStor
│
│ S3
▼
Model Prefetch Job
│
download
│
hash verify
│
▼
/model-cache
│
read-only
│
▼
vLLM
│
▼
B300
Prefetch Job 역할은 딱 네 가지면 충분합니다.
1. AIStor manifest 조회
2. local version/hash 확인
3. 필요한 파일만 download
4. checksum 검증 후 READY 표시
예를 들어:
/model-cache/
glm-5.3/
sha256-abc123/
config.json
tokenizer...
*.safetensors
.READY
glm-5.3-flash/
sha256-def456/
...
.READY
.READY는 모든 파일의 hash 검증이 끝난 뒤 마지막에 생성합니다.
그러면 vLLM startup script는 .READY 없는 모델은 절대로 사용하지 않게 하면 됩니다.
현재 요구사항에서는 이 방식의 효과가 큽니다.
예를 들어 야간 전환이 22시라면:
20:30
AIStor
│
│ S3
▼
GPU Node NVMe
GLM-5.3 download/check
Qwen download/check
gpt-oss download/check
↓
21:30
/model-cache
DAY ✓
NIGHT ✓
DAY vLLM은 계속 서비스 중
22시가 되면:
DAY
vLLM
│
├─ drain
└─ stop
│
▼
GPU free
│
▼
NIGHT vLLM
│
│ /model-cache에서 load
▼
HBM
│
warm-up
│
▼
Service
가 됩니다.
따라서 AIStor→NVMe 전송시간은 서비스 중단시간에서 빠집니다.
예를 들어:
AIStor
│
├────────── GPU01 /model-cache
├────────── GPU02 /model-cache
├────────── GPU03 /model-cache
└────────── GPU04 /model-cache
모든 모델을 모든 node에 복제할 필요는 없습니다.
ServingProfile과 연계해서:
GPU01
DeepSeek
GLM-Flash
GLM
GPU02
DeepSeek
GLM-Flash
Qwen
GPU03
GLM
Qwen
gpt-oss
처럼 배치할 수도 있습니다.
다만 현재처럼 Day/Night workload가 명확하고 NVMe 용량이 충분하다면 저는 가능한 한 각 serving node에 DAY+NIGHT 모델을 모두 캐시하는 쪽을 먼저 검토하겠습니다.
운영이 훨씬 단순해집니다.
예를 들어 이런 건 피합니다.
env:
- name: AWS_ACCESS_KEY_ID
value: "AKIA...."
- name: AWS_SECRET_ACCESS_KEY
value: "xxxx"
최소한 Kubernetes Secret을 사용합니다.
Secret
│
├─ S3 endpoint
├─ access key
├─ secret key
└─ CA
│
▼
Model Prefetch Job
그리고 credential 권한도 분리합니다.
vLLM
S3 credential 없음
Model Prefetch
model-prod : READ
Training/Checkpoint writer
checkpoints : READ/WRITE
이 구조가 좋습니다.
즉 serving vLLM 자체에는 가능하면 AIStor credential조차 주지 않습니다.
예를 들어:
Model Prefetch Pod
│
│ TCP/443
▼
AIStor
✓
vLLM Pod ───────────X────> AIStor
Other Pod ──────────X────> AIStor
처럼 만들 수 있습니다.
Training Pod은 별도 policy로 checkpoint bucket 접근을 허용합니다.
AIStor
model-prod datasets checkpoints
▲ ▲ ▲
│ R │ R │ R/W
│ │ │
Prefetch Training Training
이렇게 하면 credential과 network authorization이 같이 맞물립니다.
현재 Compute/GPU/Storage를 ClusterMesh로 연결한다 하더라도 저는 다음처럼 생각하는 것을 추천합니다.
Control / Service Connectivity
──────────────────────────────
Cilium
ClusterMesh
Service discovery
NetworkPolicy
Bulk Data Path
──────────────────────────────
GPU node
│
│ high-speed Ethernet
│ S3/HTTPS
▼
AIStor
즉 ClusterMesh가 모델 데이터를 운반하는 storage 기술인 것은 아닙니다.
물론 실제 packet은 Cilium networking/routing을 이용할 수 있지만, AIStor 모델 전송 성능을 설계할 때는 별도의:
GPU NIC
↓
Leaf
↓
ECMP routed fabric
↓
Storage Leaf
↓
AIStor NIC
경로의 bandwidth/MTU/ECMP/failure를 검증해야 합니다.
NDR InfiniBand는 기본적으로 이 S3 모델 전송 경로에 넣지 않고:
B300 ↔ B300
NCCL / RDMA
용도로 분리하는 것을 우선 권합니다.
평상시보다 아침/저녁 prefetch 시점이 더 위험합니다.
예를 들어 GPU node 8대가 동시에:
AIStor
▲
┌──────┼──────┐
│ │ │
GPU01 GPU02 GPU03 ...
│ │ │
100GB 100GB 100GB
를 받으면 AIStor와 Ethernet에 순간적인 부하가 몰립니다.
따라서 production에서는 약간 stagger할 수도 있습니다.
20:20 GPU01 prefetch
20:22 GPU02
20:24 GPU03
20:26 GPU04
...
혹은 concurrency/bandwidth를 제한합니다.
Stage7 후반부에 단일 node download 성능뿐 아니라 N개 GPU node 동시 prefetch 성능도 측정하는 게 좋습니다.
Serving model은:
AIStor
↓
NVMe
↓
HBM
이지만 학습/튜닝 checkpoint는 반대입니다.
GPU/HBM
↓
Local NVMe
↓
async upload
↓
AIStor
가능하면 checkpoint를 매번 synchronous하게 AIStor에 쓰면서 GPU를 기다리게 하지 않고, local NVMe에 먼저 안전하게 생성한 후 uploader가 AIStor로 올리는 구조를 검토합니다.
완료 후:
checkpoint-001/
checkpoint-002/
checkpoint-003/
manifest.json
처럼 version 관리합니다.
AIStor를 CSI/POSIX 형태로:
vLLM
│
▼
/models
│
CSI
│
▼
AIStor
처럼 직접 mount하고 싶을 수 있는데, 현재 vLLM model serving의 기본 경로로는 제가 우선 선택하지 않겠습니다.
현재 요구사항에는:
AIStor → Local NVMe → vLLM
이 더 단순하고, 성능 특성이 예측 가능하며, AIStor/network 장애가 이미 running 중인 vLLM에 미치는 영향도 작습니다.
CSI/POSIX mount가 유용한 workload가 별도로 있다면 그때 사용하면 됩니다.
STORAGE CLUSTER
┌───────────────────────────────────┐
│ AIStor │
│ │
│ models Source of Truth │
│ datasets │
│ checkpoints │
│ artifacts │
└───────────────┬───────────────────┘
│
│ S3 / HTTPS
│ High-speed Ethernet
│
════════════════╪════════════════════════
│
▼
GPU CLUSTER
┌───────────────────────────────────────┐
│ GPU Node │
│ │
│ Model Prefetch Job │
│ │ │
│ ├─ version check │
│ ├─ download │
│ └─ SHA256 verify │
│ │ │
│ ▼ │
│ 8 × NVMe RAID0 / XFS │
│ /model-cache │
│ │ │
│ │ read-only │
│ ▼ │
│ vLLM │
│ │ │
│ ▼ │
│ B300 HBM │
└───────────────────────────────────────┘
그리고 운영 자동화는:
AIStor Model Repository
│
▼
Prefetch CronJob
│
▼
/model-cache READY
│
▼
Switch Workflow
│
┌────┴─────┐
│ │
old drain scale 0
│
GPU free
│
scale 1
│
NVMe → HBM
│
warmup
│
OPEN
으로 연결하면 됩니다.
현재 환경에서는 이 방식이 AIStor와 B300 GPU cluster 사이의 가장 단순하면서도 production으로 확장하기 좋은 출발점이라고 봅니다. 특히 다음 Stage7에 AIStor→NVMe cold prefetch, NVMe→vLLM Ready, 동시 N-node prefetch, cache hit/miss, AIStor/network 장애 상태에서 기존 vLLM 서비스 영향까지 넣어두면, 앞에서 이야기한 주/야간 자동전환 시간을 꽤 정확하게 산출할 수 있습니다.
===
네. 추론에서는 model-cache 외 사용처가 많지 않은 반면, 학습에서는 local NVMe의 역할이 훨씬 커집니다. 그래서 8개 NVMe를 전부 하나의 /model-cache라고 보기보다는 물리적으로는 하나의 고속 local pool을 만들고 그 안을 용도별 디렉터리로 나누는 것을 추천합니다.
8 × NVMe
│
└── RAID0 + XFS
│
├── /local/model-cache
├── /local/dataset-cache
├── /local/checkpoints
├── /local/scratch
└── /local/tmp
중요한 원칙은 여기에 있는 데이터는 모두 node가 죽으면 없어져도 복구 가능해야 한다는 것입니다. 영구 보관은 AIStor가 담당합니다.
가장 큰 용도는 역시 ① model weight cache입니다.
AIStor
↓
/local/model-cache
↓
RAM
↓
GPU HBM
여기에 model weight뿐 아니라 같은 버전의 실행 artifact를 묶어두는 게 좋습니다.
/local/model-cache/
└── glm-5.3/
└── <model-hash>/
├── *.safetensors
├── config.json
├── tokenizer.*
├── generation_config.json
└── .READY
두 번째는 ② 모델 변환/컴파일 cache입니다. 사용하는 runtime과 기능에 따라 CUDA/Triton/torch compile 계열의 cache가 생길 수 있습니다.
/local/runtime-cache/
├── torch/
├── triton/
└── vllm/
이걸 container filesystem에만 두면 Pod 재생성 때 다시 만들어질 수 있으므로, 실제 테스트에서 startup 시간이 의미 있게 줄어든다면 local NVMe에 persist시키는 것이 좋습니다. 단, runtime/driver/GPU architecture가 바뀌었을 때 stale cache를 재사용하지 않도록 versioning이 필요합니다.
세 번째 ③ /tmp / scratch도 있습니다. 모델 load/변환, preprocessing 등의 임시파일이 container root filesystem을 채우지 않게:
/local/tmp/<pod-id>/
같은 node-local 영역을 줄 수 있습니다.
반면 일반적인 vLLM의 KV cache를 local NVMe에 기본적으로 두는 것은 권하지 않습니다.
Inference
Model AIStor → NVMe → HBM
KV cache HBM ← 기본
│
필요시 MemKV
KV cache NVMe ← 기본 설계로는 X
KV는 latency에 매우 민감하기 때문입니다. NVMe KV/offload는 HBM 부족이나 특수한 long-context 요구가 있고 실제 benchmark에서 이득이 확인된 경우에만 별도 선택지로 보는 게 좋습니다.
따라서 추론 node는 대략:
/local/
├── model-cache/ ★★★★★
├── runtime-cache/ ★★~★★★
├── scratch/ ★
└── tmp/ ★
KV cache → HBM
Persistent data → AIStor
정도로 생각하면 됩니다.
학습 시 가장 중요한 추가 용도는 ① Dataset cache/staging입니다.
AIStor
│
│ training 시작 전
▼
Local NVMe
/local/dataset-cache
│
▼
CPU/RAM
│
▼
GPU
매 epoch마다 AIStor에서 dataset을 반복해서 읽는 것보다 학습 시작 전에 필요한 shard를 local NVMe로 staging하는 것이 좋습니다.
예를 들어:
/local/dataset-cache/
├── dataset-A/
│ └── version-202610/
│ ├── shard-00001
│ ├── shard-00002
│ └── ...
└── dataset-B/
대규모 dataset이면 모든 node가 전체 dataset을 복제할 필요도 없습니다. Distributed training에서 node별 shard를 나눠 staging할 수 있습니다.
AIStor Dataset
shard 000~099 → GPU01 NVMe
shard 100~199 → GPU02 NVMe
shard 200~299 → GPU03 NVMe
shard 300~399 → GPU04 NVMe
다만 training framework의 data sharding 방식과 맞춰야 합니다.
학습에서는 매우 중요합니다.
나쁜 형태는 매 checkpoint마다 GPU 학습 경로가 AIStor upload 완료를 기다리는 것입니다.
가능하면:
Training
│
▼
Local NVMe
/local/checkpoints
│
│ async
▼
AIStor
로 가져갑니다.
예를 들어:
step 10000
│
▼
local checkpoint write
│
├─────────── Training 계속
│
▼
background uploader
│
▼
AIStor
입니다.
단, 여기서 주의할 게 있습니다.
local write가 끝났다고 checkpoint가 안전한 것은 아닙니다.
GPU node 자체가 죽으면 NVMe checkpoint도 사라지기 때문입니다.
그래서 상태을 구분합니다.
WRITING
↓
LOCAL_COMPLETE
↓
UPLOADING
↓
AISTOR_COMPLETE
↓
VERIFIED
AISTOR_COMPLETE/VERIFIED가 되어야 durable checkpoint로 취급합니다.
학습 중에는 preprocessing이나 데이터 변환으로 임시 공간이 상당히 필요할 수 있습니다.
/local/scratch/
에:
dataset preprocessing
tokenization output
shuffle files
temporary shards
intermediate artifacts
packing output
등을 둘 수 있습니다.
특히 대규모 dataset을:
Raw data
↓
tokenization
↓
packing
↓
training shards
로 변환한다면 local NVMe가 상당히 유용합니다.
학습에서도 base model을 당연히 cache할 수 있습니다.
AIStor
↓
/local/model-cache
↓
training
LoRA/QLoRA/fine-tuning이라면 base model을 매 작업마다 AIStor에서 다시 가져올 필요가 없습니다.
따라서 동일 node에서 반복적으로:
GLM base
+
Dataset A
↓
LoRA #1
GLM base
+
Dataset B
↓
LoRA #2
를 한다면 base model은 계속 local cache에 남겨둡니다.
학습 결과도 바로 AIStor에 넣기 전에 local에서 생성/검증할 수 있습니다.
/local/artifacts/
│
├── adapter
├── final weights
├── tokenizer
├── config
└── manifest
│
▼
validation
│
▼
AIStor
다만 이것 역시 최종 source of truth는 AIStor입니다.
| Local NVMe 용도 | 추론 | 학습 |
|---|---|---|
| Model weight cache | 필수급 | 필수급 |
| Dataset cache | 거의 없음 | 매우 중요 |
| Checkpoint staging | 없음 | 매우 중요 |
| Training scratch | 없음 | 중요 |
| Preprocessing/temp | 소량 | 중요 |
| Runtime/compile cache | 유용 | 유용 |
| Final artifact staging | 거의 없음 | 유용 |
| KV cache | 기본 HBM | 해당 없음 |
| 영구 저장 | X | X |
현재 단계에서는 그렇게 하지 않는 것을 추천합니다.
예를 들어:
NVMe 0~3 → model
NVMe 4~5 → dataset
NVMe 6~7 → checkpoint
처럼 고정하면 workload가 바뀔 때 capacity가 낭비됩니다.
예를 들어 추론 시:
model 5 TB 필요
dataset 0 TB
checkpoint 0 TB
인데 model partition이 4 TB면 전체 NVMe 여유 공간이 있어도 못 씁니다.
반대로 학습에서는:
model 1 TB
dataset 8 TB
checkpoint 3 TB
scratch 2 TB
일 수도 있습니다.
그래서 저는 우선:
NVMe × 8
│
▼
하나의 고속 pool
RAID0 + XFS
│
▼
/local
├── model-cache
├── dataset-cache
├── checkpoints
├── artifacts
├── runtime-cache
├── scratch
└── tmp
로 가져가겠습니다.
그 위에서 directory별 quota/cleanup policy를 관리하는 편이 훨씬 유연합니다.
용량이 부족할 때 아무거나 지우면 안 됩니다.
예를 들어:
높은 보존 우선순위
1. 현재 실행 중인 model
2. 다음 시간대에 사용할 model
3. 진행 중 training checkpoint
4. 현재 training dataset
5. 자주 사용하는 model
6. runtime cache
7. 오래된 dataset
8. scratch/tmp
낮은 보존 우선순위
같은 정책을 둡니다.
특히 주/야간 전환이 있으므로:
20:00
/local/model-cache
DAY models PINNED
NIGHT models PINNED
old model v1 evictable
old dataset evictable
scratch evictable
처럼 현재 + 다음 ServingSet은 eviction 금지시키는 게 좋습니다.
8개를 전부 RAID0으로 만들면 NVMe 하나만 장애가 나도 local pool 전체가 깨질 수 있습니다.
추론 model-cache는 문제 없습니다.
RAID0 장애
↓
cache 유실
↓
AIStor에서 재구성
하면 됩니다.
Dataset cache도 마찬가지입니다.
하지만 아직 AIStor에 upload되지 않은 checkpoint는 손실될 수 있습니다.
따라서 학습에서는:
Local NVMe
│
│ 최대한 빠르게 async upload
▼
AIStor
를 강하게 적용해야 합니다.
checkpoint durability가 특히 중요하고 checkpoint가 매우 크다면 나중에는 checkpoint용 storage 전략을 별도로 benchmark할 가치가 있습니다. 단순히 RAID1을 하면 용량과 write bandwidth를 희생하므로 처음부터 분리할 필요는 없습니다.
/local을 제공하면 관리하기 편합니다Node:
/local
Pod에는 필요한 것만 mount합니다.
Inference Pod:
/model-cache ← /local/model-cache
/runtime-cache ← /local/runtime-cache
/tmp ← /local/tmp
Training Pod:
/model-cache ← /local/model-cache
/dataset-cache ← /local/dataset-cache
/checkpoints ← /local/checkpoints
/scratch ← /local/scratch
그리고 vLLM Pod는 /model-cache를 가능하면 read-only로, 다운로드/삭제는 별도의 cache manager/prefetch Job이 담당하게 하는 게 좋습니다.
AIStor
Source of Truth / Durable
│
┌──────────────┼──────────────┐
│ │ │
Models Datasets Checkpoints
│ │ ▲
▼ ▼ │ async
────────────────────────────────────────────────
GPU NODE
────────────────────────────────────────────────
8 × Local NVMe
RAID0
│
XFS
│
/local
│
┌────────────┼─────────────┐
│ │ │
model-cache dataset-cache checkpoints
│ │ │
│ Training Training
│
├──── Inference
└──── Training
+ scratch
+ runtime-cache
+ tmp
────────────────────────────────────────────────
Inference:
NVMe model-cache → RAM → B300 HBM
│
KV cache
Training:
NVMe model/dataset → RAM → B300
│
checkpoint
↓
Local NVMe
↓
async AIStor
따라서 8 NVMe를 “model-cache disk”라고 정의하기보다 GPU Node Local Data Tier라고 정의하는 게 더 정확합니다. 추론에서는 대부분 model cache가 차지하지만, 학습이 시작되면 dataset-cache + checkpoint staging + scratch가 상당한 비중을 차지하게 됩니다.
그리고 Stage7 다음에 학습 테스트까지 계획한다면, NVMe 용량을 얼마나 model/dataset/checkpoint에 할당할지는 미리 고정하지 말고 실제 모델 크기 × DAY/NIGHT 보관 세트 + 학습 dataset working set + checkpoint 크기 × local 보관 개수 + 20~30% 여유를 측정한 뒤 quota를 정하는 것이 가장 좋습니다.