最近在搭一套基于规范条文的 RAG 知识库向量库选了 Milvus。单机版部署本身不复杂docker compose up -d一条命令的事但我发现很多人包括最开始的我起完之后其实并不清楚那三个容器各自在干嘛遇到端口冲突、连不上、数据写不进去之类的问题就只能干瞪眼。这篇把两件事讲清楚Milvus standalone 是怎么跑起来的以及部署时实际会踩到的坑。不讲那些官网抄一遍就有的东西只说自己装的时候真正需要搞明白的部分。一、为什么单机版要起三个容器你docker compose up -d之后docker ps会看到三个容器milvus-standalone、milvus-etcd、milvus-minio。第一次见到的人通常会愣一下——我不就装个数据库吗怎么冒出来仨。原因是 Milvus 走的是存算分离的架构。它自己不负责持久化数据而是把不同种类的状态分别甩给专门的组件去存。这个设计在分布式集群里是为了各部分能独立扩缩容到了单机版虽然计算角色都压进了一个进程但依赖关系没变所以 etcd 和 minio 还是得拉起来陪跑。说白了就是standalone 是干活的etcd 和 minio 是存东西的而且这俩存的是完全不同的东西。standalone本体但不存数据这是 Milvus 真正干活的进程。在集群版里 proxy接请求、querynode查询、datanode写数据、indexnode建索引是分开的不同节点单机版把它们全打包进了这一个容器。你代码里连的19530端口就是它。关键点它本身不持久化任何数据。这个容器删了重建里面是空的所有数据得靠下面两个组件捞回来。所以你会发现重启 standalone 很快因为它只是个计算层。etcd存元数据etcd 里放的是目录信息——有哪些 collection、每个 collection 的 schema 长什么样、字段定义、索引参数、分区信息、各组件状态和时间戳。数据量很小但极其关键。Milvus 要知道这个库里有什么、结构是怎样、索引建在哪全靠问 etcd。可以把它理解成整个库的账本。minio存数据本体minio 是个 S3 兼容的对象存储真正的大块数据都堆在这——向量、标量字段的值、建好的索引文件、各种日志。你插进去的向量和 HNSW 索引最终都以对象的形式落在 minio 里。一个不太严谨但好懂的类比把 standalone 想成图书馆管理员干活但不藏书etcd 是借阅目录的卡片柜记着每本书叫什么、在哪个架子上minio 是真正堆书的书库。管理员下班重启了无所谓只要卡片柜和书库还在他回来查一下目录就能把书找回来。二、insert 和 search 时三者怎么配合理解了分工数据流就很顺了。写入insert请求打到 standalone → 这批数据属于哪个 collection、对应什么 schema这类元数据记进 etcd → 向量和索引文件这些实体数据写进 minio。检索search请求到 standalone → 先问 etcd “这个 collection 的索引在哪、什么类型” → 再从 minio 把索引和数据读出来在内存里算相似度 → 返回结果。这里有个容易忽略的细节这三个容器之间走的是容器内网用服务名互相寻址。standalone 的配置里连的是etcd:2379和minio:9000而不是宿主机端口。这点等下排端口冲突时有用。三、部署步骤前置条件装好 Docker 和 Docker ComposeV2。注意是docker compose中间空格不是老的docker-compose。docker compose version能正常输出版本号就行。下载 compose 文件并启动# 钉死版本别用会自己变的 latestwgethttps://github.com/milvus-io/milvus/releases/download/v2.6.18/milvus-standalone-docker-compose.yml\-Odocker-compose.ymldockercompose up-d起来之后三个容器都应该是 Up 状态standalone 对外暴露 19530dockercomposeps验证直接用 Python 连一下最直观frompymilvusimportMilvusClient clientMilvusClient(urihttp://localhost:19530)print(client.list_collections())# 能返回哪怕是空列表就说明通了四、踩坑端口冲突我第一次docker compose up -d直接报了这个Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint milvus-minio: failed to bind host port for 0.0.0.0:9001 ... address already in use这跟 Milvus 本身没关系是宿主机的 9001 端口被别的进程占了minio 绑不上。先看是谁占的sudoss-ltnp|grep:9001大概率两种情况。一种是之前已经起过一次 Milvus旧的 minio 容器还在跑。这种最常见把整套停干净再重起dockercompose downdockerps-a|grepmilvus# 确认没有残留的 milvus-* 容器dockercompose up-d要是还看到游离的容器可能是上次用别的方式起的手动清掉dockerrm-fmilvus-minio milvus-etcd milvus-standalonedockercompose up-d另一种是 9001 被非 Milvus 的进程占了。那就改 compose把 minio 映射到宿主机的端口换一个容器内端口不要动ports:-9011:9001# 只改冒号左边宿主机侧-9010:9000为什么只改左边回到前面讲的Milvus 内部是通过容器网络minio:9000连 minio 的跟你映射到宿主机的端口没半点关系。冒号右边是容器内端口改了反而连不上。顺手做个简化其实 minio 的 9000/9001、etcd 的 2379根本没必要绑到宿主机因为 Milvus 走的是容器内网。你真正需要从宿主机访问的只有 standalone 的19530以及可选的 WebUI 9091。所以想彻底避免这类端口冲突、顺便更安全一点可以直接把 minio 和 etcd 那几行ports:注释掉只留 19530 对外。装多套环境、或者机器上服务比较杂的时候这一步能省不少事。五、几点实战判断数据的真值不在 Milvus 里。三个容器的数据落在 compose 同目录的volumes/下volumes/etcd、volumes/minio、volumes/milvus。但如果你的向量是从某份源文件比如我这边是清洗好的 jsonl灌进去的那真值是那份源文件Milvus 里的东西随时能重建。我的做法是源文件进 gitvolumes 目录根本不备份——这点数据量重灌比恢复还快。当然如果你的向量是花了大代价算出来、源头丢了就没了那另说该备份备份。排障时按组件对症下药。理解了三者分工出问题能快速定位连不上 19530 是 standalone 的事报 collection / schema 相关的诡异错误常是 etcd 状态乱了报存储、写不进数据、failed to put object这类基本是 minio 的问题最常见是磁盘满了。Milvus 单机版部署这事命令是真简单但把这三个容器的关系搞明白之后遇到问题才知道往哪查而不是每次都重装一遍碰运气。后面我会接着写向量化服务和检索这部分怎么和它对接有需要的可以关注一下。
Milvus standalone 部署实录:三个容器到底在干什么,以及怎么把它跑起来
最近在搭一套基于规范条文的 RAG 知识库向量库选了 Milvus。单机版部署本身不复杂docker compose up -d一条命令的事但我发现很多人包括最开始的我起完之后其实并不清楚那三个容器各自在干嘛遇到端口冲突、连不上、数据写不进去之类的问题就只能干瞪眼。这篇把两件事讲清楚Milvus standalone 是怎么跑起来的以及部署时实际会踩到的坑。不讲那些官网抄一遍就有的东西只说自己装的时候真正需要搞明白的部分。一、为什么单机版要起三个容器你docker compose up -d之后docker ps会看到三个容器milvus-standalone、milvus-etcd、milvus-minio。第一次见到的人通常会愣一下——我不就装个数据库吗怎么冒出来仨。原因是 Milvus 走的是存算分离的架构。它自己不负责持久化数据而是把不同种类的状态分别甩给专门的组件去存。这个设计在分布式集群里是为了各部分能独立扩缩容到了单机版虽然计算角色都压进了一个进程但依赖关系没变所以 etcd 和 minio 还是得拉起来陪跑。说白了就是standalone 是干活的etcd 和 minio 是存东西的而且这俩存的是完全不同的东西。standalone本体但不存数据这是 Milvus 真正干活的进程。在集群版里 proxy接请求、querynode查询、datanode写数据、indexnode建索引是分开的不同节点单机版把它们全打包进了这一个容器。你代码里连的19530端口就是它。关键点它本身不持久化任何数据。这个容器删了重建里面是空的所有数据得靠下面两个组件捞回来。所以你会发现重启 standalone 很快因为它只是个计算层。etcd存元数据etcd 里放的是目录信息——有哪些 collection、每个 collection 的 schema 长什么样、字段定义、索引参数、分区信息、各组件状态和时间戳。数据量很小但极其关键。Milvus 要知道这个库里有什么、结构是怎样、索引建在哪全靠问 etcd。可以把它理解成整个库的账本。minio存数据本体minio 是个 S3 兼容的对象存储真正的大块数据都堆在这——向量、标量字段的值、建好的索引文件、各种日志。你插进去的向量和 HNSW 索引最终都以对象的形式落在 minio 里。一个不太严谨但好懂的类比把 standalone 想成图书馆管理员干活但不藏书etcd 是借阅目录的卡片柜记着每本书叫什么、在哪个架子上minio 是真正堆书的书库。管理员下班重启了无所谓只要卡片柜和书库还在他回来查一下目录就能把书找回来。二、insert 和 search 时三者怎么配合理解了分工数据流就很顺了。写入insert请求打到 standalone → 这批数据属于哪个 collection、对应什么 schema这类元数据记进 etcd → 向量和索引文件这些实体数据写进 minio。检索search请求到 standalone → 先问 etcd “这个 collection 的索引在哪、什么类型” → 再从 minio 把索引和数据读出来在内存里算相似度 → 返回结果。这里有个容易忽略的细节这三个容器之间走的是容器内网用服务名互相寻址。standalone 的配置里连的是etcd:2379和minio:9000而不是宿主机端口。这点等下排端口冲突时有用。三、部署步骤前置条件装好 Docker 和 Docker ComposeV2。注意是docker compose中间空格不是老的docker-compose。docker compose version能正常输出版本号就行。下载 compose 文件并启动# 钉死版本别用会自己变的 latestwgethttps://github.com/milvus-io/milvus/releases/download/v2.6.18/milvus-standalone-docker-compose.yml\-Odocker-compose.ymldockercompose up-d起来之后三个容器都应该是 Up 状态standalone 对外暴露 19530dockercomposeps验证直接用 Python 连一下最直观frompymilvusimportMilvusClient clientMilvusClient(urihttp://localhost:19530)print(client.list_collections())# 能返回哪怕是空列表就说明通了四、踩坑端口冲突我第一次docker compose up -d直接报了这个Error response from daemon: failed to set up container networking: driver failed programming external connectivity on endpoint milvus-minio: failed to bind host port for 0.0.0.0:9001 ... address already in use这跟 Milvus 本身没关系是宿主机的 9001 端口被别的进程占了minio 绑不上。先看是谁占的sudoss-ltnp|grep:9001大概率两种情况。一种是之前已经起过一次 Milvus旧的 minio 容器还在跑。这种最常见把整套停干净再重起dockercompose downdockerps-a|grepmilvus# 确认没有残留的 milvus-* 容器dockercompose up-d要是还看到游离的容器可能是上次用别的方式起的手动清掉dockerrm-fmilvus-minio milvus-etcd milvus-standalonedockercompose up-d另一种是 9001 被非 Milvus 的进程占了。那就改 compose把 minio 映射到宿主机的端口换一个容器内端口不要动ports:-9011:9001# 只改冒号左边宿主机侧-9010:9000为什么只改左边回到前面讲的Milvus 内部是通过容器网络minio:9000连 minio 的跟你映射到宿主机的端口没半点关系。冒号右边是容器内端口改了反而连不上。顺手做个简化其实 minio 的 9000/9001、etcd 的 2379根本没必要绑到宿主机因为 Milvus 走的是容器内网。你真正需要从宿主机访问的只有 standalone 的19530以及可选的 WebUI 9091。所以想彻底避免这类端口冲突、顺便更安全一点可以直接把 minio 和 etcd 那几行ports:注释掉只留 19530 对外。装多套环境、或者机器上服务比较杂的时候这一步能省不少事。五、几点实战判断数据的真值不在 Milvus 里。三个容器的数据落在 compose 同目录的volumes/下volumes/etcd、volumes/minio、volumes/milvus。但如果你的向量是从某份源文件比如我这边是清洗好的 jsonl灌进去的那真值是那份源文件Milvus 里的东西随时能重建。我的做法是源文件进 gitvolumes 目录根本不备份——这点数据量重灌比恢复还快。当然如果你的向量是花了大代价算出来、源头丢了就没了那另说该备份备份。排障时按组件对症下药。理解了三者分工出问题能快速定位连不上 19530 是 standalone 的事报 collection / schema 相关的诡异错误常是 etcd 状态乱了报存储、写不进数据、failed to put object这类基本是 minio 的问题最常见是磁盘满了。Milvus 单机版部署这事命令是真简单但把这三个容器的关系搞明白之后遇到问题才知道往哪查而不是每次都重装一遍碰运气。后面我会接着写向量化服务和检索这部分怎么和它对接有需要的可以关注一下。