本文以拉取镜像部署方式来部署,也可以用 docker-compose 服务编排方式,编排方式看文末。
1.简介
Milvus 是一款面向生成式 AI 应用的开源向量数据库。
-
- 依赖的三大外部组件:etcd、minio、mq
- etcd
- etcd.endpoints:这是 etcd 服务地址的配置参数,Milvus 默认会使用自己启动 etcd 服务,不过在一些大型企业中通常会有专门运维 etcd 的团队,对接他们的 etcd 服务,稳定性会更高。 修改这个参数就可以对接企业自己的etcd服务。
- etcd.rootPath:存储 Milvus 元数据的 key 的前缀名,默认不需要修改。但是当我们有多套 Milvus 集群想要使用同一套 etcd 服务的时候,就可以修改不同的前缀名,来做集群元数据隔离。
- etcd.auth:milvus 默认不会开启 etcd 的身份验证,但是当外部 etcd 服务开启安全验证时,可以在这里配置用户名密码。
- etcd
- 依赖的三大外部组件:etcd、minio、mq
-
-
- minio,虽然这个模块的名字是"minio",但是在 Milvus 中,它其实指代的是所有的 OSS(Object Storage Service) client。使用 minio client 来操作所有的 OSS,可以是 S3、GCS、Aliyun OOS 或其他,通过这个模块下的 cloudProvider 参数配置 OSS 提供商。这块需要关注的重点配置有四个:
- minio.address/minio.port:minio 服务的配置地址,如果我们想使用第三方的 minio 服务,在此处配置服务地址。
- minio.bucketName:minio 的 bucket 名字,如果有多套 Milvus 集群想要使用同一个 minio 服务时,可以通过 bucketName 来隔离。
- minio.rootPath:Milvus 在 minio 里存储数据的 key 的前缀。如果有多套 Milvus 集群想要使用同一个 minio 服务的同一个 bucket 时,那么可以用 rootPath 来做隔离。
- minio.cloudProvider:OSS 提供商的配置参数,常见的 OSS 提供商比如,AWS 、GCP、Aliyun 都是支持的,详见:https://milvus.io/docs/product_faq.md#Where-does-Milvus-store-data。
- minio,虽然这个模块的名字是"minio",但是在 Milvus 中,它其实指代的是所有的 OSS(Object Storage Service) client。使用 minio client 来操作所有的 OSS,可以是 S3、GCS、Aliyun OOS 或其他,通过这个模块下的 cloudProvider 参数配置 OSS 提供商。这块需要关注的重点配置有四个:
-
-
-
- mq
- pulsar.address/pulsar.port:pulsar 的服务地址,通过这两个参数可以配置使用外部 pulsar 服务。
- pulsar.tenant:pulsar 使用的租户名,当有多套 milvus 集群想要共用一套 pulsar,可以通过不同的租户名来隔离。
- msgChannel.chanNamePrefix.cluster: 如果不想使用 pulsar 的多租户来做隔离,可以通过修改 message channel 的前缀名来做隔离。
- Milvus 也支持 Kafka 作为 mq,开启时需要注释掉 pulsar 的相关配置,然后开启 Kafka 配置的注释。
- mq
-
-
- 自身组件配置:包括 proxy、rootcoord、querycoord、querynode、indexnode、datacoord、datanode 的相关配置。
- rootcoord,主要用来处理 DDL(data definition language )、DCL(data control language) 请求以及时间戳服务 TSO 的管理。这个组件需要关注如下几个配置:
- rootCoord.maxPartitionNum:一个集合中允许创建的最大集合数,这个配置参数提出来,不是希望大家去修改,而是记住这个限制。当你的业务中,如果租户数超过 1024,就不推荐使用 partition 来做隔离了,推荐使用 partition key 方案,租户数可以达到百万以上。
- rootCoord.enableActiveStandby:Milvus 里面的 coord 节点,不能直接做横向扩展,需要开启 ActiveStandby 模式之后才可以扩展多节点来做容灾。
- rootcoord,主要用来处理 DDL(data definition language )、DCL(data control language) 请求以及时间戳服务 TSO 的管理。这个组件需要关注如下几个配置:
- 自身组件配置:包括 proxy、rootcoord、querycoord、querynode、indexnode、datacoord、datanode 的相关配置。
-
-
- proxy,用来接收和验证请求以及做结果合并的组件,里面需要关注的参数有:
- proxy.maxFieldNum:Milvus 的一个集合里面可以创建多个标量字段和向量字段,但是这些字段的总数不建议超过 64,字段数过多,系统的存储压力和检索压力都会比较大。
- proxy.maxVectorFieldNum:在进行多向量字段搜索时,一个集合中的向量字段数不能超过 10 个,主要在一些多模态检索,多因子认证的场景会用到。
- proxy.maxShardNum:Milvus在创建集合时需要设置 shard_num,shard 可以理解数据进入 Milvus 的通道。目前建议每 2 亿条数据设置一个 shard。2 亿数据以内,设置一个 shard,2-4 亿数据设置 2 个 shard,以此类推。
- proxy.accessLog:Milvus 里有一个很有用的功能,就是 accessLog,默认是关闭的,如果需要可以在这里打开,可以监测 Milvus 收到请求的详细信息,包括发送用户,ip 地址,调用接口,sdk 信息等。
- proxy,用来接收和验证请求以及做结果合并的组件,里面需要关注的参数有:
-
-
-
- querynode,负责监控管理 querynode 的状态以及节点间的负载均衡,配置基本都是内部使用,用户不需要关注太多,所以我们就跳过 querycoord 直接到 querynode。querynode 的配置参数很多,作为用户重点关注一个参数即可:
- queryNode.mmap:mmap 的开关。对于有很多标量字段的 collection,如果把数据都存放到内存,成本肯定会很高,milvus 提供的 mmap 能力,可以很好的解决这个问题。打开这个配置的缺点就是在(内存/检索数据)比过低的情况,搜索性能会急剧下降,需要根据我们自己的场景来评估。
- querynode,负责监控管理 querynode 的状态以及节点间的负载均衡,配置基本都是内部使用,用户不需要关注太多,所以我们就跳过 querycoord 直接到 querynode。querynode 的配置参数很多,作为用户重点关注一个参数即可:
-
-
-
- datacoord,Indexnode 没有用户侧需要关注的参数,我们直接跳过进入到 datacoord。datacoord 主要负责数据段(segment)的大小、生命周期,以及压缩管理,同时负责数据的垃圾回收。同时,之前的 indexcoord 的功能也合并进了 datacoord,所以现在的 datacoord 还负责 create index,indexnode 索引任务调度等职责。这块重点关注如下参数:
- dataCoord.segment.maxSize:控制 Milvus 集群中一个数据分片(segment)的最大尺寸(in-memory index)。总体来讲,在机器内存资源允许的情况下,这个参数设置得越大,系统中的 segment 数量会越少,查询速度也会更好。社区之前有用户分享过,单个 querynode 节点 128G 内存,将 dataCoord.segment.maxSize 设置为 8G,获得的性能比 1G 的 dataCoord.segment.maxSize 有四倍左右的提升。
- dataCoord.segment.diskSegmentMaxSize:控制 Milvus 集群中一个数据分片(segment)的最大尺寸(diskann index)。和 dataCoord.segment.maxSize 的配置原则相似,不过主要是针对 disk index。
- dataCoord.segment.sealProportion:一个 growing segment 转成 sealed segment 的系数。dataCoord.segment.maxSize和 dataCoord.segment.sealProportion共同决定一个 segment 何时转化为 sealed 状态。按照示意图中的默认配置,当一个 segment 的大小增长为 1024*0.12=123 MB 时,就会转化为 sealed 状态。对于一些数据更新比较频繁,且希望尽快创建好索引加快搜索性能的场景,这个参数可以设置小一点,比如 0.12,让 segment 尽快 seal 然后创建索引。对于一些偏离线场景,对性能要求不高,这个参数可以设置大一些,比如 0.3~0.5,节省索引节点的开销。
- dataCoord.segment.expansionRate:compaction 过程中,segment 允许的膨胀倍数,Milvus 允许存在的最大 Segment Size = maxSize * expansionRate。
- dataCoord.gc.dropTolerance:segment compaction 产生新的 segment, 老的segment 就会被清理;另外drop collection 之后,这个 collection 里的 segment 也会被清理。但是 Milvus 不会立即删除这些待清理的 segment 释放存储,而是会等 gc 时间到了之后才会物理清除。而 dataCoord.gc.dropTolerance 就是控制 gc 等待的时间。
- datacoord,Indexnode 没有用户侧需要关注的参数,我们直接跳过进入到 datacoord。datacoord 主要负责数据段(segment)的大小、生命周期,以及压缩管理,同时负责数据的垃圾回收。同时,之前的 indexcoord 的功能也合并进了 datacoord,所以现在的 datacoord 还负责 create index,indexnode 索引任务调度等职责。这块重点关注如下参数:
-
-
-
- datanode,负责数据写入落盘的节点,相关的配置使用默认的参数即可
- 其他功能配置:包括 log、security、quotaAndLimits 等
- log,Milvus 是一个组件众多的分布式系统,日志是帮助我们进行 trouble shooting 重要的工具,对于每一个用户都需要认真配置,推荐使用 Loki 这类专业的日志采集工具来收集分析 Milvus 的日志。
- log.level:生产环境建议使用 info 级别日志,测试环境建议用 debug 级别。debug 级别的日志会非常多,开启 debug 等级后务必注意磁盘空间用量。
- log.file:Milvus 的日志默认会打印到标准输出,同时也支持配置成文件的形式存储,对于文件的形式支持设置最大文件size,最长生命周期以及最多备份文件数。
- log,Milvus 是一个组件众多的分布式系统,日志是帮助我们进行 trouble shooting 重要的工具,对于每一个用户都需要认真配置,推荐使用 Loki 这类专业的日志采集工具来收集分析 Milvus 的日志。
-
-
-
- security,用户鉴权,以及基于角色的权限管控(RBAC),相关配置在 common 模块下:
- common.security.authorizationEnabled:安全验证的控制开关,默认是关闭的。
- common.security.defaultRootPassword:打开安全验证后,Milvus 默认的 root 用户密码(社区里经常问的一个问题 : )
- security,用户鉴权,以及基于角色的权限管控(RBAC),相关配置在 common 模块下:
-
-
-
- quotaAndLimits,quotaAndLimits 是 milvus.yaml 里占用篇幅很大的一块内容。Milvus 提供了非常多的限流的配置,包括写入、删除、查询等等。我们重点关注以下几个配置:
- quotaAndLimits.flushRate.collection:这个参数是控制 collection 做 Flush 操作的频率,默认 0.1 是指每 10 秒只允许执行 1 次 Flush 操作。Flush 操作会做两件事,将 growing segment 转化为 sealed segment,然后将 mq 里的数据落盘到对象存储上。如果过于频繁地调用 Flush,系统会产生大量的小 segment,给系统造成较大的 compaction 压力,进而影响查询性能。Flush 一般是在插入完一批数据,不再继续插入的时候,可以手动调用一次,加快数据落盘和索引建立。在 Milvus 内部,growing segment size 达到 maxSize * sealProportion,便会自动 seal,同时系统也会每 10 分钟自动做 seal segment 的落盘,所以大部分情况下,我们不需要去手动调用 Flush。最后还需要强调一点,数据的可见性和 Flush 没关系,是由查询的一致性等级来决定的,之前社区里有不少朋友担心数据可见性的问题,每插入一次数据,都会调一次 Flush,导致系统整体性能非常差,并且影响稳定性。
- quotaAndLimits.upsertRate/quotaAndLimits.deleteRate:这两个参数是用来控制 upsert 和 delete 的速率。Milvus 采用 LSM Tree 作为其底层数据结构,更新和删除操作过于频繁,会给系统的 compaction 任务产生压力。upsert 和 delete 的速率建议都不要超过 0.5MB/s。如果确实需要快速地更新一个集合中的大部分数据,建议通过 collection alias 的方式,将新数据写入新集合,更新完毕后,将 alias 指向新集合。
- quotaAndLimits,quotaAndLimits 是 milvus.yaml 里占用篇幅很大的一块内容。Milvus 提供了非常多的限流的配置,包括写入、删除、查询等等。我们重点关注以下几个配置:
-
2.场景示例
- Case 1:Performance First
- 对于性能要求比较高的场景,比如推荐系统,金融风控,大家一般都会选择图索引,Milvus 支持的常见图索引:HNSW,DISKANN。除了索引选择外,一般会同时配合调节如下参数:
- 调高 dataCoord.segment.maxSize, dataCoord.segment.diskSegmentMaxSize。根据机器配置情况,可以将这两个参数调大到 4G 或者 8G。调高 maxSize 要求更高的 IndexNode 和 QueryNode 的运行时内存,且引入更多的写放大,所以务必使用较大规格的机器。
- 调低 dataCoord.segment.sealProportion,dataCoord.segment.expansionRate,将单个 Growing Segment maxSize 控制在 200MB 左右,减轻 Delegator (QueryNode 中的 leader 节点)的内存压力。
- 对于性能要求比较高的场景,比如推荐系统,金融风控,大家一般都会选择图索引,Milvus 支持的常见图索引:HNSW,DISKANN。除了索引选择外,一般会同时配合调节如下参数:
- Case 2:Cost First
- 对于性能要求不严苛,但是成本比较敏感的场景,比如自动驾驶模型训练中的 corner case 图片搜索,超大规模知识库系统,我们可以利用索引量化或磁盘+内存的方式,在有限内存里装更多的数据,当然这样做的代价就是牺牲召回率或性能。索引的量化包括 SCANN,IVF_SQ8,以及 Milvus 2.5 推出的 HNSW_SQ/PQ/PRQ 索引;而使用磁盘的方式,一是可以使用磁盘索引,创建 Index 的时候选择 DISKANN 类型。第二种则是在配置文件中开启 mmap:
-
- 如果对于成本非常敏感,对于性能没有要求,推荐将 vectorField、vectorIndex、scalarField、scalarIndex 的 mmap 开关都打开。如果查询过程中需要用到标量字段且希望标量过滤性能足够快,可以将 vectorIndex、scalarIndex 的 mmap 开关关掉,来加速标量搜索的性能。需要注意的是,配合 mmap 使用的常见索引是 HNSW,这个索引会比原始数据膨胀 1.8 倍左右。所以当挂载了 100G 的磁盘,实际能装的数据只有 50G 左右,如果将原始数据也缓存到磁盘上,那么实际能装的数据就会更少,需要提前规划好磁盘空间。
3.Docker 镜像部署
1、创建持久化目录
sudo mkdir -p /opt/milvus/etcd
sudo mkdir -p /opt/milvus/minio
sudo mkdir -p /opt/milvus/milvus
sudo chown -R 1000:1000 /opt/milvus
2、拉取镜像
docker pull quay.io/coreos/etcd:v3.5.5
docker pull minio/minio:RELEASE.2023-03-20T20-16-18Z
docker pull milvusdb/milvus:v2.4.8
3、创建 docker 内部网络,否则 milvus 访问不了 etcd
docker network create milvus-net
4、启动 etcd
docker run -d \
--name milvus-etcd \
--restart unless-stopped \
--network milvus-net \
-e ETCD_AUTO_COMPACTION_MODE=revision \
-e ETCD_AUTO_COMPACTION_RETENTION=1000 \
-e ETCD_QUOTA_BACKEND_BYTES=4294967296 \
--mount type=bind,src=/opt/milvus/etcd,dst=/etcd \
quay.io/coreos/etcd:v3.5.5 \
/usr/local/bin/etcd \
--name milvus-etcd \
--data-dir=/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://milvus-etcd:2379 \
--listen-peer-urls=http://0.0.0.0:2380 \
--initial-advertise-peer-urls=http://milvus-etcd:2380 \
--initial-cluster=milvus-etcd=http://milvus-etcd:2380 \
--initial-cluster-state=new \
--initial-cluster-token=milvus-cluster
5、启动 MinIO
docker run -d \
--name milvus-minio \
--restart unless-stopped \
--network milvus-net \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
--mount type=bind,src=/opt/milvus/minio,dst=/minio_data \
minio/minio:RELEASE.2023-03-20T20-16-18Z \
minio server /minio_data
6、启动 Milvus
docker run -d \
--name milvus-standalone \
--restart unless-stopped \
--network milvus-net \
--security-opt seccomp:unconfined \
-e ETCD_ENDPOINTS=milvus-etcd:2379 \
-e MINIO_ADDRESS=milvus-minio:9000 \
--mount type=bind,src=/opt/milvus/milvus,dst=/var/lib/milvus \
-p ${VM_IP}:19530:19530 \
-p ${VM_IP}:9091:9091 \
milvusdb/milvus:v2.4.8 \
milvus run standalone
7、检查状态:
docker ps
docker logs --tail 100 milvus-standalone
curl http://127.0.0.1:9091/healthz
三条本地目录分别是:
- /data/milvus/etcd:元数据
- /data/milvus/minio:Milvus 的对象存储文件
- /data/milvus/milvus:Milvus 本体数据与索引
VM 使用桥接或仅主机网络后,查看 VM IP:
hostname -I
7、配置后端 yaml
# 这样就不需要启动变量。
akim:
rag:
vector-store: milvus
milvus:
collection-per-embedding-model: true
# 同时把 Milvus 地址改为 VM 的 IP,例如:
spring:
ai:
vectorstore: # 向量存储
milvus:
initialize-schema: true
database-name: default # Milvus 中数据库的名称
collection-name: knowledge_segment # Milvus 中集合的名称:用于存储向量数据的集合标识符,所有相关的向量操作都会在这个集合中进行
client:
host: 192.168.1.100
port: 19530
4.docker compose 部署
未验证
mkdir -p ~/services/milvus
cd ~/services/milvus
# 创建 docker-compose.yml:
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"
command: >
etcd
-advertise-client-urls=http://127.0.0.1:2379
-listen-client-urls=http://0.0.0.0:2379
--data-dir=/etcd
volumes:
- ./volumes/etcd:/etcd
minio:
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
container_name: milvus-minio
environment:
MINIO_ACCESS_KEY: minioadmin
MINIO_SECRET_KEY: minioadmin
command: minio server /minio_data
volumes:
- ./volumes/minio:/minio_data
milvus:
image: milvusdb/milvus:v2.4.8
container_name: milvus-standalone
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
ports:
- "127.0.0.1:19530:19530"
- "127.0.0.1:9091:9091"
volumes:
- ./volumes/milvus:/var/lib/milvus
- minio
# 启动并检查:
docker compose up -d
docker compose ps
curl http://127.0.0.1:9091/healthz















