Sáu Tháng Dùng Milvus trong Production — Những Gì Tôi Học Được
Tôi đã vận hành hệ thống production trên MySQL, PostgreSQL và MongoDB. Mỗi cái đều có chỗ đứng riêng. Nhưng khi xây dựng hệ thống retrieval-augmented generation (RAG) cần xử lý hàng chục triệu embedding với độ trễ thấp — đó là lúc cả ba chạm trần giới hạn.
Tôi đã đánh giá những cái tên quen thuộc. ChromaDB cảm giác quá nhẹ cho bất cứ thứ gì ngoài prototype. Pinecone thì khả thi, nhưng vendor lock-in và chi phí egress là điều không thể chấp nhận. Overhead vận hành của Weaviate nhiều hơn mức tôi muốn gánh. Milvus thì khác: được xây dựng từ đầu cho tìm kiếm vector ở quy mô hàng tỷ, với thiết kế xem enterprise deployment là mối quan tâm hàng đầu, không phải thứ được bổ sung sau.
Hướng dẫn này phản ánh sáu tháng vận hành Milvus trong Docker cho pipeline RAG production. Không phải demo — mà là deployment thực tế xử lý ~50 triệu vector với nhiều service cùng truy vấn đồng thời.
Tại Sao Chọn Milvus cho AI Quy Mô Doanh Nghiệp
Kiến trúc chính là câu trả lời. Milvus tách biệt các tầng storage, indexing và query, nghĩa là mỗi thành phần scale độc lập. Khi tải truy vấn của pipeline tăng gấp đôi, tôi scale query nodes mà không cần đụng vào storage. Sự linh hoạt đó không tồn tại trong PostgreSQL hay MongoDB với vector workload — bạn bị buộc phải scale toàn bộ hệ thống.
Những lý do chính Milvus xứng đáng có mặt trong stack của tôi:
- Nhiều loại index: HNSW, IVF_FLAT, IVF_SQ8, DISKANN — chọn dựa trên đánh đổi giữa bộ nhớ và độ chính xác
- Tìm kiếm kết hợp: kết hợp tìm kiếm vector dày đặc với bộ lọc scalar (lọc theo
user_idhoặccreated_attrong khi chạy ANN search) - Multi-tenancy: cô lập ở cấp partition giúp phù hợp cho kiến trúc SaaS
- Bảo trì tích cực: Zilliz hỗ trợ thương mại, các vấn đề production được giải quyết nhanh chóng
Với các dự án nhỏ dưới vài triệu vector, các lựa chọn nhẹ hơn hoàn toàn ổn. Vượt qua ngưỡng đó, độ phức tạp cài đặt cao hơn một chút của Milvus sẽ bù lại rất nhanh.
Cài đặt: Milvus trên Docker
Milvus có ba chế độ triển khai: Lite (nhúng), Standalone và Cluster. Với các team chạy Docker mà không có Kubernetes, Standalone là lựa chọn phù hợp. Nó tích hợp etcd và MinIO bên trong và chạy như một tập hợp container có thể quản lý được — không cần nền tảng orchestration.
Yêu cầu
- Docker Engine 20.10+ và Docker Compose v2
- Ít nhất 4 CPU core và 8 GB RAM (khuyến nghị 16 GB cho production)
- Ưu tiên host Linux — macOS hoạt động cho development nhưng không khuyến nghị cho production
Tải File Docker Compose Chính Thức
mkdir -p /opt/milvus && cd /opt/milvus
# Tải file compose standalone chính thức
wget https://github.com/milvus-io/milvus/releases/download/v2.4.9/milvus-standalone-docker-compose.yml \
-O docker-compose.yml
Luôn bắt đầu từ file chính thức. Đây là những phần quan trọng, đã rút gọn cho rõ ràng:
version: '3.5'
services:
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
volumes:
- ./volumes/etcd:/etcd
minio:
image: minio/minio:RELEASE.2023-03-13T19-46-17Z
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
volumes:
- ./volumes/minio:/minio_data
command: minio server /minio_data
standalone:
image: milvusdb/milvus:v2.4.9
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
ports:
- "19530:19530" # gRPC
- "9091:9091" # HTTP / metrics
volumes:
- ./volumes/milvus:/var/lib/milvus
depends_on:
- etcd
- minio
Khởi động Stack
docker compose up -d
# Kiểm tra tất cả container đang chạy
docker compose ps
Bạn sẽ thấy ba container — etcd, minio và milvus-standalone — tất cả hiển thị trạng thái Up. Lần khởi động đầu tiên mất 30–60 giây trong khi Milvus khởi tạo cấu trúc metadata.
Cấu hình: Tinh chỉnh cho Production
Cài đặt mặc định sẽ đủ để qua demo. Chúng không thể trụ được với traffic thực tế. Đây là những thay đổi tôi thực hiện sau tháng đầu tiên, khi các pattern sử dụng thực tế trở nên rõ ràng.
Tùy chỉnh milvus.yaml
Mount file config tùy chỉnh để ghi đè mặc định mà không cần build lại image:
mkdir -p /opt/milvus/config
cat > /opt/milvus/config/milvus.yaml << 'EOF'
log:
level: warn # giảm log ồn ào; dùng "info" để debug
dataCoord:
segment:
maxSize: 512 # MB; segment nhỏ hơn = indexing nhanh hơn
sealProportion: 0.8
queryNode:
gracefulTime: 5000 # ms chờ trước khi dừng query
common:
retentionDuration: 86400 # giây; 0 = giữ dữ liệu mãi mãi
cache:
cacheSize: 4 # GB; đặt ~30-40% RAM khả dụng
EOF
Thêm config mount vào docker-compose.yml dưới service standalone:
volumes:
- ./volumes/milvus:/var/lib/milvus
- ./config/milvus.yaml:/milvus/configs/milvus.yaml
Giới hạn Tài nguyên
Bỏ qua bước này và Milvus sẽ tiêu thụ toàn bộ bộ nhớ khả dụng khi indexing nặng. Thêm giới hạn tài nguyên vào định nghĩa service standalone:
deploy:
resources:
limits:
memory: 12G
reservations:
memory: 6G
Chọn Loại Index Phù Hợp
Lựa chọn index quan trọng hơn hầu hết bất kỳ quyết định cấu hình đơn lẻ nào khác. Với workload của tôi — embedding 768 chiều, ~50 triệu vector — HNSW mang lại recall tốt nhất. Đây là cách cài đặt:
from pymilvus import Collection, FieldSchema, CollectionSchema, DataType, connections
connections.connect(host="localhost", port="19530")
# HNSW: recall tốt nhất, tốn nhiều bộ nhớ hơn
index_params = {
"metric_type": "IP", # Inner Product cho embedding đã chuẩn hóa
"index_type": "HNSW",
"params": {
"M": 16, # M càng cao = recall tốt hơn, tốn nhiều bộ nhớ hơn
"efConstruction": 200 # càng cao = chất lượng index tốt hơn, build chậm hơn
}
}
collection = Collection("my_embeddings")
collection.create_index(field_name="embedding", index_params=index_params)
collection.load() # phải load vào bộ nhớ trước khi truy vấn
Bộ nhớ hạn chế? Chuyển sang IVF_SQ8. Quantization giảm dung lượng bộ nhớ khoảng 75% với cái giá chỉ là giảm 2–3% recall — một đánh đổi hợp lý cho hầu hết trường hợp sử dụng.
Kiểm tra và Giám sát
Kiểm tra Sức khỏe
# HTTP health endpoint
curl -f http://localhost:9091/healthz
# Kết quả mong đợi: {"status":"healthy"}
Kết nối và Chạy Kiểm tra Nhanh
pip install pymilvus
from pymilvus import connections, utility
connections.connect(host="localhost", port="19530")
print("Đã kết nối:", utility.get_server_version())
print("Danh sách collection:", utility.list_collections())
Attu: Giao diện Quản lý Milvus
Attu là GUI chính thức cho Milvus — hãy nghĩ đến pgAdmin, nhưng dành cho vector database. Thêm vào file compose của bạn:
attu:
image: zilliz/attu:v2.4.9
environment:
MILVUS_URL: standalone:19530
ports:
- "3000:3000"
depends_on:
- standalone
Khởi động lại và mở http://localhost:3000. Bạn có thể duyệt collection, xem thống kê index, thực hiện truy vấn tìm kiếm tương tác và xem trạng thái segment — tất cả mà không cần viết một dòng code nào. Tôi dùng Attu hằng ngày để theo dõi hoạt động hệ thống. Đây không phải tùy chọn trong setup của tôi.
Metrics Prometheus
Milvus hiển thị metrics tương thích Prometheus tại http://localhost:9091/metrics. Ba metric tôi theo dõi sát nhất trong production:
milvus_querynode_search_latency_bucket— độ trễ tìm kiếm p50/p99milvus_datanode_flush_segment_size_bytes— hành vi flush segmentmilvus_rootcoord_collection_num— số lượng collection theo thời gian
# Cấu hình scrape prometheus.yml
scrape_configs:
- job_name: 'milvus'
static_configs:
- targets: ['milvus-host:9091']
Kết nối với Grafana sử dụng dashboard chính thức của Milvus (ID 17777) và bạn có khả năng quan sát mạnh mẽ mà không cần tự xây dựng gì.
Giám sát Log
# Xem log trực tiếp, chỉ lọc lỗi
docker compose logs -f standalone | grep -E "ERROR|WARN"
# Kiểm tra sức khỏe etcd — Milvus phụ thuộc nhiều vào nó
docker compose exec etcd etcdctl endpoint health
Những Gì Tôi Sẽ Làm Khác Nếu Bắt Đầu Lại
Bốn điều tôi ước mình biết từ ngày đầu:
- Luôn chuẩn hóa embedding trước khi chèn khi dùng metric Inner Product. Tôi đã quên điều này một lần và mất hai tiếng đồng hồ bối rối vì kết quả tìm kiếm vô nghĩa.
- Gọi
collection.load()một cách tường minh sau mỗi lần restart service. Milvus không tự động load collection vào bộ nhớ — điều này khiến ai cũng vấp ngã ít nhất một lần. - Cài đặt Attu ngay lập tức, đừng để sau. Debug các vấn đề segment compaction mà không có UI để theo dõi thực sự rất đau đầu.
- Lên kế hoạch partition sớm. Thêm partition sau hàng chục triệu lần insert là khả thi nhưng gây gián đoạn cho các pattern truy vấn.
Sau sáu tháng, Milvus là thành phần tôi ít phải đụng đến nhất trong toàn bộ stack. Đó chính xác là điều bạn muốn từ infrastructure. Cấu hình đúng ngay từ đầu, và nó sẽ không làm phiền bạn.

