管理 Elasticsearch 内存并进行故障排查

随着 Elastic Cloud 提供 Observability、Security 和 Search 等解决方案,我们已将使用 Elastic Cloud 的用户范围扩大到数据工程师、安全团队和顾问。作为 Elastic 的支持代表,我乐于与各类用户和用例进行交流。

随着服务对象的增多,我接触到了更多关于管理资源分配方面的问题,特别是如何排查分配健康状况以及如何避免触发熔断器。我完全理解!当我开始使用 Elasticsearch 时,也曾遇到过同样的问题。这是我第一次接触如何管理 Java 堆和时序数据库分片,以及如何扩展自己的基础设施。

当我加入 Elastic 团队时,我除了喜欢阅读文档之外,还喜欢看博客和教程,这让我能够快速上手。但我入职的第一个月过得并不轻松,因为我要将理论知识与用户通过工单系统发送过来的错误信息联系起来。最终,和其他支持代表一样,我发现所报告的错误中有不少都属于分配问题的症状,而通过那大约七个链接,便可让用户快速上手并成功管理其资源分配。

作为支持代表,我将介绍我们向用户发送的关于资源分配管理理论的主要链接、最常见的问题症状,以及我们引导用户更新配置以解决资源分配问题的途径。

理论

作为 Java 应用程序,Elasticsearch 需要从系统的物理内存中分配一部分逻辑内存(堆)。该值通常不应超过物理内存的一半,上限为 32GB。设置更高的堆使用率通常是为了响应成本较高的查询和更大的数据存储。父级断路器默认值为 95%,但我们建议在持续达到 85% 时就扩展资源。

我强烈推荐阅读以下概述文章,以获取更多信息:

配置

默认情况下,Elasticsearch 的默认设置会根据节点角色和总内存自动调整您的 JVM 堆的大小。但如有需要,您也可以通过以下三种方式直接对其进行配置:

1. 直接在本地 Elasticsearch 文件的 config > jvm.options 文件中:

## JVM configuration

################################################################
## IMPORTANT: JVM heap size
################################################################

…

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms4g
-Xmx4g

2. 作为 docker-compose 中的 Elasticsearch 环境变量

version: '2.2'
services:
  es01:
	image: docker.elastic.co/elasticsearch/elasticsearch:7.12.0
	environment:
  	- node.name=es01
  	- cluster.name=es
  	- bootstrap.memory_lock=true
  	- "ES_JAVA_OPTS=-Xms4g -Xmx4g"
  	- discovery.type=single-node
	ulimits:
  	memlock:
    	soft: -1
    	hard: -1
	ports:
  	- 9200:9200

3. 通过“Elastic Cloud Hosted > 部署 >编辑视图”。注意:下拉菜单用于分配物理内存,其中大约一半将分配给堆内存。

ElasticSearch 热数据与内容层级

故障排查

如果您的集群当前遇到性能问题,则很可能是以下常见问题所致:

  • 配置问题:主节点过小,没有 ILM 策略
  • 引入的数据量:高请求速度/负载,存在重叠的昂贵查询/写入操作
所有以下 cURL/API 请求都可以在 Elastic Cloud Hosted > Elasticsearch API Console(Elasticsearch API 控制台)中作为 Elasticsearch API 的 cURL 发出,或者在 Kibana > “Dev Tools”(开发工具)下执行。

分配健康状况

数据索引存储在子分片中,这些分片在维护以及搜索/写入请求期间使用堆内存。分片大小不应超过 50GB。以上述 Elastic Cloud Hosted 示例为例,该示例在两个可用区中分配了 8GB 物理内存(总共将分配两个节点),让我们将其与以下示例结合:_cat/allocation

GET /_cat/allocation?v=true&h=shards,node
shards node
    41 instance-0000000001
    41 instance-0000000000

然后转到:_cluster/health

GET /_cluster/health?filter_path=status,*_shards

{
  "status": "green",
  "unassigned_shards": 0,
  "initializing_shards": 0,
  "active_primary_shards": 41,
  "relocating_shards": 0,
  "active_shards": 82,
  "delayed_unassigned_shards": 0
}

如果有任何分片报告(active_shardsactive_primary_shards 除外)的数字大于 0,则说明您已找到性能问题的原因。

如果这方面报告了问题,最常见的情况将是 unassigned_shards>0。如果这些分片是主要分片,则集群将报告为 status:red;如果只是副本分片,它将报告为 status:yellow。(这就是在索引上设置副本分片的重要之处,如果集群遇到问题,它可以恢复数据而避免数据丢失。)假设我们有一个 status:yellow,带有一个未分配的分片。为了进行调查,我们将通过 _cat/shards 来查看哪个索引分片出现问题。

GET _cat/shards?v=true&s=state
index                                 	shard prirep state    	docs   store ip       	node
logs                                  	0 	p  	STARTED     	2  10.1kb 10.42.255.40 instance-0000000001
logs                                  	0 	r  	UNASSIGNED
kibana_sample_data_logs               	0 	p  	STARTED 	14074  10.6mb 10.42.255.40 instance-0000000001
.kibana_1                             	0 	p  	STARTED  	2261   3.8mb 10.42.255.40 instance-0000000001

所以这将适用于拥有未分配副本分片的非系统索引日志。让我们通过运行 _cluster/allocation/explain 来查看问题所在。(专业提示:当您将问题升级到支持团队时,这正是我们所采取的操作。)

GET _cluster/allocation/explain?pretty&filter_path=index,node_allocation_decisions.node_name,node_allocation_decisions.deciders.*

{ "index": "logs",
  "node_allocation_decisions": [{
      "node_name": "instance-0000000005",
      "deciders": [{
          "decider": "data_tier",
          "decision": "NO",
          "explanation": "node does not match any index setting [index.routing.allocation.include._tier] tier filters [data_hot]"
}]}]}

此错误信息指向 data_hot,它是索引生命周期管理 (ILM) 策略的一部分,表明我们的 ILM 策略与当前索引设置不一致。在这种情况下,该错误的原因是设置了热温 ILM 策略,但没有指定暖节点。(我需要确保某些操作会失败,因此特意制造了这些错误示例供你们参考。有关更多信息,请参阅此示例故障排除视频了解解决方法。)

如果您在没有任何未分配分片的情况下运行此命令,则会出现 400 错误,提示“无法找到任何可解释的未分配分片”,因为此时没有问题需要报告如果您遇到非逻辑性原因(例如临时网络错误,如“节点在分配期间离开集群”),则可以使用 Elastic 提供的便捷工具 _cluster/reroute

POST /_cluster/reroute

此请求(未进行任何自定义设置)会启动一个异步后台进程,尝试分配所有当前状态为“未分配”的分片。(请您耐心等待该过程完成后再联系开发人员,别像我一样,以为该过程会瞬间完成,结果恰好在问题升级时,他们说没有问题——毕竟那时问题确实已经解决了。)有关更多信息,请参阅此故障排除视频,以了解如何监控分配健康状况

熔断器

堆内存分配达到上限会导致集群请求超时或出错,并且经常会导致集群出现熔断器异常。熔断器错误会在 elasticsearch.log 中生成如下事件:

Caused by: org.elasticsearch.common.breaker.CircuitBreakingException: [parent] Data too large, data for [<transport_request>] would be [num/numGB], which is larger than the limit of [num/numGB], usages [request=0/0b, fielddata=num/numKB, in_flight_requests=num/numGB, accounting=num/numGB]

要进行调查,请查看您的 heap.percent,可通过查看 _cat/nodes 来实现:

GET /_cat/nodes?v=true&h=name,node*,heap*
# heap = JVM (logical memory reserved for heap)
# ram  = physical memory

name                                node.role heap.current heap.percent heap.max
tiebreaker-0000000002 mv             119.8mb           23    508mb
instance-0000000001   himrst           1.8gb           48    3.9gb
instance-0000000000   himrst           2.8gb           73    3.9gb

或者,如果您之前已启用此功能,请导航至“Kibana > Stack Monitoring”。

Elasticsearch 节点

如果您确认将会触发内存熔断器,则可以考虑临时增加堆容量,为自己腾出空间进行调查。在调查根本原因时,请通过审计日志慢速日志集群日志或 elasticsearch.log 查找先前的连续事件。您将寻找:

  • 成本高昂的查询,尤其是:
    • 高分桶聚合
      • 当我发现搜索在运行查询之前会根据搜索大小或分桶维度临时分配一部分堆内存时,我感到非常愚蠢,所以设置 10,000,000 确实让我的运营团队感到非常沮丧。
    • 非优化的映射
      • 第二个让我感到愚蠢的原因是,我曾以为分层报告的数据比扁平化数据更容易搜索(事实并非如此)。
  • 请求量/速度:通常为批量或异步查询

进行扩展的时间

如果这并非您第一次遇到熔断机制,或者您怀疑这将是一个持续存在的问题(例如,内存使用率持续达到 85%,说明是时候考虑扩展资源了),那么您需要仔细查看 JVM 内存压力,将其作为长期堆内存使用情况的指标。您可以在“Elastic Cloud Hosted > 部署”中查看此指标。

Elasticsearch 实例

或者,您也可以根据 _nodes/stats 进行计算:

GET /_nodes/stats?filter_path=nodes.*.jvm.mem.pools.old

{"nodes": { "node_id": { "jvm": { "mem": { "pools": { "old": {
  "max_in_bytes": 532676608,
  "peak_max_in_bytes": 532676608,
  "peak_used_in_bytes": 104465408,
  "used_in_bytes": 104465408
}}}}}}}

其中:

JVM Memory Pressure = used_in_bytes / max_in_bytes

这种情况的一个潜在症状是:elasticsearch.log 中的垃圾收集器 (gc) 事件发生的频率高且持续时间长:

[timestamp_short_interval_from_last][INFO ][o.e.m.j.JvmGcMonitorService] [node_id] [gc][number] overhead, spent [21s] collecting in the last [40s]

如果您确认是这种情况,就需要考虑一下,要么扩展集群,要么减少对集群的需求。您需要调查/考虑:

  • 增加堆资源(堆/节点,节点数)
  • 减少分片(删除不必要的/旧的数据;使用 ILM 将数据放入热存储/冷存储以便缩小数据;对于无需担心丢失的数据,则关闭副本)

我们随时为您提供真诚帮助

哇!从我在 Elastic 支持中见到的情况来看,用户提交的工单中最常见问题包括:未分配的分片、不平衡的分片堆、熔断器、大量垃圾收集和分配错误。所有这些都是核心资源分配管理对话的症状。希望您现在也对相关理论和解决步骤有了一些了解。

不过,如果您现在仍无法解决问题,请随时与我们联系。我们将竭诚为您提供帮助!联系我们:

为非运维人员(同时也热爱运维)也能自主管理 Elastic Stack 资源分配的能力欢呼!

最初发布于 2021 年 4 月 27 日;更新于 2024 年 11 月 5 日。

本文中描述的任何功能或功能性的发布和时间均由 Elastic 自行决定。当前尚未发布的任何功能或功能性可能无法按时提供或根本无法提供。