Milvus 向量数据库架构手册——配置 Milvus 系统

0 阅读17分钟

在开始本章之前,我们先简要回顾一下前面已经覆盖的内容,包括向量数据库概览、如何部署 Milvus,以及如何与 Milvus 交互。到这里,你应该已经对向量数据库和 Milvus 有了清晰理解。本章将更深入地讨论使用 Milvus 的实践层面,包括以下内容:

  • 理解 Milvus 的依赖服务和启动配置
  • 管理 Milvus 中的核心系统和服务配置
  • 修改配置

技术要求

我们将继续使用基础工具,这些工具都是开源的,包括:

  • VS Code:安装方式请参考第 1 章 “技术要求” 部分
  • Docker:安装方式请参考第 1 章 “技术要求” 部分

除了这些基础工具之外,我们还会使用一些开源服务来监控系统日志和指标,包括:

  • Prometheus
  • Grafana
  • Promtail
  • Loki

我们还会使用一个 Milvus 开发工具 Attu。下载地址如下:

https://github.com/zilliztech/attu

它的作用是方便地与 Milvus 通信。

本章使用的代码可以在我们的 GitHub 仓库中找到:

https://github.com/PacktPublishing/The-Architecture-Handbook-for-Milvus-Vector-Database

理解 Milvus 的依赖服务和启动配置

为了更好理解 Milvus,认识它所依赖的服务及其主要功能非常重要。这些知识将帮助你在生产环境中选择合适的依赖服务。下面是 Milvus 主要依赖的服务及其角色。

etcd

etcd 是一个开源分布式 key-value store,主要用于存储和管理配置数据、服务发现,以及协调分布式系统状态。它的核心特性包括:

  • 共识算法:使用 Raft consensus algorithm(https://raft.github.io/Raft)确保数据一致性和可靠性。
  • 简单 key-value 存储:通过 key-value pairs 方便地进行数据存储和检索。
  • 事件通知机制:实现 observer pattern,用于事件通知,常用于服务发现。

在 Milvus 中,etcd 用于 metadata storage,在运行过程中记录必要的系统信息和数据管理细节。它还辅助服务发现和节点健康检查。需要特别注意的是,etcd 是 Milvus 的关键依赖;如果 etcd 不可用,Milvus 将无法正常工作,导致请求失败以及内部指标异常。如果 etcd 服务变慢,例如由于磁盘延迟较高,可能增加 Milvus 响应时间或造成请求超时。不过,一旦 etcd 恢复,Milvus 会自动恢复正常运行。

Pulsar / Kafka / RocksMQ

这些选项都是 message queue(MQ,消息队列)服务,在 Milvus 运行过程中,你可以根据需要选择其中一种。MQ 是一种用于在分布式系统中传输数据的通信机制,允许不同应用或服务之间进行异步信息交换。这有助于缓解系统负载、提升性能,并实现解耦。

RocksMQ 是基于 RocksDB 的轻量级 MQ,设计目标是极简。它是 Milvus standalone mode 中的默认选项,但不适合 cluster mode,因为它目前只支持文件存储。PulsarKafka 更适合大规模应用。表 4.1 展示了它们的相同点和不同点。

Similarity / DifferenceKafkaPulsar
Model support二者都支持 publish / subscribe 模型,允许 producers 将消息发送到 topics,并允许 consumers 订阅这些 topics。二者都支持 publish / subscribe 模型,允许 producers 将消息发送到 topics,并允许 consumers 订阅这些 topics。
Message storage二者都提供持久化消息存储,确保系统崩溃后消息不会丢失。二者都提供持久化消息存储,确保系统崩溃后消息不会丢失。
Horizontal scaling二者都采用分布式架构,可以水平扩展以处理大量消息。二者都采用分布式架构,可以水平扩展以处理大量消息。
Message delivery二者都能够管理高吞吐消息投递。二者都能够管理高吞吐消息投递。
Architecture designKafka 采用单 broker 架构,将数据存储和消息投递合并在一起,每个 broker 负责存储和处理消息。https://kafka.apache.org/Pulsar 使用由 brokers 和 BookKeeper 组成的分离式架构,允许存储和计算独立扩展。https://pulsar.apache.org/
Message modelKafka 主要使用 log-structured message storage system,已消费消息默认会保留,直到过期或被删除。Pulsar 支持多种消费模型,包括 shared、exclusive 和 failover,并允许配置消息保留策略。
Processing latencyKafka 在高吞吐场景下通常具有较低延迟,但在某些条件下可能受到影响。由于其架构,Pulsar 通常在处理延迟方面表现更好,适合低延迟应用。

表 4.1:Kafka 和 Pulsar 的相同点与不同点

在 Pulsar 和 Kafka 之间选择时,必须考虑你的具体应用场景、需求和团队技术栈。Pulsar 更适合多租户和低延迟场景,而 Kafka 在高吞吐和成熟度方面更具优势。

在 Milvus 中,MQ 主要用于日志服务。所有数据写入首先进入 MQ,随后由 Milvus 内部两个服务消费消息:DataNode 和 QueryNode。DataNode 负责数据持久化,而 QueryNode 负责确保增量数据及时可见,从而保证查询正确性。

MinIO / S3

这些是 object storage services,也就是对象存储服务,用于管理和存储大量数据,特别是图像、视频、文档和备份等非结构化数据。不同于传统文件系统,对象存储不使用层级目录结构,而是在扁平命名空间中存储所有对象。对象存储能够处理海量数据,因此适合云计算和大数据应用,并支持水平扩展。

S3 是 Amazon Web Services(AWS)提供的对象存储服务,广泛用于云存储,并提供多种安全和合规选项,例如数据加密和访问控制。

MinIO 是一个开源高性能对象存储服务器,兼容 Amazon S3 API。它部署简单,适用于云环境和本地环境。

在 Milvus 中,对象存储用于数据持久化。DataNode 会定期从 MQ 中获取数据,并批量写入对象存储。

DataNode

DataNode 是构成 Milvus 的一种 server type。它的核心功能是将数据存储到磁盘。Milvus 包含许多角色,这些角色按功能划分,例如 Proxy / RootCoord / DataCoord / DataNode / QueryCoord / QueryNode / IndexNode。

这一概念将在第 5 章进一步讨论。

现在,我们将重点关注如何管理 Milvus 系统和配置。

管理 Milvus 中的核心系统和服务配置

配置是任何系统的基础能力,允许系统在不同应用场景中提供合适的服务能力。这增强了可扩展性,并支持动态调整,从而在发生错误时控制系统行为,有效降低业务损失。

此外,配置不仅是问题解决时的一种备用方法,也是根据系统负载调整任务调度和并发能力的手段,从而提高资源利用率。它还可以控制系统如何响应用户请求,例如设置有效参数范围和资源限制。

在 Milvus 中,系统配置通过 Milvus 二进制文件的 configs 目录中的 YAML 文件管理,具体文件名为 milvus.yaml。上一节中,我们讨论了 Milvus 支持三种 MQ:RocksMQ、Pulsar 和 Kafka。启动 Milvus 时,可以通过配置选择其中一种。此外,与依赖服务交互所需的一系列配置,也都在 milvus.yaml 文件中指定。

milvus.yaml 中的配置组织方式,是将用途相似的配置归类到特定节点下。例如,与 etcd 连接方式和存储 root path 相关的配置,都在 etcd 节点下。

配置文件结构如下:

etcd:
  endpoints: localhost:2379
  rootPath: by-dev
  metaSubPath: meta
  kvSubPath: kv
  log:
    level: info # Only supports debug, info, warn, error, panic, or fatal. Default 'info'.
    path: stdout

当然,前面的示例并不是完整的 etcd 相关配置。Milvus 还支持通过配置 etcdauth 和 SSL 来使用安全连接。如果不需要这些配置,删除或留空即可。对于其他应用服务也是类似的。例如,要选择某个服务,需要使用 MQ 节点下的 type 配置。默认值是 default,表示 standalone mode。你也可以根据想配置的服务选择 RocksMQ 或 Pulsar。如果想自定义,就从 Pulsar、Kafka 或 RocksMQ 中选择一种。至于 MQ 的具体服务配置,取决于选择的 MQ 类型。如果选择 RocksMQ,就在 rocksmq 节点下配置其连接参数;如果选择 Pulsar 或 Kafka,则分别在 pulsarkafka 节点下设置连接参数。

除了某些类型的服务配置外,Milvus 内部服务也包含一些类别配置。我们暂时不需要了解这些服务的含义和功能。只需要知道,Milvus 由这些服务组成:proxyrootcoorddatacoordquerycoorddatanodequerynodeindexnode。有时我们也把这些服务称为 nodes。

当然,这些 nodes 并不是静态不变的。随着 Milvus 架构演进,新的 nodes 可能出现,已有 nodes 也可能被删除。例如,在早期 Milvus 版本中,indexcoord 仍然存在。

本书引用的版本是 Milvus 2.4.17。细心的读者也会注意到,在第 2 章介绍 Milvus 部署时,我们使用的都是 Milvus 2.4.17。

服务配置同样遵循前面的配置规则。Milvus 的服务组成,例如 proxyrootcoord 等,统称为 node。

例如,与 proxy 服务相关的配置,都位于 milvus.yaml 文件中的 proxy 节点下。这包括 proxy 端口、任务队列长度、任务指定调度并发,以及对用户请求中某些字段的基础校验,例如名称长度、collection 某些属性限制等。下面是 proxy 中的常见配置:

proxy:
  maxNameLength: 255 # The maximum length of the name or alias that can be created in Milvus, including the collection name, collection alias, partition name, and field name.
  maxFieldNum: 64 # The maximum number of field can be created when creating in a collection. It is strongly DISCOURAGED to set maxFieldNum>= 64.
  maxVectorFieldNum: 4 # The maximum number of vector fields that can be specified in a collection. Value range: [1, 10].
  maxShardNum: 16 # The maximum number of shards can be created when creating in a collection.
  maxDimension: 32768 # The maximum number of dimensions of a vector can have when creating in a collection.
  maxTaskNum: 1024 # The maximum number of tasks in the task queue of the proxy.
  ddlConcurrency: 16 # The concurrent execution number of DDL at proxy.
  dclConcurrency: 16 # The concurrent execution number of DCL at proxy.
  port: 19530 # TCP port of proxy

上述配置并不是 proxy 的全部配置;随着你对 Milvus 理解加深,它们的含义会变得更加清晰。完整系统配置可以参考:

https://milvus.io/docs/system_configuration.md#Milvus-System-Configurations-Checklist

此外,多个 nodes 可能使用的通用配置,会放在配置文件中的 common 部分。

在启动 Milvus 前调整配置

启动 Milvus 之前,我们需要调整一些配置。我们将使用 Docker Compose 重新部署 Milvus,修改配置,并验证这些变更是否生效:

  1. 部署前,需要调整 docker-compose.yml 文件。由于我们需要在启动前修改 milvus.yaml 文件,因此必须把 milvus.yaml 文件映射到本地目录。这需要调整 standalone 中的 volumes 配置。你可以参考本书 GitHub 仓库(第 4 章)中 004-docker-compose 目录里的文件,那里有完整配置文件:
standalone:
    ...
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
      - ${DOCKER_VOLUME_DIRECTORY:-.}/configs/milvus.yaml:/milvus/configs/milvus.yaml
    ...

2. 调整 milvus.yaml 中的配置。我们将修改 proxy 下的 maxNameLength 设置。默认值是 255,我们将其改为 10,也就是说,创建 collection 的名称长度不能超过 10。请记得保存文件。 3. 启动 Milvus:进入 docker-compose.yml 文件所在目录,然后执行以下命令:

docker compose up -d

4. 使用 Attu 连接 Milvus。

下一步是创建 collection。创建 collection 时,需要指定 collection 相关属性,其中之一是 collection name。你可以创建不同长度的 collection name。

image.png

图 4.1:开始使用 Attu

连接成功后,你会看到首页。点击 Databases 下的 default

当 Attu 在容器内运行时,不要使用 localhost:19530 连接 Milvus,因为这会导致连接失败。应指定 Milvus 的实际地址,例如 milvus-container-name:19530host-IP:19530

image.png

图 4.2:登录后的 Attu 首页

接下来,点击 Collection 来创建 collection。

image.png

图 4.3:Attu 创建 Collection

现在我们想检查对配置所做的调整是否已经生效。记得我们在第 2 步将文件名最大长度更新为 10。为了测试这一点,填写 collection 的相关信息,确保 collection name 是一个长度超过 10 个字符的字符串,例如 hello_milvus。这应该会抛出错误。

image.png

图 4.4:Attu 创建 Collection 错误

这个错误说明配置调整已经生效。

通过这个例子,我们学习了如何在启动前配置 Milvus。其他配置的设置方式类似。这允许我们根据具体生产需求调整 Milvus 设置。当然,我们不需要一次性理解 Milvus 的所有配置;可以参考 Milvus 配置文档,并根据具体需求进行调整。

现在我们来看如何在运行时修改配置。

修改配置

上一节中,我们讨论了如何在启动 Milvus 前调整配置。Milvus 也支持在运行时修改配置。

可以动态调整的参数通常是每次使用时都需要检查的参数,例如校验 collection name 长度时使用的参数。通常,Milvus 中与 validation 和 thresholds 相关的参数,例如最大 collection size,可以动态调整。

下面简要说明 Milvus 如何实现动态配置:启动时,Milvus 会读取配置文件,对数据进行格式化,并将其存储在变量中。随后,它会定期刷新文件,并将文件内容与内存中的配置进行比较。如果检测到变化,内存中的配置值会被更新,并触发事件。因此,Milvus 允许注册配置变更监听器;当配置变化时,这些监听器会收到信号并执行对应逻辑。

还记得前面环境设置中,我们将 maxNameLength 更新为 10。现在我们直接将 proxy 中的 maxNameLength 配置从 10 调整为 15:

maxNameLength: 15

完成修改后,我们可以再次尝试创建 collection。如果 collection 创建成功,一切就会按预期工作。不过,如果创建 collection 时遇到连接错误,应该检查 Milvus pods 是否正常运行。如果它们不正常,只需重启 pods。

image.png

图 4.5:Attu 创建 Collection 成功

接下来,如果我们尝试创建名称长度超过 15 个字符的 collection,例如 hello_milvus_backup,它会产生错误,但 hello_milvus 不会。错误信息会确认最大名称长度已成功调整为 15:

image.png

图 4.6:Attu 创建 Collection 错误

从上图中的错误信息可以看到,当前最大名称长度已成功调整为 15。

不过,需要注意的是,并非所有配置都能动态调整。例如,线程池大小等设置是在启动时确定的,之后不能更改。这类似于 Go 编程语言中 channel 的长度,channel 用于 goroutines 之间通信,并提供安全的数据传递和同步方式。

此外,并非所有问题都可以通过配置变更解决;它只是解决问题的一种潜在方法。有效的错误处理需要对异常情况进行仔细设计,并配备稳健的恢复逻辑。

监控访问和系统日志

日志是任何成熟系统的重要组成部分。为了快速了解当前运行状态,大多数系统还会加入各种监控指标,例如 Milvus 某一时刻的 collection 数量和请求延迟。一个流行的开源日志和监控解决方案,是 Prometheus、Grafana、Loki 和 Promtail 的组合。

让我们更详细了解这些服务:

  • Prometheus:Prometheus 是一个开源监控和告警系统,专为记录时间序列数据而设计。它通过 HTTP 拉取指标数据,并支持多种数据存储和查询功能。Prometheus 以强大的查询语言 PromQL 和灵活数据模型闻名,适合监控微服务和容器化环境。
  • Grafana:Grafana 是一个开源数据可视化和监控平台。它支持多种数据源,例如 Prometheus、InfluxDB 和 Elasticsearch,允许用户通过图表、dashboard 和告警系统监控和分析数据。
  • Loki:Loki 是一个开源日志聚合系统,设计类似 Prometheus,但专注于日志数据。它与 Grafana 无缝集成,使用户可以直接在 Grafana 中查询和可视化日志,从而形成 metrics 和 logs 的统一视图。
  • Promtail:Promtail 是 Loki 的日志采集工具,负责从各种来源,例如文件和系统日志,中收集日志并发送到 Loki。

使用这组工具,你可以有效监控和记录 Milvus 系统的性能和行为,洞察其运行健康状况,并排查潜在问题。

部署 Prometheus 和 Grafana

这里,我们将部署 Prometheus 和 Grafana 来监控 Milvus metrics。本设置将使用 Docker Compose。部署开始前,我们需要完成以下准备:

  1. 创建一个空文件夹,并命名为 004-docker-compose-monitor
  2. 进入新创建的文件夹,并创建一个新的 prometheus.yaml 文件,内容如下:
global:
  scrape_interval: 10s
scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['standalone:9091']

这里我们使用一个非常简单的配置,其中 scrape_interval 表示采集指标的间隔。值得注意的是,targets 中的地址需要填写为 standalone:9091,因为我们需要与另一个 Docker 实例交互,并使用 Docker Compose 内置网络。

  1. 修改 docker-compose.yml,添加 Prometheus 和 Grafana 服务,并让 Prometheus 应用配置文件。完整配置文件可以在本书 GitHub 仓库第 4 章 004-docker-compose-monitor 目录中找到:
prometheus:
    container_name: milvus-prometheus
    image: prom/prometheus
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/prometheus.yml:/etc/prometheus/prometheus.yml

    ports:
      - "9090:9090"
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
    depends_on:
      - "standalone"

  grafana:

    container_name: milvus-grafana
    image: grafana/grafana
    ports:
      - "3002:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    depends_on:
      - "standalone"
 - "prometheus"

注意,这里 Grafana 映射端口被调整为将 Docker 中的 3000 端口映射到本地 3002 端口,因为本地 3000 端口可能被占用。例如,Attu 工具内部使用 3000 端口。换句话说,如果 Attu 已经打开,而端口映射也使用 3000,就会导致 Grafana 无法访问。

  1. 在本地浏览器访问:
http://localhost:3002/

你会看到登录页面,然后可以登录。用户名和密码都是 admin,这对应 docker-compose.yml 中的 GF_SECURITY_ADMIN_PASSWORD 设置。

  1. 为 Grafana 服务添加 data source,也就是我们的 Prometheus 实例,然后根据页面操作。

image.png

图 4.7:Grafana data source

添加 Prometheus 地址时,填写:

http://prometheus:9090

填写完成后,滚动到页面底部并点击 Save 按钮。

image.png

图 4.8:添加 Prometheus data source

  1. 导入 Milvus dashboard 的 JSON 文件;该文件是本书 GitHub 仓库第 4 章 004-docker-compose-monitor 目录中的 milvus-dashboard.json。值得注意的是,这里的 dashboard 配置文件是面向 k8s 部署的,但没关系。我们先导入 dashboard。这里主要是想了解有哪些 panels,以及如何查看它们。

image.png 图 4.9:Grafana 导入 dashboard

panel 导入后,如果没有错误信息,说明成功。预期情况下不会显示数据。我们找到 Root Coordinator panel row,然后找到 Collection Num panel。找到该 panel 后,按照下图点击进入 Explore 页面。因为我们已经在上一章成功创建过一个 collection,所以用这个 monitor 检查是否能看到该信息。

image.png

图 4.10:Grafana Explore 页面

然后,将语句修改为:

milvus_rootcoord_collection_num{}

然后点击右上角 Run Query 按钮。如果成功,你应该看到类似下图的页面:

image.png

图 4.11:Grafana collection number metric

通过上述步骤,我们已经成功连接到 Milvus metrics。需要强调的是,由于我们的 dashboard 基于 Kubernetes,如果使用 Docker Compose 部署,可能不兼容。不过这不是问题;我们只需要删除与 Kubernetes 相关的过滤条件,例如 app_kubernetes_io_instanceapp_kubernetes_io_namenamespace

一开始可能不清楚哪些条件与 Kubernetes 相关,但没关系。我们的重点是如何搭建完整 metrics 系统。

如果你想深入学习 metrics,例如如何编写监控查询,建议阅读相关书籍或访问 Grafana 官方网站:

https://grafana.com/docs/grafana/latest/

部署 Loki 和 Promtail

成功部署 Prometheus 和 Grafana 后,现在可以聚焦部署 Loki 和 Promtail。这些服务可以让我们有效查询日志。使用 Loki,我们可以利用 LogQL 根据各种条件过滤日志,这对故障排查非常有帮助。

  1. 调整 Milvus 配置,修改 milvus.yaml 中的 log root node。主要是将 rootPath 设置为 /tmp,并将 stdout 设置为 false
log:
  level: info
  file:
    rootPath: /tmp
    maxSize: 300
    maxAge: 10
    maxBackups: 20
  format: text
  stdout: false

2. 修改 docker-compose.yml 中 Milvus standalone Docker 实例的文件映射,将日志文件映射到当前目录,方便 Promtail 服务采集:

  standalone:
    container_name: milvus-standalone
    ...
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
      - ${DOCKER_VOLUME_DIRECTORY:-.}/configs/milvus.yaml:/milvus/configs/milvus.yaml
      - ${DOCKER_VOLUME_DIRECTORY:-.}/tmp:/tmp
    ....

3. 在当前目录中新建两个文件:loki-config.yamlpromtail-config.yaml。Loki 使用官方默认配置,可以在本书对应 GitHub repo 第 4 章的 004-docker-compose-monitor 目录中找到。这里我们强调 promtail-config.yaml 文件。配置 client 地址时,其域名应使用 loki,最后设置要监控的 Milvus 日志文件:

server:
  http_listen_port: 9080
positions:
  filename: /tmp/positions.yaml
clients:
  - url: http://loki:3100/loki/api/v1/push
scrape_configs:
  - job_name: milvuslog
    static_configs:
      - targets:
          - localhost
        labels:
          job: milvuslog
          __path__: /tmp/milvus/*.log

4. 在 docker-compose.yaml 中添加两个服务,Prometheus 和 Grafana,这涉及配置文件映射:

  loki:
    container_name: milvus-loki
    image: grafana/loki
    ports:
      - "3100:3100"
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/loki-config.yaml:/etc/loki/loki-config.yaml
    command: -config.file=/etc/loki/loki-config.yaml
  promtail:
    container_name: milvus-promtail
    image: grafana/promtail
    ports:
      - "9080:9080"
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/promtail-config.yaml:/etc/promtail/config.yaml
      - ${DOCKER_VOLUME_DIRECTORY:-.}/tmp:/tmp/milvus
    command: -config.file=/etc/promtail/config.yaml

5. 通过命令启动所有服务,然后访问 Grafana。与上一节一样,添加一个 data source。这次选择 Loki。将 Loki 服务地址填写为:

http://loki:3100

然后点击 Save。

image.png

图 4.12:添加 Loki data source

  1. 进入 Explore 页面。

image.png

图 4.13:Grafana Explore 页面

  1. 进入查询页面,在查询框输入:
{service_name="milvuslog"}

然后点击 Run Query,即可看到 Milvus 日志。

image.png

图 4.14:Grafana-Loki Milvus 日志

恭喜你,现在已经成功完成 Milvus 日志和监控服务的设置。

小结

本章对 Milvus 进行了概览,重点介绍了它的依赖服务、配置设置,以及相关监控和日志服务的部署。这些服务在开源项目中被广泛使用,也可以应用于各种云服务场景。

掌握这些知识后,当你在使用 Milvus 遇到相关问题时,就能够更快地处理它们,无论是通过配置不同验证规则,还是借助丰富的监控指标和强大的日志查询系统来定位问题。

至此,我们围绕 Milvus 使用部分的第一阶段讨论结束。下一章中,我们将更深入地学习 Milvus 内部各种流程设计,这会进一步提升你的理解。我相信这将是一段令人兴奋的旅程!