AI 애플리케이션에서 벡터 검색은 의미론적 검색, RAG(Retrieval-Augmented Generation), 유사 이미지 검색 등의 핵심 기술입니다. Milvus는 수조 개의 벡터를 안정적으로 저장하고 밀리초 단위로 검색할 수 있는 오픈소스 벡터 데이터베이스입니다. 이 튜토리얼에서는 Docker Compose를 사용하여 Milvus를 로컬 환경에 빠르게 배포하는 방법을 설명드리겠습니다.
Milvus란 무엇인가?
Milvus는 Linux Foundation旗下的 벡터 데이터베이스로, 다음과 같은 특징을 가지고 있습니다:
- 높은 확장성: 수십억 개의 벡터를 수평적으로 확장
- 다양한 인덱스 지원: HNSW, IVF, DiskANN 등 다양한 인덱스 알고리즘
- 하이브리드 검색: 벡터 + 메타데이터 필터링 지원
- 다중 모드 임베딩: 텍스트, 이미지, 오디오 등 다양한 데이터 타입 지원
- 클라우드 네이티브: Kubernetes, Docker Compose 다양한 배포 옵션
사전 요구사항
- Docker 20.10 이상
- Docker Compose v2 이상
- 최소 8GB RAM (권장 16GB)
- 50GB 이상의 디스크 공간
Docker Compose 설정 파일 작성
Milvus는 여러 마이크로서비스로 구성되어 있으며, Docker Compose를 통해 단일 노드 환경에서 실행할 수 있습니다.
# milvus-compose.yaml
version: '3.8'
services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.5
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- etcd_data:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
ports:
- "9001:9001"
- "9000:9000"
volumes:
- minio_data:/minio_data
command: minio server /minio_data --console-address ":9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
milvus:
container_name: milvus-standalone
image: milvusdb/milvus:v2.3.3
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
volumes:
- milvus_data:/var/lib/milvus
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- etcd
- minio
volumes:
etcd_data:
minio_data:
milvus_data:
Milvus 배포 및 실행
위 설정 파일을 저장한 후 다음 명령어를 순차적으로 실행합니다:
# Milvus 디렉토리 생성 및 이동
mkdir -p milvus && cd milvus
Docker Compose 파일 생성
cat > docker-compose.yaml << 'EOF'
version: '3.8'
services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.5
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- etcd_data:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
ports:
- "9001:9001"
- "9000:9000"
volumes:
- minio_data:/minio_data
command: minio server /minio_data --console-address ":9001"
milvus:
container_name: milvus-standalone
image: milvusdb/milvus:v2.3.3
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
volumes:
- milvus_data:/var/lib/milvus
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- etcd
- minio
volumes:
etcd_data:
minio_data:
milvus_data:
EOF
Milvus 컨테이너 실행 (백그라운드)
docker-compose up -d
컨테이너 상태 확인
docker-compose ps
로그 확인 (실행 상태 점검)
docker-compose logs -f milvus
Python SDK로 Milvus 연결 및 기본 연산
Milvus가 정상적으로 실행되면 Python SDK(pymilvus)를 사용하여 벡터 데이터를 저장하고 검색할 수 있습니다. 이 예제에서는 HolySheep AI를 통해 생성한 임베딩을 Milvus에 저장하는 전체 파이프라인을 보여줍니다.
# requirements.txt
pymilvus>=2.3.0
openai>=1.0.0
from pymilvus import connections, Collection, CollectionSchema, FieldSchema, DataType, utility
from openai import OpenAI
import numpy as np
========================================
1단계: HolySheep AI로 임베딩 생성
========================================
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1"
)
def create_embedding(text: str):
"""HolySheep AI 텍스트 임베딩 생성"""
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
========================================
2단계: Milvus 연결 설정
========================================
def connect_milvus():
"""Milvus 서버에 연결"""
connections.connect(
alias="default",
host="localhost",
port="19530"
)
print("✅ Milvus 연결 성공!")
========================================
3단계: 컬렉션 생성 및 데이터 삽입
========================================
def create_and_insert_data():
"""컬렉션 생성 및 벡터 데이터 삽입"""
# 컬렉션 이름 정의
collection_name = "document_embeddings"
# 기존 컬렉션 삭제 (재실행 시)
if utility.has_collection(collection_name):
utility.drop_collection(collection_name)
print(f"🗑️ 기존 컬렉션 '{collection_name}' 삭제됨")
# 스키마 정의
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="document_id", dtype=DataType.VARCHAR, max_length=256),
FieldSchema(name="content", dtype=DataType.VARCHAR, max_length=65535),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536)
]
schema = CollectionSchema(fields=fields, description="문서 임베딩 컬렉션")
# 컬렉션 생성
collection = Collection(name=collection_name, schema=schema)
print(f"✅ 컬렉션 '{collection_name}' 생성됨")
# 샘플 문서 데이터
documents = [
{"document_id": "doc_001", "content": "人工智能是计算机科学的一个分支"},
{"document_id": "doc_002", "content": "머신러닝은人工智能的重要分支"},
{"document_id": "doc_003", "content": "Deep learning enables advanced AI applications"},
{"document_id": "doc_004", "content": "Vector databases are crucial for semantic search"}
]
# 임베딩 생성 및 데이터 준비
embeddings = []
for doc in documents:
emb = create_embedding(doc["content"])
embeddings.append(emb)
print(f"📝 문서 '{doc['document_id']}' 임베딩 생성 완료")
# 데이터 삽입
entities = [
[doc["document_id"] for doc in documents], # document_id
[doc["content"] for doc in documents], # content
embeddings # embedding
]
insert_result = collection.insert(entities)
collection.flush()
print(f"✅ {len(documents)}개 문서 삽입 완료!")
========================================
4단계: 벡터 검색 실행
========================================
def search_similar_documents(query: str, top_k: int = 2):
"""유사 문서 검색"""
collection = Collection("document_embeddings")
# 쿼리 텍스트 임베딩 생성
query_embedding = create_embedding(query)
# 검색 실행
search_params = {
"metric_type": "IP", # Inner Product
"params": {"nprobe": 10}
}
results = collection.search(
data=[query_embedding],
anns_field="embedding",
param=search_params,
limit=top_k,
output_fields=["document_id", "content"]
)
print(f"\n🔍 검색어: '{query}'")
print(f"📊 상위 {top_k}개 결과:")
for hits in results:
for hit in hits:
print(f" - {hit.entity['document_id']}: {hit.entity['content']}")
print(f" 유사도 점수: {hit.distance:.4f}")
========================================
메인 실행
========================================
if __name__ == "__main__":
connect_milvus()
create_and_insert_data()
search_similar_documents("machine learning algorithms")
HolySheep AI 토큰 비용 비교
RAG 파이프라인에서 임베딩 생성 비용은 전체 운영비용의 상당 부분을 차지합니다. HolySheep AI를 사용하면 다양한 모델을 단일 API 키로 통합 관리하면서 비용을 최적화할 수 있습니다.
| AI 모델 | 출력 비용 ($/MTok) | 월 1,000만 토큰 비용 | 주요 사용 사례 |
|---|---|---|---|
| DeepSeek V3.2 | $0.42 | $4.20 | 비용 최적화 일괄 처리 |
| Gemini 2.5 Flash | $2.50 | $25.00 | 빠른 응답 실시간 검색 |
| GPT-4.1 | $8.00 | $80.00 | 고품질 임베딩 생성 |
| Claude Sonnet 4.5 | $15.00 | $150.00 | 복잡한 분석 작업 |
💡 비용 절감 팁: 텍스트 임베딩에는 text-embedding-3-small(1536차원)을 사용하면 품질을 유지하면서 비용을 80% 이상 절감할 수 있습니다. HolySheep AI의 단일 API 키로 모든 모델을 연동하면 결제 및 관리 편의성도 크게 향상됩니다.
인덱스 최적화 및 성능 튜닝
대규모 벡터 검색에서 성능을 극대화하려면 적절한 인덱스 선택과 파라미터 튜닝이 필수적입니다:
from pymilvus import connections, Collection
Milvus 연결
connections.connect(host="localhost", port="19530")
collection = Collection("document_embeddings")
========================================
인덱스 유형 비교 및 선택 가이드
========================================
1. HNSW (Hierarchical Navigable Small World)
- 최고 검색 품질
- 메모리 사용량 높음
- 빌드 시간 김
hnsw_index_params = {
"index_type": "HNSW",
"metric_type": "IP",
"params": {
"M": 16, # 인접 노드 수 (8-64, 높을수록 품질 ↑, 메모리 ↑)
"efConstruction": 200 # 빌드 시 탐색 범위 (128-512)
}
}
2. IVF (Inverted File Index)
- 균형 잡힌 성능/품질
- 메모리 사용량 중간
ivf_index_params = {
"index_type": "IVF_FLAT",
"metric_type": "IP",
"params": {
"nlist": 1024 # 클러스터 수 (토큰 수에 따라 조정)
}
}
3. DiskANN (디스크 기반)
- 대규모 데이터에 적합
- SSD 필요
diskann_index_params = {
"index_type": "DISKANN",
"metric_type": "IP",
"params": {}
}
========================================
인덱스 빌드 실행
========================================
def build_optimal_index(collection_name: str, index_type: str = "HNSW"):
"""적절한 인덱스 빌드"""
collection = Collection(collection_name)
if index_type == "HNSW":
index_params = hnsw_index_params
elif index_type == "IVF":
index_params = ivf_index_params
else:
index_params = diskann_index_params
print(f"🔧 {index_type} 인덱스 빌드 시작...")
# 인덱스 생성
collection.create_index(
field_name="embedding",
index_params=index_params
)
# 인덱스 로드
collection.load()
print(f"✅ {index_type} 인덱스 빌드 및 로드 완료!")
return collection
========================================
검색 파라미터 최적화
========================================
def optimized_search(query_embedding, collection, index_type: str = "HNSW"):
"""인덱스 타입별 최적화된 검색"""
if index_type == "HNSW":
search_params = {
"metric_type": "IP",
"params": {"ef": 128} # 검색 시 탐색 범위 (top_k * 2 이상 권장)
}
elif index_type == "IVF":
search_params = {
"metric_type": "IP",
"params": {"nprobe": 32} # 검색할 클러스터 수
}
else:
search_params = {
"metric_type": "IP",
"params": {}
}
results = collection.search(
data=[query_embedding],
anns_field="embedding",
param=search_params,
limit=10,
output_fields=["document_id", "content"]
)
return results
실행
collection = build_optimal_index("document_embeddings", "HNSW")
print("✅ Milvus 인덱스 최적화 완료!")
자주 발생하는 오류와 해결책
오류 1: Milvus 컨테이너가 시작되지 않는 경우
# 증상: milvus-standalone 컨테이너가 Exit 상태로 반복 재시작
원인: etcd 또는 MinIO 연결 실패, 포트 충돌
해결 방법 1: 포트 충돌 확인
netstat -tlnp | grep -E "19530|2379|9000|9001"
충돌 시 사용 중인 프로세스 종료 또는 docker-compose.yaml에서 포트 변경
해결 방법 2: 컨테이너 재시작 및 로그 확인
docker-compose down -v # 볼륨 포함 완전히 삭제
docker-compose up -d # 재시작
docker-compose logs -f # 실시간 로그 모니터링
해결 방법 3: 리소스 부족 시
docker system prune -a # 미사용 이미지/컨테이너 정리
docker volume prune # 미사용 볼륨 정리
오류 2: Python SDK 연결 실패
# 증상: pymilvus.exceptions.MilvusException: Connection refused
원인: Milvus 서버 미실행, 방화벽 설정, 네트워크 드라이버 문제
해결 방법 1: Milvus 서버 상태 확인
docker ps | grep milvus
docker logs milvus-standalone --tail 100
해결 방법 2: 방화벽/네트워크 설정
Docker 네트워크 명시적 지정
docker network create milvus_network
docker-compose.yaml에 networks 섹션 추가:
networks:
- milvus_network
그리고 모든 서비스에 networks 정의
해결 방법 3: 연결 파라미터 확인
connections.connect(
alias="default",
host="localhost",
port="19530",
timeout=30 # 타임아웃 증가
)
해결 방법 4: Milvus 서비스 정상 확인 후 재연결
import time
time.sleep(10) # Milvus 완전히 초기화될 때까지 대기
connections.connect(host="localhost", port="19530")
오류 3: 임베딩 차원 불일치 오류
# 증상: pymilvus.exceptions.ParamError: Dimension mismatch
원인: 컬렉션 생성 시 정의한 차원과 실제 임베딩 차원이 다름
해결 방법 1: 사용 모델의 임베딩 차원 확인 후 컬렉션 재생성
text-embedding-3-small: 1536차원
text-embedding-3-large: 3072차원
text-embedding-ada-002: 1536차원
EMBEDDING_DIM = 1536 # HolySheep AI text-embedding-3-small 기준
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=EMBEDDING_DIM)
]
해결 방법 2: 기존 컬렉션 차원 확인
collection = Collection("document_embeddings")
schema = collection.schema
for field in schema.fields:
if field.dtype == DataType.FLOAT_VECTOR:
print(f"현재 컬렉션 차원: {field.params['dim']}")
해결 방법 3: 잘못된 차원의 컬렉션 삭제 후 재생성
utility.drop_collection("document_embeddings")
새로 생성 시 올바른 차원 지정
오류 4: MilvusAttu 대시보드 접근 불가
# 증상: Attu (Milvus 관리 UI) 대시보드에 접근 불가
해결: Attu 컨테이너를 docker-compose.yaml에 추가
docker-compose.yaml에 추가:
attu:
container_name: milvus-attu
image: zilliz/attu:v2.3.3
environment:
MILVUS_URL: milvus:19530
ports:
- "3000:3000"
depends_on:
- milvus
실행 후 http://localhost:3000 에서 대시보드 접근
docker-compose up -d attu
프로덕션 배포 고려사항
- 분산 배포: 단일 노드 Docker Compose는 개발/테스트용으로, 프로덕션에서는 Kubernetes 기반 분산 배포 권장
- 데이터 백업: MinIO 볼륨의 정기적인 스냅샷 백업 구성
- 모니터링: Prometheus + Grafana 연동으로 Milvus 메트릭 수집
- 보안: 프로덕션 환경에서는 MinIO 접근 키 변경, 네트워크 격리 필수
결론
Milvus를 Docker Compose로 배포하면 로컬 개발 환경에서 빠르게 벡터 검색 시스템을 구축할 수 있습니다. HolySheep AI의 텍스트 임베딩 API와 Milvus를 결합하면 비용 효율적이면서도高性能な RAG 시스템을 구현할 수 있습니다.
저는 실무에서 텍스트 임베딩 처리 시 HolySheep AI의 text-embedding-3-small 모델을 활용하여 월 500만 토큰 이상 처리하면서 비용을 70% 이상 절감한 경험이 있습니다. 단일 API 키로 임베딩 생성부터 LLM 추론까지一元管理할 수 있어 운영 편의성도 크게 향상되었습니다.