Kiến trúc Cluster và phân tách thành phần
Trong kiến trúc Cluster, Milvus tách biệt các thành phần tính toán và lưu trữ để đảm bảo khả năng mở rộng (Scalability) và chịu lỗi (High Availability - HA).
Khác với Standalone, Cluster mode sử dụng RootCoord để quản lý metadata, IndexCoord để quản lý index, và QueryCoord để phân phối query.
Các thành phần quan trọng cần nhân bản (replica) là:
- QueryNode: Thực thi truy vấn vector và lọc bộ lọc (filter). Cần nhiều replica để tăng thông lượng đọc.
- DataNode: Xử lý lưu trữ dữ liệu bulk, compact dữ liệu, và đồng bộ với MinIO. Cần nhiều replica để tăng tốc độ ghi.
- Datanode (DN): Trong cấu hình mới, DataNode và DN có thể được tách biệt hoặc gộp tùy phiên bản, nhưng ở đây ta tập trung vào DataNode xử lý dữ liệu raw.
Mục tiêu của bước này là hiểu rõ luồng dữ liệu: Client -> Proxy -> QueryCoord -> QueryNode (Read) hoặc DataNode (Write).
Cấu hình Docker Compose cho Cluster Mode
Tạo file cấu hình chính để khởi tạo các service với chế độ Cluster. File này sẽ khai báo nhiều container cho cùng một service để tạo thành replica.
Đường dẫn file: /etc/milvus/docker-compose-cluster.yaml
Nội dung file hoàn chỉnh:
version: '3.8'
services:
etcd:
image: quay.io/coreos/etcd:v3.5.5
container_name: milvus-etcd
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
- ETCD_QUOTA_BACKEND_BYTES=4294967296
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- /data/milvus/etcd:/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: 10s
retries: 3
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
container_name: milvus-minio
command: minio server /data --address 0.0.0.0:9000 --console-address 0.0.0.0:9001
volumes:
- /data/milvus/minio:/data
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 10s
retries: 3
rootcoord:
image: milvusdb/milvus:v2.4.0
container_name: milvus-rootcoord
command: ["milvus", "run", "rootcoord"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
depends_on:
- etcd
- minio
networks:
- milvus-net
indexcoord:
image: milvusdb/milvus:v2.4.0
container_name: milvus-indexcoord
command: ["milvus", "run", "indexcoord"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
depends_on:
- etcd
- minio
networks:
- milvus-net
querycoord:
image: milvusdb/milvus:v2.4.0
container_name: milvus-querycoord
command: ["milvus", "run", "querycoord"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
depends_on:
- etcd
- minio
networks:
- milvus-net
datanode:
image: milvusdb/milvus:v2.4.0
container_name: milvus-datanode-1
command: ["milvus", "run", "datanode"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
COMMON_REPLICA_NUMBER: 2
depends_on:
- etcd
- minio
networks:
- milvus-net
querynode:
image: milvusdb/milvus:v2.4.0
container_name: milvus-querynode-1
command: ["milvus", "run", "querynode"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
COMMON_REPLICA_NUMBER: 2
depends_on:
- etcd
- minio
networks:
- milvus-net
querynode-replica:
image: milvusdb/milvus:v2.4.0
container_name: milvus-querynode-2
command: ["milvus", "run", "querynode"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
COMMON_REPLICA_NUMBER: 2
depends_on:
- etcd
- minio
networks:
- milvus-net
datanode-replica:
image: milvusdb/milvus:v2.4.0
container_name: milvus-datanode-2
command: ["milvus", "run", "datanode"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
COMMON_REPLICA_NUMBER: 2
depends_on:
- etcd
- minio
networks:
- milvus-net
proxy:
image: milvusdb/milvus:v2.4.0
container_name: milvus-proxy
command: ["milvus", "run", "proxy"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
QUERYCOORD_ADDRESS: querycoord:19531
INDEXCOORD_ADDRESS: indexcoord:31000
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- etcd
- minio
- rootcoord
- indexcoord
- querycoord
networks:
- milvus-net
# Logging
pulsar:
image: apachepulsar/pulsar:3.0.0
container_name: milvus-pulsar
ports:
- "6650:6650"
- "8080:8080"
environment:
- PULSAR_MEM=Xms2g Xmx2g
volumes:
- /data/milvus/pulsar:/pulsar/data
depends_on:
- etcd
networks:
- milvus-net
mixcoord:
image: milvusdb/milvus:v2.4.0
container_name: milvus-mixcoord
command: ["milvus", "run", "mixcoord"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
PULSAR_ADDRESS: pulsar:6650
depends_on:
- etcd
- minio
- pulsar
networks:
- milvus-net
networks:
milvus-net:
driver: bridge
volumes:
etcd:
minio:
pulsar:
Kết quả mong đợi: File cấu hình được tạo, khai báo 2 instance cho QueryNode và DataNode, cùng các service điều phối (Coord) và lưu trữ.
Triển khai Cluster và xác minh trạng thái
Khởi động toàn bộ cluster sử dụng file cấu hình vừa tạo. Cần đảm bảo đã tạo các thư mục data trước khi chạy.
mkdir -p /data/milvus/{etcd,minio,pulsar}
docker compose -f /etc/milvus/docker-compose-cluster.yaml up -d
Kiểm tra trạng thái của các container để đảm bảo tất cả đều đang chạy (Up) và healthy.
docker compose -f /etc/milvus/docker-compose-cluster.yaml ps
Verify kết quả: Bạn sẽ thấy danh sách các container với trạng thái "healthy" hoặc "running", bao gồm milvus-querynode-1, milvus-querynode-2, milvus-datanode-1, milvus-datanode-2.
Cấu hình Nginx Load Balancer
Trong kiến trúc Cluster, Proxy chỉ là một điểm vào. Để cân bằng tải và đảm bảo HA cho Proxy, ta cần đặt Nginx trước các Proxy (ở đây ta giả định có thể mở rộng Proxy, nhưng hiện tại dùng Nginx để định tuyến traffic vào cluster).
Tuy nhiên, trong Docker Compose trên, Proxy đã expose port 19530. Để mô phỏng Load Balancer thực tế cho nhiều Proxy hoặc phân phối request nội bộ, ta cấu hình Nginx.
Đường dẫn config: /etc/nginx/sites-available/milvus-cluster
Nội dung file:
upstream milvus_cluster {
server 127.0.0.1:19530 weight=1;
# Thêm server khác ở đây nếu bạn deploy thêm proxy node
# server 127.0.0.1:19531 weight=1;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://milvus_cluster;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_connect_timeout 86400;
}
}
Kích hoạt config và reload Nginx.
ln -s /etc/nginx/sites-available/milvus-cluster /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx
Kết quả mong đợi: Nginx chạy không lỗi, sẵn sàng nhận traffic từ port 80 và chuyển tiếp vào Milvus Proxy.
Logic Auto-scaling dựa trên tải
Milvus tự động quản lý số lượng QueryNode và DataNode dựa trên tải, nhưng cần cấu hình giới hạn và kích hoạt cơ chế tự động mở rộng thông qua Horizontal Pod Autoscaler (HPA) nếu dùng K8s. Trên Docker, ta dùng script để mô phỏng logic này dựa trên CPU/Memory.
Tạo script tự động thêm node khi tải cao. Script này sẽ check số lượng container hiện tại và CPU usage.
Đường dẫn script: /opt/scripts/auto-scale-milvus.sh
#!/bin/bash
CONFIG_FILE="/etc/milvus/docker-compose-cluster.yaml"
QUERYNODE_PREFIX="milvus-querynode-"
DATANODE_PREFIX="milvus-datanode-"
MAX_REPLICAS=5
MIN_REPLICAS=2
check_and_scale() {
local type=$1
local prefix=$2
# Đếm số lượng node hiện tại
current_count=$(docker ps --filter "name=${prefix}" --format "{{.Names}}" | wc -l)
# Lấy CPU average usage của các node hiện tại (giả định > 70%)
# Trong thực tế, cần dùng Prometheus/Docker stats để đọc chính xác hơn
avg_cpu=0
if [ $current_count -gt 0 ]; then
# Đọc CPU usage từ docker stats (giả lập logic đơn giản)
# Thực tế: avg_cpu=$(docker stats --no-stream --format "{{.CPUPerc}}" $(docker ps --filter "name=${prefix}" --format "{{.ID}}") | awk '{sum+=$1} END {print sum/NR}')
# Ở đây ta giả định logic trigger: nếu > MIN_REPLICAS và tải cao -> scale up
avg_cpu=85 # Giả lập giá trị cao để test
fi
if [ $current_count -lt $MAX_REPLICAS ] && [ $avg_cpu -gt 70 ]; then
echo "Scaling up $type: Current=$current_count, CPU=$avg_cpu%"
next_id=$((current_count + 1))
# Tạo config mới cho node tiếp theo
# Lưu ý: Trong production, nên dùng K8s HPA thay vì script shell
# Ở đây ta dùng docker compose up --scale
docker compose -f $CONFIG_FILE up -d --scale ${type}=$next_id
echo "Added ${prefix}${next_id}"
elif [ $current_count -gt $MIN_REPLICAS ] && [ $avg_cpu -lt 30 ]; then
echo "Scaling down $type: Current=$current_count, CPU=$avg_cpu%"
# Scale down logic (cẩn thận với dữ liệu)
docker compose -f $CONFIG_FILE up -d --scale ${type}=$((current_count - 1))
fi
}
# Chạy check định kỳ
while true; do
check_and_scale "querynode" "$QUERYNODE_PREFIX"
check_and_scale "datanode" "$DATANODE_PREFIX"
sleep 60
done
Cấp quyền và chạy script ở background.
chmod +x /opt/scripts/auto-scale-milvus.sh
nohup /opt/scripts/auto-scale-milvus.sh > /var/log/milvus-autoscale.log 2>&1 &
Kết quả mong đợi: Script chạy ngầm, giám sát và tự động thêm container nếu tải tăng (trong mô phỏng).
Kiểm tra khả năng chịu lỗi (High Availability)
Thực hiện kiểm tra HA bằng cách tắt ngẫu nhiên một node đang hoạt động và quan sát hệ thống có tự động chuyển đổi (failover) hay không.
Tắt một QueryNode đang chạy (ví dụ: querynode-1).
docker stop milvus-querynode-1
Quan sát logs của QueryCoord và Proxy để xem hệ thống có phát hiện node down và phân phối lại query không.
docker logs -f milvus-querycoord --tail 50
docker logs -f milvus-proxy --tail 50
Thực hiện một query đơn giản để kiểm tra tính liên tục.
python3 -c "from pymilvus import connections, Collection; connections.connect('default', host='localhost', port=19530); print('Connection successful')"
Kết quả mong đợi: Client vẫn kết nối thành công, logs hiển thị cảnh báo về node bị mất nhưng hệ thống tự động chuyển traffic sang milvus-querynode-2. Không có lỗi "Connection refused" từ phía client.
Khởi động lại node đã tắt để khôi phục.
docker start milvus-querynode-1
docker compose -f /etc/milvus/docker-compose-cluster.yaml up -d
Verify: Kiểm tra lại số lượng node hoạt động.
docker ps --filter "name=milvus-querynode" --format "table {{.Names}}\t{{.Status}}"
Điều hướng series:
Mục lục: Series: Triển khai Database Vector với Milvus và Ubuntu 24.04
« Phần 6: Tối ưu hóa hiệu năng và phân tích logs trên Ubuntu 24.04
Phần 8: Bảo mật, Backup nâng cao và các mẹo vận hành (DevOps) »