在开始本章之前,我们先简要回顾一下前面已经覆盖的内容,包括向量数据库概览、如何部署 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,因为它目前只支持文件存储。Pulsar 和 Kafka 更适合大规模应用。表 4.1 展示了它们的相同点和不同点。
| Similarity / Difference | Kafka | Pulsar |
|---|---|---|
| Model support | 二者都支持 publish / subscribe 模型,允许 producers 将消息发送到 topics,并允许 consumers 订阅这些 topics。 | 二者都支持 publish / subscribe 模型,允许 producers 将消息发送到 topics,并允许 consumers 订阅这些 topics。 |
| Message storage | 二者都提供持久化消息存储,确保系统崩溃后消息不会丢失。 | 二者都提供持久化消息存储,确保系统崩溃后消息不会丢失。 |
| Horizontal scaling | 二者都采用分布式架构,可以水平扩展以处理大量消息。 | 二者都采用分布式架构,可以水平扩展以处理大量消息。 |
| Message delivery | 二者都能够管理高吞吐消息投递。 | 二者都能够管理高吞吐消息投递。 |
| Architecture design | Kafka 采用单 broker 架构,将数据存储和消息投递合并在一起,每个 broker 负责存储和处理消息。https://kafka.apache.org/ | Pulsar 使用由 brokers 和 BookKeeper 组成的分离式架构,允许存储和计算独立扩展。https://pulsar.apache.org/ |
| Message model | Kafka 主要使用 log-structured message storage system,已消费消息默认会保留,直到过期或被删除。 | Pulsar 支持多种消费模型,包括 shared、exclusive 和 failover,并允许配置消息保留策略。 |
| Processing latency | Kafka 在高吞吐场景下通常具有较低延迟,但在某些条件下可能受到影响。 | 由于其架构,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 还支持通过配置 etcd 的 auth 和 SSL 来使用安全连接。如果不需要这些配置,删除或留空即可。对于其他应用服务也是类似的。例如,要选择某个服务,需要使用 MQ 节点下的 type 配置。默认值是 default,表示 standalone mode。你也可以根据想配置的服务选择 RocksMQ 或 Pulsar。如果想自定义,就从 Pulsar、Kafka 或 RocksMQ 中选择一种。至于 MQ 的具体服务配置,取决于选择的 MQ 类型。如果选择 RocksMQ,就在 rocksmq 节点下配置其连接参数;如果选择 Pulsar 或 Kafka,则分别在 pulsar 和 kafka 节点下设置连接参数。
除了某些类型的服务配置外,Milvus 内部服务也包含一些类别配置。我们暂时不需要了解这些服务的含义和功能。只需要知道,Milvus 由这些服务组成:proxy、rootcoord、datacoord、querycoord、datanode、querynode 和 indexnode。有时我们也把这些服务称为 nodes。
当然,这些 nodes 并不是静态不变的。随着 Milvus 架构演进,新的 nodes 可能出现,已有 nodes 也可能被删除。例如,在早期 Milvus 版本中,indexcoord 仍然存在。
本书引用的版本是 Milvus 2.4.17。细心的读者也会注意到,在第 2 章介绍 Milvus 部署时,我们使用的都是 Milvus 2.4.17。
服务配置同样遵循前面的配置规则。Milvus 的服务组成,例如 proxy、rootcoord 等,统称为 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,修改配置,并验证这些变更是否生效:
- 部署前,需要调整
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。
图 4.1:开始使用 Attu
连接成功后,你会看到首页。点击 Databases 下的 default。
当 Attu 在容器内运行时,不要使用 localhost:19530 连接 Milvus,因为这会导致连接失败。应指定 Milvus 的实际地址,例如 milvus-container-name:19530 或 host-IP:19530。
图 4.2:登录后的 Attu 首页
接下来,点击 Collection 来创建 collection。
图 4.3:Attu 创建 Collection
现在我们想检查对配置所做的调整是否已经生效。记得我们在第 2 步将文件名最大长度更新为 10。为了测试这一点,填写 collection 的相关信息,确保 collection name 是一个长度超过 10 个字符的字符串,例如 hello_milvus。这应该会抛出错误。
图 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。
图 4.5:Attu 创建 Collection 成功
接下来,如果我们尝试创建名称长度超过 15 个字符的 collection,例如 hello_milvus_backup,它会产生错误,但 hello_milvus 不会。错误信息会确认最大名称长度已成功调整为 15:
图 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。部署开始前,我们需要完成以下准备:
- 创建一个空文件夹,并命名为
004-docker-compose-monitor。 - 进入新创建的文件夹,并创建一个新的
prometheus.yaml文件,内容如下:
global:
scrape_interval: 10s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['standalone:9091']
这里我们使用一个非常简单的配置,其中 scrape_interval 表示采集指标的间隔。值得注意的是,targets 中的地址需要填写为 standalone:9091,因为我们需要与另一个 Docker 实例交互,并使用 Docker Compose 内置网络。
- 修改
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 无法访问。
- 在本地浏览器访问:
http://localhost:3002/
你会看到登录页面,然后可以登录。用户名和密码都是 admin,这对应 docker-compose.yml 中的 GF_SECURITY_ADMIN_PASSWORD 设置。
- 为 Grafana 服务添加 data source,也就是我们的 Prometheus 实例,然后根据页面操作。
图 4.7:Grafana data source
添加 Prometheus 地址时,填写:
http://prometheus:9090
填写完成后,滚动到页面底部并点击 Save 按钮。
图 4.8:添加 Prometheus data source
- 导入 Milvus dashboard 的 JSON 文件;该文件是本书 GitHub 仓库第 4 章
004-docker-compose-monitor目录中的milvus-dashboard.json。值得注意的是,这里的 dashboard 配置文件是面向 k8s 部署的,但没关系。我们先导入 dashboard。这里主要是想了解有哪些 panels,以及如何查看它们。
图 4.9:Grafana 导入 dashboard
panel 导入后,如果没有错误信息,说明成功。预期情况下不会显示数据。我们找到 Root Coordinator panel row,然后找到 Collection Num panel。找到该 panel 后,按照下图点击进入 Explore 页面。因为我们已经在上一章成功创建过一个 collection,所以用这个 monitor 检查是否能看到该信息。
图 4.10:Grafana Explore 页面
然后,将语句修改为:
milvus_rootcoord_collection_num{}
然后点击右上角 Run Query 按钮。如果成功,你应该看到类似下图的页面:
图 4.11:Grafana collection number metric
通过上述步骤,我们已经成功连接到 Milvus metrics。需要强调的是,由于我们的 dashboard 基于 Kubernetes,如果使用 Docker Compose 部署,可能不兼容。不过这不是问题;我们只需要删除与 Kubernetes 相关的过滤条件,例如 app_kubernetes_io_instance、app_kubernetes_io_name 和 namespace。
一开始可能不清楚哪些条件与 Kubernetes 相关,但没关系。我们的重点是如何搭建完整 metrics 系统。
如果你想深入学习 metrics,例如如何编写监控查询,建议阅读相关书籍或访问 Grafana 官方网站:
https://grafana.com/docs/grafana/latest/
部署 Loki 和 Promtail
成功部署 Prometheus 和 Grafana 后,现在可以聚焦部署 Loki 和 Promtail。这些服务可以让我们有效查询日志。使用 Loki,我们可以利用 LogQL 根据各种条件过滤日志,这对故障排查非常有帮助。
- 调整 Milvus 配置,修改
milvus.yaml中的logroot 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.yaml 和 promtail-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。
图 4.12:添加 Loki data source
- 进入 Explore 页面。
图 4.13:Grafana Explore 页面
- 进入查询页面,在查询框输入:
{service_name="milvuslog"}
然后点击 Run Query,即可看到 Milvus 日志。
图 4.14:Grafana-Loki Milvus 日志
恭喜你,现在已经成功完成 Milvus 日志和监控服务的设置。
小结
本章对 Milvus 进行了概览,重点介绍了它的依赖服务、配置设置,以及相关监控和日志服务的部署。这些服务在开源项目中被广泛使用,也可以应用于各种云服务场景。
掌握这些知识后,当你在使用 Milvus 遇到相关问题时,就能够更快地处理它们,无论是通过配置不同验证规则,还是借助丰富的监控指标和强大的日志查询系统来定位问题。
至此,我们围绕 Milvus 使用部分的第一阶段讨论结束。下一章中,我们将更深入地学习 Milvus 内部各种流程设计,这会进一步提升你的理解。我相信这将是一段令人兴奋的旅程!