记录 Elasticsearch 的 maximum shards open 问题

问题背景

某天打开 Jaeger UI 后,发现里面没有任何数据了,这是个奇怪的问题。

然后立马上服务器检查了 jaeger-collector, jaeger-agent, jaeger-query 和 Elasticsearch 的服务进程、端口及网络通信。所有一切都正常。

然后进一步排查数据流向问题,通过排查 jaeger-collector 日志,发现 jaeger-agent -> jaeger-collector 之间的数据传输没有问题。

而 jaeger-collector -> ES 之间数据传输时报错了。错误如下:

{"level":"error","ts":1576483292.2617185,"caller":"config/config.go:130","msg":"Elasticsearch part of bulk request failed","map-key":"index","response":{"_index":"jaeger-span-2019-12-16","_type":"_doc","status":400,"error":{"type":"validation_exception","reason":"Validation Failed: 1: this action would add [10] total shards, but this cluster currently has [992]/[1000] maximum shards open;"}},"stacktrace":"github.com/jaegertracing/jaeger/pkg/es/config.(*Configuration).NewClient.func2\n\t/home/travis/gopath/src/github.com/jaegertracing/jaeger/pkg/es/config/config.go:130\ngithub.com/jaegertracing/jaeger/vendor/github.com/olivere/elastic.(*bulkWorker).commit\n\t/home/travis/gopath/src/github.com/jaegertracing/jaeger/vendor/github.com/olivere/elastic/bulk_processor.go:588\ngithub.com/jaegertracing/jaeger/vendor/github.com/olivere/elastic.(*bulkWorker).work\n\t/home/travis/gopath/src/github.com/jaegertracing/jaeger/vendor/github.com/olivere/elastic/bulk_processor.go:501"}

提取关键错误信息

this action would add [10] total shards, but this cluster currently has [992]/[1000] maximum shards open

根据报错,可以看出,目前集群的shard数量已经是992个,集群最大索引为1000个,将要添加的shard 数量超越了集群管理的最大值,所以数据无法写入。

1000 个shards的限制是怎么来的?

根据官方解释,从Elasticsearch v7.0.0 开始,集群中的每个节点默认限制 1000 个shard,如果你的es集群有3个数据节点,那么最多 3000 shards。这里我们是开发环境,只有一台es。所以只有1000。

ES 基本概念

如果您是Elasticsearch的新手,那么了解基本术语并掌握基本概念非常重要。

Elasticsearch集群的简单图

image

群集– Elasticsearch群集由一个或多个节点组成,并且可以通过其群集名称进行标识。

节点–一个Elasticsearch实例。在大多数环境中,每个节点都在单独的盒子或虚拟机上运行。

index–在Elasticsearch中,索引是文档的集合。

分片–由于Elasticsearch是分布式搜索引擎,因此索引通常会分为多个元素,这些元素称为分片,分布在多个节点上。Elasticsearch自动管理这些分片的排列。它还会根据需要重新平衡分片,因此用户无需担心细节。

副本–默认情况下,Elasticsearch为每个索引创建五个主要分片和一个副本。这意味着每个索引将包含五个主要分片,并且每个分片将具有一个副本。

分配多个分片和副本是分布式搜索功能设计的本质,它提供了高可用性并可以快速访问索引中的文档。主分片和副本分片之间的主要区别在于,只有主分片才能接受索引请求。副本和主分片都可以满足查询请求。

在上图中,我们有一个Elasticsearch集群,该集群由默认分片配置中的两个节点组成。Elasticsearch会自动在两个节点之间排列五个主要分片。每个主碎片都有一个副本碎片,但是这些副本碎片的排列与主要碎片的排列完全不同。

请记住,number_of_shards值与索引有关,而不与整个集群有关。此值指定每个索引的分片数量(而不是集群中的总主分片)。

副本主要是为了提高搜索性能,用户可以随时添加或删除它们。它们为您提供了额外的容量,更高的吞吐量和更强的故障转移。我们始终建议生产集群具有2个副本以进行故障转移。

解决方案

找到了问题原因,那么如何解决这个问题?

解决这个问题需要回答两个问题:

一,每个 Index 多少个 Shard 合适?

配置 Elasticsearch 集群后,对于分片数,是比较难确定的。因为一个索引分片数一旦确定,以后就无法修改,所以我们在创建索引前,要充分的考虑到,以后我们创建的索引所存储的数据量,否则创建了不合适的分片数,会对我们的性能造成很大的影响。

如果以后发现有必要更改分片的数量,则需要重新索引所有源文档。(尽管重新编制索引是一个漫长的过程,但可以在不停机的情况下完成)。

主分片配置与硬盘分区非常相似,在硬盘分区中,原始磁盘空间的重新分区要求用户备份,配置新分区并将数据重写到新分区上。

稍微过度分配是好的。但是如果你给每个 Index 分配 1000 个Shard 那就是不好的。

请记住,您分配的每个分片都需要支付额外费用:

  • 由于分片本质上是Lucene索引,因此会消耗文件句柄,内存和CPU资源。
  • 每个搜索请求都将触摸索引中每个分片的副本,当分片分布在多个节点上时,这不是问题。当分片争夺相同的硬件资源时,就会出现争用并且性能会下降。

我们的客户期望他们的业务增长,并且其数据集会相应地扩展。因此,始终需要应急计划。许多用户说服自己,他们将遇到爆炸性增长(尽管大多数用户从未真正看到无法控制的增长)。此外,我们所有人都希望最大程度地减少停机时间并避免重新分片。

如果您担心数据的快速增长,那么我们建议您关注一个简​​单的约束:Elasticsearch最大JVM堆大小建议约为30-32GB。这是对绝对最大分片大小限制的可靠估计。例如,如果您确实认为可以达到200GB(但在不更改其他基础架构的情况下无法达到更大容量),那么我们建议分配7个分片,或最多8个分片

绝对不要为从现在起三年后达到的太高的10 TB目标分配资源。

如果现在你的场景是分片数不合适了,但是又不知道如何调整,那么有一个好的解决方法就是按照时间创建索引,然后进行通配查询。如果每天的数据量很大,则可以按天创建索引,如果是一个月积累起来导致数据量很大,则可以一个月创建一个索引。如果要对现有索引进行重新分片,则需要重建索引.

修改默认的 Elasticsearch 分片数

这是正确的改变配置文件中的index.number_of_shards默认值将涉及更改所有节点上的设置,然后理想地按照rolling restarts的指导方针重新启动实例。

但是,如果这不是一个选项,如果在创建新索引时在设置中明确指定number_of_shards并不理想,那么解决方法将使用index templates

可以创建index_defaults默认值,如下所示

PUT /_template/index_defaults 
{
  "template": "*", 
  "settings": {
    "number_of_shards": 4
  }
}

这会将index_defaults模板中指定的设置应用于所有新索引。

重建索引

参考:教你如何在 elasticsearch 中重建索引

二,每个节点的 maximum shards open 设置为多大合适?

对于分片数的大小,业界一致认为分片数的多少与内存挂钩,认为 1GB 堆内存对应 20-25 个分片。因此,具有30GB堆的节点最多应有600个分片,但是越低于此限制,您可以使其越好。而一个分片的大小不要超过50G,通常,这将有助于群集保持良好的运行状况。

三,具体措施

我的观点是开发、测试环境,如果数据不那么重要的话,可以清空所有 index

DELETE /_all

官方文档:删除索引

然后,重新设置默认值,降低number_of_shards数量,同时提高max_shards_per_node的数量。

vim elasticsearch.yml

# Set the number of shards (splits) of an index (5 by default):
#
index.number_of_shards: 2
# Set the number of replicas (additional copies) of an index (1 by default):
#
index.number_of_replicas: 1

cluster.max_shards_per_node: 3000

对于生产环境

vim elasticsearch.yml

# Set the number of shards (splits) of an index (5 by default):
#
index.number_of_shards: 5
# Set the number of replicas (additional copies) of an index (1 by default):
#
index.number_of_replicas: 2

cluster.max_shards_per_node: 3000

Index 的配置参数在es配置文件里直接配置,会报错。不过可以用Kibana来设置。

打开 console

记录 Elasticsearch 的 maximum shards open 问题_第1张图片

DELETE /_all

PUT _template/default
{
    "index_patterns" : ["jaeger*"],
    "order" : 1,
    "settings": {
        "number_of_shards": "2",
        "number_of_replicas": "1"
    }
}

PUT _template/default1
{
    "index_patterns" : ["*"],
    "order" : 0,
    "settings": {
        "number_of_shards": "5",
        "number_of_replicas": "2"
    }
}

PUT /_cluster/settings
{
  "transient": {
    "cluster": {
      "max_shards_per_node":10000
    }
  }
}

order

(Optional,integer) Order in which Elasticsearch applies this template if index matches multiple templates.

  • Templates with lowerordervalues are merged first.
  • Templates with higherordervalues are merged later, overriding templates with lower values.

Put index template API

可以发现生效了

$ curl -X GET 'http://127.0.0.1:9200/\_cat/indices?v'

health status index uuid  pri rep docs.count docs.deleted store.size pri.store.size

yellow open  jaeger-span-2019-12-17 2DHx3EaGTnKlVC4mefsUJw  2  1  27 0  22kb  22kb

yellow open  .kibana SeD9KqnhR7aKpzc2\_AcIDw  2  1 1 0 4.3kb 4.3kb

最重要的是,要定期删除无用数据,比如对于jaeger的数据,可以每个月初删除一个月前的所有数据,即只保留最近1个月的Index数据。

仅保存近30天的数据任务分解为

  • delete_by_query设置检索近30天数据;
  • 执行forcemerge操作,手动释放磁盘空间。
#!/bin/sh
curl -XPOST "http://127.0.0.1:9200/jaeger-*/_delete_by_query?conflicts=proceed" -H'Content-Type:application/json' -d'{
    "query": {
        "range": {
            "pt": {
                "lt": "now-100d",
                "format": "epoch_millis"
            }
        }
    }
}
'

force merge API

这里有3个参数可以用

  • max_num_segments 期望merge到多少个segments,1的意思是强行merge到1个segment
  • only_expunge_deletes 只做清理有deleted的segments,即瘦身
  • flush 清理完执行一下flush,默认是true

forcemerge 脚本如下:

#!/bin/sh
curl -XPOST 'http://127.0.0.1:9200/_forcemerge?only_expunge_deletes=true&max_num_segments=1'

有没有更简便的方法?

有,使用ES官网工具——curator工具。

curator 简介

主要目的:规划和管理ES的索引。支持常见操作:创建、删除、合并、reindex、快照等操作。curator 官网地址

curator 适用场景

最重要的是:

  • 仅以删除操作为例:curator可以非常简单地删除x天后的索引。不过前提是:索引命名要遵循特定的命名模式——如:以天为命名的索引:

    jaeger-span-2019-09-20 
    jaeger-span-2019-10-07 
    jaeger-service-2019-10-11
  • 命名模式需要和action.yml中的delete_indices下的timestring对应。

四,临时提高阈值

通过ES API零时修改

curl -X PUT "dev-jaeger-es01.bj:9200/_cluster/settings" -H 'Content-Type: application/json' -d'
{
    "persistent" : {
        "cluster.max_shards_per_node" : "5000"
    }
}
'

也可在kibanatools中改变设置

PUT /_cluster/settings
{
  "transient": {
    "cluster": {
      "max_shards_per_node":10000
    }
  }
}

参考

Optimizing Elasticsearch: How Many Shards per Index?
How many shards should I have in my Elasticsearch cluster?
从 10 秒到 2 秒!ElasticSearch 性能调优
Elasticsearch调优实践
索引设置

你可能感兴趣的:(elasticsearch,jaeger,golang)