作者:来自 Elastic Pooya Salehi, Henning Andersen, Francisco Fernández Castaño
正确调整 Elasticsearch 集群的大小并不容易。集群的最佳大小取决于集群正在经历的工作负载,而工作负载可能会随着时间的推移而变化。自动扩展会自动调整集群大小以适应工作负载,无需人工干预。它避免了仅为满足峰值使用量而为集群过度配置资源,并且还可以防止在配置不足的情况下降低集群性能。
我们依靠这种机制让 Elastic Cloud Serverless 产品的用户无需为索引层做出大小决策。摄取自动扩展需要不断估计处理传入工作负载所需的资源,并及时配置和取消配置这些资源。
在这篇博文中,我们探讨了 Elasticsearch 中的摄取自动扩展,涵盖以下内容:
Elasticsearch 中的摄取自动扩展由 Elasticsearch 本身公开的一组指标驱动。这些指标反映了索引层的摄取负载和内存需求。Elasticsearch 提供了一个自动扩展指标 API 来提供这些指标,允许外部组件监视这些指标并决定是否需要更改集群大小(参见图 1)。
在 Elastic Cloud Serverless 服务中,有一个自动扩展器组件,即 Kubernetes 控制器。自动扩展器定期轮询 Elasticsearch 自动扩展指标 API,并根据这些指标计算所需的集群大小。如果所需的集群大小与当前集群大小不同,则自动扩展器会更改集群大小以将集群中的可用资源整合到所需资源中。此更改既涉及集群中的 Elasticsearch 节点数量,也涉及每个节点可用的 CPU、内存和磁盘。
摄取自动扩展的一个重要考虑因素是,当集群收到索引负载的峰值时,自动扩展过程可能需要一些时间才能有效地调整集群大小。虽然我们试图将反应时间保持在尽可能低的水平,但它不可能是即时的。因此,在集群扩展时,如果增加的负载会导致集群不稳定问题,Elasticsearch 集群应该能够暂时推迟它收到的负载。索引负载的增加可能表现为集群需要更多资源,即 CPU、内存或磁盘。Elasticsearch 具有保护机制,允许节点推迟索引负载,如果这些资源中的任何一个成为瓶颈。
为了处理索引请求,Elasticsearch 使用专用线程池,其大小取决于节点可用的内核数量。如果增加的索引负载导致 CPU 或其他资源成为瓶颈,则传入的索引请求将排队。此队列的最大大小是有限的,当队列已满时到达节点的任何请求都将被拒绝,并显示 429 HTTP 代码。
Elasticsearch 还会跟踪处理正在进行的索引请求所需的内存,如果索引缓冲区的增长超过可用堆内存的 10%,则会拒绝传入的请求(使用 429)。这限制了用于索引的内存,并确保节点不会耗尽内存。
Elastic Cloud Serverless 产品依赖对象存储作为索引数据的主要存储。节点上的本地磁盘暂时用于保存索引数据。Elasticsearch 会定期将索引数据上传到对象存储,这样可以释放本地磁盘空间,因为我们依赖对象存储来确保索引文档的持久性。尽管如此,在高索引负载下,节点可能会在定期上传任务有机会运行并释放本地磁盘空间之前耗尽磁盘空间。为了处理这些情况,Elasticsearch 会监控可用的本地磁盘空间,并在必要时限制索引活动,同时尝试通过强制上传到对象存储而不是等待定期上传完成来释放空间。请注意,这种限制反过来会导致传入的索引请求排队。
这些保护机制允许 Elasticsearch 集群暂时拒绝请求,并向客户端提供响应,表明集群在尝试扩展时已过载。来自 Elasticsearch 的这种推回信号为客户端提供了做出反应的机会,即尽可能减少负载或重试请求,如果在集群扩展时重试,请求最终应该会成功。
Elasticsearch 中用于摄取自动扩展的两个指标是摄取负载和内存。
摄取负载表示处理当前索引负载所需的线程数。自动扩展指标 API 公开了摄取负载值列表,每个索引节点一个。请注意,由于写入线程池(处理索引请求)的大小取决于节点上的 CPU 核心数,因此这实际上决定了集群中处理索引工作负载所需的核心总数。
每个索引节点上的摄取负载由两个部分组成:
每个索引节点的摄取负载计算为所有三个写入线程池的这两个值的总和。Elasticsearch 集群的总摄取负载是各个节点的摄取负载的总和。
线程池利用率是线程池中繁忙线程数的指数加权移动平均值 ( exponentially weighted moving average - EWMA),每秒采样一次。采样的线程池利用率值的 EWMA 配置为过去 10 秒的采样值对提取负载的线程池利用率组件影响最大,而超过 60 秒的采样影响非常小。
要估计处理线程池中排队的索引请求所需的资源,我们需要估计每个排队任务需要多长时间才能执行。为此,每个线程池还提供请求执行时间的 EWMA。索引请求的请求执行时间是请求离开队列并由工作线程开始执行后完成所需的(挂钟)时间。由于某些排队是可以接受的,并且应该可以由线程池管理,因此我们尝试估计处理超额排队所需的资源。我们认为现有工作线程数最多可管理 30 秒的任务,并根据该值比例分配一个额外的线程。例如,如果平均任务执行时间为 200 毫秒,我们估计每个线程能够在 30 秒内处理 150 个索引请求,因此每 150 个排队项目分配一个额外的线程。
请注意,由于索引节点依赖于定期将索引数据推送到对象存储中,因此我们不需要根据索引数据的总大小来扩展索引层。但是,在做出自动扩展决策时需要考虑索引工作负载的磁盘 IO 要求。提取负载代表索引节点的 CPU 要求以及磁盘 IO,因为 CPU 和 IO 工作都是由写入线程池工作程序完成的,我们依靠挂钟时间来估计处理排队请求所需的时间。
每个索引节点都会计算其提取负载并定期将此值发布到主节点(master node)。主节点通过自动扩展指标 API 将每个节点的提取负载值提供给自动扩展器。
自动扩展指标 API 公开的内存指标包括节点(node)内存和层(tier)内存。节点内存表示集群中每个索引节点的最小内存要求。层内存指标表示索引层中应可用的最小总内存。请注意,这些值仅表示最小值,以确保每个节点能够处理基本索引工作负载并保存集群和索引元数据,同时确保层包含足够的节点以容纳所有索引分片。
节点内存必须至少有 500MB 才能处理索引工作负载,并且每个索引必须有固定数量的内存。这可确保所有节点都可以保存集群的元数据,其中包括每个索引的元数据。层内存是通过考虑索引字段映射的内存开销以及集群中节点上分配的每个开放分片所需的内存量来确定的。目前,每个分片的内存要求使用 6MB 的固定估计值。我们计划改进这个值。
每个索引映射的内存需求估计值由托管索引分片的数据节点之一计算。计算出的估计值将发送到主节点(master node)。每当发生映射更改时,此估计值都会更新并再次发布到主节点。主节点根据这些信息通过自动缩放指标 API 向自动缩放器提供节点和总内存指标。
自动扩展器负责通过公开的指标监控 Elasticsearch 集群,计算适合索引工作负载的理想集群大小,并相应地更新部署。这是通过根据提取负载和内存指标计算所需的总 CPU 和内存资源来完成的。每个节点的所有提取负载值的总和决定了索引层所需的 CPU 核心总数。
计算出的 CPU 要求和提供的最小节点和层内存资源被映射到一组预定的集群大小。每个集群大小决定了节点数以及每个节点的 CPU、内存和磁盘大小。特定集群大小内的所有节点都具有相同的硬件规格。CPU、内存和磁盘之间存在固定的比率,因此始终线性扩展所有 3 种资源。索引层的现有集群大小基于从 4GB/2vCPU/100GB 磁盘到 64GB/32vCPU/1600GB 磁盘的节点大小。一旦 Elasticsearch 集群扩展到最大节点大小(64GB 内存),任何进一步的扩展都会添加新的 64GB 节点,从而使集群可以扩展到 32 个 64GB 节点。请注意,这不是集群中 Elasticsearch 节点数量的硬性上限,可以根据需要增加。
自动扩缩器每 5 秒轮询一次主节点的指标,计算所需的集群大小,如果与当前集群大小不同,它会相应地更新 Elasticsearch Kubernetes 部署。请注意,实际将部署与所需的集群大小进行协调以及添加和删除 Elasticsearch 节点以实现此目的都是由 Kubernetes 完成的。为了避免集群大小发生非常短暂的变化,我们在缩减期间计算所需集群大小时会考虑 10% 的余量,并且只有在过去 15 分钟内所有所需的集群大小计算都表明缩减时,缩减才会生效。
目前,指标增加到第一个 Elasticsearch 节点添加到集群并准备好处理索引负载所需的时间不到 1 分钟。
在这篇博文中,我们解释了 Elasticsearch 中的采集自动扩展是如何工作的,涉及的不同组件,以及用于量化处理索引工作量所需资源的指标。我们认为,这种自动扩展机制对于通过在必要时自动增加集群中的可用资源来减少用户的 Elasticsearch 集群的运营开销至关重要。此外,当集群中的可用资源不再需要时,通过缩小集群规模可以降低成本。
准备好自己尝试一下了吗?开始免费试用。
想要获得 Elastic 认证吗?了解下一期 Elasticsearch 工程师培训何时开始!
原文:Elasticsearch ingest autoscaling — Search Labs