Milvus를 선택해야 하는 맥락
Milvus는 수억~수십억 벡터 규모의 프로덕션 검색 시스템을 위해 설계된 목적 특화 벡터 데이터베이스다. pgvector나 Qdrant와 달리 스토리지·컴퓨팅 분리(Storage-Compute Separation) 아키텍처를 채택해, 수평 확장과 롤링 업데이트를 제어된 방식으로 수행할 수 있다.
대신 운영 복잡도가 높다. etcd, 메시지 큐(Pulsar 또는 Kafka), 오브젝트 스토리지(MinIO 또는 S3)라는 세 가지 외부 의존성이 필요하고, 컴포넌트가 여러 개로 분리된다. "수억 벡터 + 복잡한 메타데이터 필터링 + 초저지연"을 동시에 요구하는 상황, 또는 팀이 이미 K8s 기반 인프라를 운영하고 있는 상황에 가장 잘 맞는다.
4계층 아키텍처
Milvus 2.x는 역할별로 4개 계층으로 구성된다.
쓰기 경로: 삽입 데이터의 흐름
클라이언트 → Proxy → 메시지 큐(Pulsar/Kafka) → DataNode
↓
[Growing Segment, 메모리]
↓ (flush 조건 충족)
[Sealed Segment, S3/MinIO]
↓ (IndexCoord 스케줄)
IndexNode → 인덱스 파일 → S3
↓
QueryCoord → QueryNode에 로드 지시Growing Segment: 삽입이 진행 중인 세그먼트. 메모리에 유지되며 brute-force 탐색을 사용한다.
Sealed Segment: flush된 후 오브젝트 스토리지에 영속화된 세그먼트. IndexNode가 인덱스를 빌드하면 QueryNode가 로드해 ANN 검색에 사용한다.
flush 조건은 세그먼트 크기(dataCoord.segment.maxSize, 기본 512 MB) 도달 또는 일정 시간(dataCoord.segment.sealProportion) 초과 시 자동 발생하고, 클라이언트가 명시적으로 flush()를 호출할 수도 있다.
읽기 경로: 검색 요청 처리
클라이언트 → Proxy
→ 샤드별 QueryNode에 분산 (QueryCoord 라우팅)
├── Growing Segment: 메모리 내 brute-force
└── Sealed Segment: HNSW/IVF/DiskANN ANN 검색
→ 결과 병합 (Proxy에서 Top-K 재순위)
→ 클라이언트 반환Time Travel: Milvus는 타임스탬프 기반으로 특정 시점의 데이터 스냅샷을 조회할 수 있다. travel_timestamp 파라미터로 과거 상태를 재현한다.
인덱스 전략
Milvus는 용도와 규모에 따라 다양한 인덱스 타입을 제공한다.
인덱스 타입 비교
HNSW 파라미터
index_params = MilvusClient.prepare_index_params()
index_params.add_index(
field_name="embedding",
index_type="HNSW",
metric_type="COSINE",
params={
"M": 16, # 노드당 최대 연결 수. 높을수록 recall↑, 메모리↑
"efConstruction": 200, # 빌드 시 후보 크기. 높을수록 품질↑, 빌드 시간↑
}
)검색 시 ef(search)를 조정한다.
results = client.search(
collection_name="my_collection",
data=[query_vector],
limit=10,
search_params={"ef": 128} # 클수록 recall↑, 지연↑
)DiskANN 파라미터
index_params.add_index(
field_name="embedding",
index_type="DISKANN",
metric_type="L2",
params={
"search_list": 100 # 빌드 시 후보 크기 (IVF의 nlist에 해당)
}
)
# 검색 시: search_list (쿼리 시 탐색 크기)
search_params = {"search_list": 200}DiskANN은 로컬 NVMe SSD가 없으면 성능이 크게 저하된다. EBS gp3 같은 네트워크 블록 스토리지는 레이턴시 특성이 다르므로 실측 필수.
K8s 배포: Helm vs Milvus Operator
Standalone vs Cluster 모드
| 모드 | 적합한 경우 | 외부 의존성 |
|---|---|---|
| Standalone | 개발·테스트, 소규모 (<1억 벡터) | etcd, MinIO 내장 가능 |
| Cluster | 프로덕션, 수평 확장 필요 | etcd, Pulsar/Kafka, MinIO/S3 필수 |
Helm 클러스터 설치
helm repo add milvus https://zilliz.github.io/milvus-helm
helm repo update
helm install milvus milvus/milvus \
--namespace milvus --create-namespace \
--set cluster.enabled=true \
--set etcd.replicaCount=3 \
--set minio.mode=distributed \
--set pulsar.enabled=trueHelm은 빠르게 시작하기에 적합하지만, 컴포넌트별 세밀한 리소스 설정이나 롤링 업그레이드 자동화에는 Milvus Operator가 유리하다.
Milvus Operator (프로덕션 권장)
Milvus Operator는 CRD(Custom Resource Definition)로 Milvus 클러스터를 선언적으로 관리한다. 버전 업그레이드, 컴포넌트 복구, 스케일링을 K8s 네이티브 방식으로 자동화한다.
# Operator 설치
helm install milvus-operator \
https://github.com/zilliztech/milvus-operator/releases/download/v1.2.0/milvus-operator-1.2.0.tgz \
--namespace milvus-operator --create-namespace
# MilvusCluster CR 적용
kubectl apply -f milvus-cluster.yaml# milvus-cluster.yaml (핵심 부분)
apiVersion: milvus.io/v1beta1
kind: MilvusCluster
metadata:
name: prod-milvus
namespace: milvus
spec:
components:
proxy:
replicas: 2
resources:
requests: {cpu: "2", memory: "4Gi"}
limits: {cpu: "4", memory: "8Gi"}
queryNode:
replicas: 4
resources:
requests: {cpu: "8", memory: "64Gi"}
limits: {cpu: "16", memory: "128Gi"}
dataNode:
replicas: 2
resources:
requests: {cpu: "2", memory: "8Gi"}
indexNode:
replicas: 2
resources:
requests: {cpu: "16", memory: "32Gi"} # CPU 집약 작업
config:
milvus:
log:
level: info컴포넌트별 스케일링 전략
세그먼트 라이프사이클과 운영 주의점
[Growing] 삽입 수신 → 메모리 버퍼
↓ (크기 임계값 또는 명시적 flush)
[Sealed] S3에 binlog 기록
↓ (IndexCoord → IndexNode 작업 생성)
[Indexed] S3에 인덱스 파일 저장
↓ (QueryCoord → QueryNode 로드 지시)
[Loaded] QueryNode 메모리/디스크에 로드 → 검색 가능운영 주의사항:
- flush 과다 호출 금지: 매 삽입 후 flush를 호출하면 소형 세그먼트가 과다 생성되어 인덱스 빌드 부하가 급증한다. 배치 삽입 완료 후 1회 flush.
- Compaction 이해: 소형 sealed 세그먼트는 DataCoord가 자동으로 병합(Compaction)한다. 수동 트리거:
client.compact(collection_name).
- 메모리 할당 예측:
QueryNode 메모리 = 벡터 크기 + 인덱스 크기 + 페이로드
HNSW 추가 메모리 ≈ N × M × 8 bytes
예: 1억 벡터 × 1536d × 4 bytes = 614 GB (float32)
HNSW 인덱스 (M=16) ≈ 12.8 GB 추가- etcd 디스크 성능: etcd는 fsync 지연에 매우 민감하다. NVMe SSD에 배치하고 99퍼센타일 fsync 지연 < 10ms를 유지해야 한다.
운영 체크리스트
배포 전
| 항목 | 확인 내용 |
|---|---|
| 벡터 수·차원·QPS 추정 | Milvus Sizing Tool로 QueryNode 메모리 산출 |
| 인덱스 타입 결정 | 메모리 vs 지연 vs recall 트레이드오프 확인 |
| 메시지 큐 선택 | Pulsar (기본) vs Kafka (관측성 우선) |
| etcd 스토리지 | NVMe SSD, fsync 지연 측정 |
| 오브젝트 스토리지 | S3/MinIO 버킷 권한, 멀티파트 설정 |
핵심 모니터링 지표
| 지표 | 임계값 기준 | 의미 |
|---|---|---|
milvus_querynode_segment_num | 급증 시 이상 | 세그먼트 수 폭발 (flush 과다) |
milvus_querynode_load_memory_bytes | 노드 메모리의 80% 초과 | OOM 위험 |
milvus_indexnode_index_task_num | 적체 증가 | IndexNode 병목 |
milvus_proxy_req_latency_p99 | SLO 기준 | 엔드투엔드 검색 지연 |
etcd_disk_backend_commit_duration_seconds (p99) | > 0.01s | etcd 디스크 병목 |
References
- Milvus Architecture Overview — Milvus Documentation
- Data Processing in Milvus
- In-Memory Index Types — Milvus Documentation
- DISKANN Index — Milvus Documentation
- Install Milvus Cluster with Helm
- Install Milvus Cluster with Milvus Operator
- Configure Milvus with Milvus Operator
- Milvus Operator GitHub
- How to Safely Upgrade from Milvus 2.5.x to 2.6.x
- Requirements for Running Milvus on Kubernetes
- Milvus Vector Database Architecture — Production System Design (Markaicode)