如何调试一个无响应的Elasticsearch集群

321 阅读15分钟

Elasticsearch是一个开源的搜索引擎和分析存储,被各种应用所使用,从电子商务商店的搜索,到使用ELK栈的内部日志管理工具("Elasticsearch, Logstash, Kibana "的缩写)。作为一个分布式数据库,你的数据被分割成 "分片",然后被分配到一个或多个服务器。

由于这种分片,对Elasticsearch集群的读或写请求需要在多个节点之间进行协调,因为在单个服务器上没有你数据的 "全局视图"。虽然这使得Elasticsearch具有高度的可扩展性,但也使得它的设置和调整比其他流行的数据库如MongoDB或PostgresSQL要复杂得多,后者可以在单台服务器上运行。

当可靠性问题出现时,如果你的Elasticsearch设置是错误的或不稳定的,消防工作会很有压力。你的事件可能会影响到客户,这可能会对收入和你的企业声誉产生负面影响。快速的补救措施是很重要的,然而在事故或中断期间,花大量的时间在网上研究解决方案并不是大多数工程师所能做到的。本指南旨在为工程师运行中可能导致Elasticsearch问题的常见问题以及需要注意的事项提供一个小抄。

作为一个通用的工具,Elasticsearch有成千上万种不同的配置,这使它能够适应各种不同的工作负载。即使是在网上发布的,对某家公司有效的数据模型或配置也不一定适合你。让Elasticsearch扩展并没有什么灵丹妙药,需要勤奋的性能测试和试验/错误。

无响应的Elasticsearch集群问题

集群的稳定性问题是最难调试的,尤其是当你的数据量或代码库没有任何变化的时候。

检查集群状态的大小

它的作用是什么?

  • Elasticsearch集群状态跟踪我们集群的整体状态,是控制流量和集群的核心。集群状态包括集群中节点的元数据、分片的状态以及它们如何被映射到节点、索引映射(即模式)等。
  • 集群状态通常不会经常改变。然而,某些操作,如在索引映射中添加一个新字段,会触发更新。
  • 因为集群更新会广播给集群中的所有节点,所以它应该是小的(<100MB)。
  • 一个大的集群状态会很快使集群不稳定。这种情况发生的常见方式是通过映射爆炸(索引中太多的键)或太多的索引。

要寻找的东西

  • 使用下面的命令下载集群状态,看看返回的JSON的大小。
curl -XGET 'http://localhost:9200/_cluster/state'

如何修复

  • 看一下数据是如何被索引的。一个常见的映射爆炸发生的方式是当高cardinality标识符被用作JSON键。每次看到一个新的键,如 "4 "和 "5",集群的状态就会被更新。例如,下面的JSON将很快导致Elasticsearch的稳定性问题,因为每个键都被添加到全局状态中。
  {
 "1": {
   "status": "ACTIVE"
 },
 "2": {
   "status": "ACTIVE"
 },
 "3": {
   "status": "DISABLED"
 }
  }

要解决这个问题,请将你的数据扁平化为对Elasticsearch友好的东西。

{
  [    {      "id": "1",      "status": "ACTIVE"    },    {      "id": "2",      "status": "ACTIVE"    },    {      "id": "3",      "status": "DISABLED"    }  ]
}

检查Elasticsearch的任务队列

它是做什么的?

  • 当对Elasticsearch提出请求时(索引操作、查询操作等),它首先被插入到任务队列中,直到有工作线程可以接替它。
  • 一旦工作者池有了空闲的线程,它就会从任务队列中拿起一个任务并进行处理。
  • 这些操作通常是由你通过HTTP请求在:9200:9300 端口进行的,但它们也可以是内部的,以处理索引的维护任务
  • 在一个特定的时间,可能有成百上千的飞行操作,但应该很快完成(如微秒或毫秒)。

要寻找的东西

  • 运行下面的命令,寻找那些卡住运行很长时间的任务,如几分钟或几小时。

  • 这意味着有什么东西在饿着集群,阻止它取得进展。

  • 对于某些长期运行的任务,如移动索引,需要很长的时间,这是可以的。然而,正常的查询和索引操作应该是快速的。

    curl -XGET 'http://localhost:9200/_cat/tasks?detailed'
    
  • 通过?detailed 参数,你可以获得更多关于目标索引和查询的信息。

  • 找出哪些任务一直在列表的顶部的模式。它是同一个索引吗?是同一个节点吗?

  • 如果是的话,也许是该索引的数据出了问题,或者该节点超载了。

如何修复

  • 如果请求量高于正常值,那么看看如何优化请求(如使用批量API或更有效的查询/写入)。
  • 如果请求量没有变化并且看起来是随机的,这意味着有其他东西拖慢了集群。任务的备份只是一个更大问题的症状。
  • 如果你不知道这些请求来自哪里,可以在Elasticsearch客户端上添加 X-Opaque-Id头,以识别哪些客户端在触发查询。

检查Elasticsearch挂起的任务

它的作用是什么?

  • 待定任务是对集群状态的待定更新,如创建一个新的索引或更新其映射。
  • 与之前的任务队列不同,待定更新需要一个多步骤的握手来向集群中的所有节点广播更新,这可能需要一些时间。
  • 在给定的时间内,飞行中的任务应该几乎为零。请记住,像快照恢复这样昂贵的操作可能会导致这个数字暂时飙升。

需要注意的是

  • 运行该命令,确保没有或很少有飞行中的任务。

    curl curl curl -XGET 'http://localhost:9200/_cat/pending_tasks'
    
  • 如果它看起来是一个持续的集群更新流,并迅速完成,看看什么可能会触发它们。是映射爆炸还是创建了太多的索引?

  • 如果只是几个,但它们似乎被卡住了,看看主节点的日志和指标,看看是否有任何问题。例如,主节点是否遇到了内存或网络问题,以至于它无法处理集群更新?

热线程

它是做什么的?

  • 热线程API是一个有价值的内置剖析器,可以告诉你Elasticseach在哪里花费了最多的时间。
  • 这可以提供一些洞察力,比如Elasticsearch是否在索引刷新上花费了太多时间,或者执行了昂贵的查询。

要找的东西

  • 热线程API进行调用。为了提高准确性,建议使用?snapshots参数捕获许多快照。

    curl -XGET 'http://localhost:9200/_nodes/hot_threads?snapshots=1000'
    
  • 这将返回快照拍摄时的堆栈痕迹。

  • 在许多不同的快照中寻找相同的堆栈。例如,你可能会看到这样的文字:5/10 snapshots sharing following 20 elements 。这意味着一个线程在5个快照期间在代码的那个区域花费了时间。

  • 你还应该看一下CPU%。如果一个区域的代码既有高的快照共享,也有高的CPU%,这就是一个热门的代码路径。

  • 通过查看代码模块,拆解Elasticseach在做什么。

  • 如果你看到等待或停车状态,这通常是可以的。

如何修复

  • 如果大量的CPU时间花在索引刷新上,那么尝试增加刷新间隔,超过默认的1秒。
  • 如果你在缓存中看到大量的数据,也许你的默认缓存设置是次优的,导致严重的错过。

内存问题

检查Elasticsearch Heap/Garbage Collection

它是做什么的?

  • 作为一个JVM进程,堆是存储大量Elasticsearch数据结构的内存区域,需要垃圾收集周期来修剪旧对象。
  • 对于典型的生产设置,Elasticsearch在启动时使用mlockall 锁定所有的内存,并禁止交换。如果你没有这样做,现在就做吧。
  • 如果一个节点的Heap持续高于85%或90%,这意味着我们已经接近于没有内存了。

要寻找的东西

  • 在Elasticsearch日志中搜索collecting in the last 。如果出现这些情况,这意味着Elasticsearch在垃圾收集上花费了更多的开销(这占用了其他生产任务的时间)。
  • 只要Elasticsearch没有把大部分的CPU周期花在垃圾收集上,偶尔出现一些这样的情况是可以的(计算花在收集上的时间相对于整个时间的百分比)。
  • 一个在垃圾收集上花费100%时间的节点是停滞不前的,无法取得进展。
  • 看起来有超时等网络问题的节点,实际上可能是由于内存问题。这是因为在垃圾收集周期中,节点无法响应传入的请求。

如何修复

  • 最简单的方法是添加更多的节点来增加集群的可用堆。然而,Elasticsearch需要时间来重新平衡分片到空节点上。

  • 如果只有一小部分节点有很高的堆使用率,你可能需要更好地平衡你的客户。例如,如果你的分片大小差异很大,或者有不同的查询/索引带宽,你可能为同一组节点分配了太多的热分片。要移动一个分片,请使用重新路由API。只要调整分片意识的灵敏度,以确保它不会被移回。

    curl -XPOST -H "Content-Type: application/json" localhost:9200/_cluster/reroute -d '
    {
    "commands": [
        {
          "move": {
            "index": "test", "shard": 0,
            "from_node": "node1", "to_node": "node2"
          }
        }
      ]
    }'
    
  • 如果你正在向Elasticsearch发送大量的请求,试着减少批处理的大小,使每个批处理在100MB以下。虽然较大的批次有助于减少网络开销,但它们需要分配更多的内存来缓冲请求,而这些内存在请求完成后和下一个GC周期才会被释放。

检查Elasticsearch的旧内存压力

它的作用是什么?

  • 旧内存池包含了那些已经在多个垃圾收集周期中存活下来的对象,是长寿的对象。
  • 如果旧内存超过75%,你可能要注意它。当它填满超过85%时,更多的GC循环将发生,但这些对象无法被清理掉。

需要注意的是

  • 看一下旧池使用量/旧池最大值。如果它超过了85%,那就值得注意了。

如何解决

  • 你是否急于加载大量的Fieldddata?这些数据会在内存中停留很长时间。
  • 你是否在执行许多长期运行的分析任务?某些任务应该被卸载到分布式计算框架中,比如Apache Spark,用于地图/还原操作。

检查Elasticsearch字段数据大小

它是做什么的?

  • FieldData用于计算一个字段的聚合,如terms 聚合
  • 通常情况下,一个字段的fielddata不会被加载到内存中,直到第一次对其进行聚合时。
  • 然而,如果设置了eager_load_ordinals ,这也可以在索引刷新时预先计算。

要看什么

  • 像这样看一个索引或所有索引的字段数据大小。

    curl -XGET 'http://localhost:9200/index_1/_stats/fielddata'
    
  • 如果我们在错误的数据类型上使用一个索引,它可能有非常大的字段数据结构。你是否在非常高cardinality的字段上执行聚合,如UUID或跟踪ID?Fielddata不适合用于非常高cardinality的字段,因为它们会产生大量的fielddata结构。

  • 你是否有很多设置了eager_load_ordinals 的字段,或者为fielddata缓存分配了大量的内容?这将导致在刷新时间与查询时间产生的fielddata。虽然它可以加速聚合,但如果你在索引刷新时计算许多字段的fielddata,并且在查询中从不消耗它,那就不是最佳选择。

如何修复

  • 对你的查询或映射进行调整,不要在非常高的cardinality key上聚合。
  • 审核你的映射,以减少设置为 "true "的数量,eager_load_ordinals

Elasticsearch网络问题

节点离开或节点断开连接

它的作用是什么?

  • 如果一个节点不响应请求,它最终会被从集群中删除。
  • 这使得碎片可以被复制到其他节点上,以满足复制因素,并确保高可用性,即使一个节点被移除。

要看什么

  • 看一下主节点的日志。即使有多个主节点,你也应该看一下当前当选的主节点。你可以使用节点的API或者像Cerebro这样的工具来做这件事。
  • 看看是否有一个一致的节点超时或有问题。例如,你可以通过在主节点的日志中寻找短语pending nodes ,看看哪些节点还在等待集群更新。
  • 如果你看到同一个节点不断被添加,但又被删除,这可能意味着该节点过载或无响应。
  • 如果你不能从你的主节点到达该节点,这可能意味着一个网络问题。你也可能是遇到了网卡或CPU带宽的限制。

如何修复

  • 在设置transport.compression 为真时进行测试。这将压缩节点之间的流量(比如从摄取节点到数据节点),减少网络带宽,牺牲CPU带宽。
  • 注意:早期的版本把这个设置称为transport.tcp.compression
  • 如果你也有内存问题,试着增加内存。一个节点可能会因为在垃圾收集上花费大量时间而变得没有反应。

没有足够的主节点问题

它是做什么的?

  • 主节点和其他节点需要互相发现以形成一个集群。
  • 在第一次启动时,你必须提供一组静态的主节点,这样你就不会有大脑分裂的问题
  • 然后,只要主节点存在,其他节点就会自动发现这个集群。

需要注意的是

  • 启用跟踪记录以审查与发现有关的活动。

    curl -XPUT -H "Content-Type: application/json" localhost:9200/_cluster/_settings -d '
    {
      "transient": {"logger.discovery.zen":"TRACE"}
    }'
    
  • 查看配置,如minimum_master_nodes (如果比6.x老)。

  • 查看初始主节点列表中的所有主节点是否可以相互ping。

  • 审查你是否有法定人数,这应该是number of master nodes / 2 +1 。如果你少于法定人数,将不会发生集群状态的更新以保护数据的完整性。

如何修复

  • 有时网络或DNS问题会导致原始主节点无法到达。
  • 检查你是否有至少number of master nodes / 2 +1 主节点目前正在运行。

分区分配错误

Elasticsearch处于黄色或红色状态(未分配的分片)。

它是做什么的?

  • 当一个节点重启或集群恢复开始时,分片不会立即可用。
  • 恢复是节制的,以确保集群不会被淹没。
  • 黄色状态意味着主索引已被分配,但二级(副本)分片尚未被分配。虽然黄色索引既可读又可写,但可用性会下降。黄色状态通常是可以自我修复的,因为集群会复制分片。
  • 红色索引意味着主要分片没有被分配。这可能是短暂的,如在快照恢复操作期间,但也可能意味着重大问题,如丢失数据。

要寻找的东西

  • 看看为什么停止分配的原因。

    curl -XGET 'http://localhost:9200/_cluster/allocation/explain'
    
    curl -XGET 'http://localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason'
    
  • 获取红色索引的列表,了解哪些索引导致了红色状态。只要至少有一个索引是红色的,集群状态就会处于红色状态。

    curl -XGET 'http:localhost:9200/_cat/indices' | grep red
    
  • 对于单个索引的更多细节,你可以看到违规索引的恢复状态。

    curl -XGET 'http:localhost:9200/index_1/_recovery'
    

如何修复

  • 如果你看到max_retries (也许集群在分配过程中很忙)的超时,你可以暂时增加断路器阈值(默认是5)。一旦数字高于断路器,Elasticsearch将开始初始化未分配的分片。

    curl -XPUT -H "Content-Type: application/json" localhost:9200/index1,index2/_settings -d '
    {
      "index.allocation.max_retries": 7
    }'
    

Elasticsearch磁盘问题

索引是只读的

它是做什么的?

  • Elasticsearch有三个基于磁盘的水印,影响分片分配。cluster.routing.allocation.disk.watermark.low 水印可以防止新的分片被分配到磁盘已满的节点上。默认情况下,这是85%的磁盘使用量。
  • cluster.routing.allocation.disk.watermark.high 水印将迫使集群开始将分片从该节点转移到其他节点。默认情况下,这是90%。这将开始移动数据,直到低于高水印。如果Elasticsearch磁盘超过洪水阶段的水印cluster.routing.allocation.disk.watermark.flood_stage ,这时磁盘就会变得非常满,在磁盘耗尽空间之前,移动速度可能不够快。当达到时,索引被置于只读状态,以避免数据损坏。

需要注意的是

  • 看看你的每个节点的磁盘空间。

  • 查看节点的日志,看是否有类似下面的信息。

    high disk watermark [90%] exceeded on XXXXXXXX free: 5.9gb[9.5%], shards will be relocated away from this node
    
  • 一旦达到洪水阶段,你会看到像这样的日志。

    flood stage disk watermark [95%] exceeded on XXXXXXXX free: 1.6gb[2.6%], all indices on this node will be marked read-only
    
  • 一旦发生这种情况,该节点上的索引是只读的。

  • 为了确认,看看哪些索引的read_only_allow_delete 设置为真。

    curl -XGET 'http://localhost:9200/_all/_settings?pretty' | grep read_only
    

如何修复

  • 首先,清理磁盘空间,如删除本地日志或临时文件。

  • 要删除这个只读块,要做命令。

    curl -XPUT -H "Content-Type: application/json" localhost:9200/_all/_settings -d '
    {
      "index.blocks.read_only_allow_delete": null
    }'
    

结论

解决稳定性和性能问题可能是一个挑战。找到根本原因的最好方法是使用假设的科学方法,并证明其正确或不正确。使用这些工具和Elasticsearch管理API,你可以深入了解Elasticsearch的性能以及问题可能出现的地方。