挑战
在生产环境中大规模运行 Milvus 带来了多项基础设施挑战:
- 固定容量 — 静态 Milvus 部署无法应对高峰时段 10 倍的查询负载峰值
- 数据丢失风险 — 临时存储上的 Pod 重启会导致大型集合的索引重建耗时数小时
- 成本效益低下 — 为峰值负载过度预配意味着 70% 的时间都在为闲置计算付费
- 存储成本 — 绑定到实例的块存储卷对于多 TB 的向量数据集而言成本高昂
- 索引重建 — 节点替换后重新索引数百万个向量会造成数小时的停机时间
- 多可用区 (Multi-AZ) 持久性 — 单可用区 (Single-AZ) 存储无法在可用区故障中幸存
我们的解决方案
我们部署了基于 Kubernetes (EKS) 的 Milvus,为查询节点配置了水平 Pod 自动扩缩容 (Horizontal Pod Autoscaling),为计算配置了集群自动扩缩容 (Cluster Autoscaler),并使用 Amazon S3 作为持久存储后端——从而消除了数据丢失风险,并将存储成本降低了约 80%。
架构
- 编排: Amazon EKS (Elastic Kubernetes Service)
- 计算: 由 Cluster Autoscaler 管理的 EC2 实例(混合实例类型)
- 向量数据库: 通过 Helm chart 以分布式模式部署的 Milvus
- 对象存储: Amazon S3,用于段文件、索引文件和 binlog 持久化
- 元数据: etcd 集群,用于 Milvus 协调和元数据
- 消息队列: 用于 Milvus 日志管道的消息流
- 监控: Prometheus + Grafana,用于 Milvus 指标和自动扩缩容信号
Kubernetes 上的 Milvus 分布式架构
组件部署
Milvus 以分布式模式运行,具有专用节点类型,每个类型都作为具有独立扩缩容能力的 Kubernetes 工作负载进行部署:
- 代理节点 (Proxy Nodes) — 处理客户端连接和请求路由
- 查询节点 (Query Nodes) — 执行向量搜索并将段加载到内存中
- 数据节点 (Data Nodes) — 处理写入路径并将段刷新到 S3
- 索引节点 (Index Nodes) — 构建向量索引并写入 S3
- 协调器 (Coordinator) — 集群协调和时间戳分配
- etcd — 元数据存储和服务发现
- 消息队列 (Message Queue) — 日志流和预写日志
水平 Pod 自动扩缩容 (HPA)
查询节点自动扩缩容
查询节点是主要的扩缩容目标——它们将向量段加载到内存中并执行搜索。扩缩容由多种指标驱动,包括 CPU 利用率、内存利用率、查询队列深度和 P99 查询延迟。HPA 配置了适当的最小/最大副本数、用于处理峰值的快速扩容以及避免抖动的逐步缩容。
索引节点自动扩缩容
索引节点根据待处理的索引构建作业进行扩缩容——当构建队列有待处理项时扩容,空闲时缩容。
EC2 集群自动扩缩容
实例策略
- 节点组: 多个具有不同实例类型的节点组,用于成本优化
- 查询工作负载: 内存优化型实例,用于内存中的向量段
- 索引工作负载: 计算优化型实例,用于 CPU 密集型索引构建
- Spot 实例: 索引节点和非关键数据节点在 Spot 实例上运行,以实现显著节省
- 按需实例 (On-Demand): 查询节点和协调器在按需实例上运行以确保稳定性
扩缩容行为
当 HPA 创建无法调度的 Pod 时,Cluster Autoscaler 会在适当的节点组中预置新的 EC2 实例。新的查询节点随后将它们分配的段从 S3 加载到内存中并开始提供查询服务,整个扩容过程在数分钟内完成。
S3 支持的持久存储
为什么选择 S3 而不是块存储
S3 为 Milvus 提供了优于块存储的显著优势:
- 大型数据集的存储成本降低约 80%
- 具有内置多可用区 (Multi-AZ) 复制功能的11个9的持久性
- 无限扩容,无需手动调整卷大小
- Pod 独立性 — 数据始终可用,不受 Pod 或节点生命周期的影响
- 无可用区 (AZ) 锁定 — 数据可从任何可用区访问
S3 数据流
- 写入路径: 数据节点在内存中缓冲插入,然后将密封段刷新到 S3
- 索引构建: 索引节点从 S3 读取段,构建索引,然后将索引文件写回 S3
- 查询路径: 查询节点从 S3 下载段和索引,加载到内存中,并提供查询服务
- 恢复: Pod 重启时,查询节点从 S3 重新下载分配的段(无数据丢失)
S3 性能优化
- 段大小调整 平衡 S3 请求成本与数据新鲜度
- 本地 SSD 缓存 在 NVMe 实例存储上,避免了对热点段的重复 S3 读取
- 并行下载 实现查询节点的快速启动
- 生命周期策略 将旧数据归档到更便宜的存储层
监控与可观测性
部署通过 Prometheus 和 Grafana 提供了全面的监控:
- 查询性能 — 延迟分布、QPS、缓存命中率
- 集群概览 — 节点数量、Pod 状态、资源利用率
- 存储健康 — S3 使用情况、段计数、刷新率
- 自动扩缩容事件 — HPA 事件、节点扩缩容、Pod 调度延迟
- 告警 — 针对高延迟、OOM 风险、刷新失败和容量限制的自动化告警
主要特点
- 查询节点 HPA — 基于 CPU、内存、延迟和队列深度的自动扩缩容
- EC2 Cluster Autoscaler — 混合实例类型的动态节点预置
- S3 持久性 — 11个9的持久性,比块存储便宜约 80%,可抵御 AZ 故障
- Spot 实例 — 索引和数据节点在 Spot 实例上运行,显著节省计算成本
- 本地 SSD 缓存 — NVMe 缓存消除了对热点段的重复 S3 读取
- 零停机恢复 — Pod 重启时从 S3 重新加载段,无数据丢失
- 多可用区 (Multi-AZ) — S3 存储 + 多可用区节点组,实现全面的 AZ 故障容忍
- 可观测性 — Prometheus + Grafana,提供 Milvus 特定指标和自动扩缩容可见性
成果
技术栈
常见问题
MicrocosmWorks 配置了 horizontal pod autoscaling,使用来自 Milvus 内置的 memory usage exporter 的自定义指标,当任何查询节点的内存利用率超过 75% 时,触发扩容事件。集合段通过 Milvus 的 segment manager 自动重新分配到新节点上,从而防止任何单个节点成为瓶颈。
MicrocosmWorks 选择使用 MinIO 作为对象存储层的 S3 支持存储,因为它将存储与计算解耦,允许查询节点独立扩展而无需预置新的 EBS 卷。与 gp3 EBS 卷相比,这种架构将存储成本降低了大约 60%,同时保持了从 S3 加载段的时间低于 100 毫秒。
MicrocosmWorks 配置了部署,为每个 Milvus 组件(包括查询节点、索引节点和数据节点)设置了副本集,并使用 pod disruption budgets 确保在滚动更新期间的最低可用性。由于所有持久化数据都存储在 S3 中,故障节点的替换可以立即访问所有段,无需数据迁移。
MicrocosmWorks 发现 r6i.2xlarge 实例为 Milvus 查询工作负载提供了最佳的性价比,提供 64GB 内存用于内存段缓存,并具有竞争力的竞价价格。对于 GPU 加速的索引构建,配备 NVIDIA A10G GPU 的 g5.xlarge 实例将索引构建时间缩短了 8 倍,相比纯 CPU 构建。
MicrocosmWorks 提供 Kubernetes 基础设施项目,费率为 $30-$50/小时。一个 Milvus 自动扩缩容部署,包括 Helm chart 定制、HPA 配置、S3 集成和监控设置,通常需要 150-250 小时。集群优化和升级的持续托管支持可按相同的每小时费率提供。
