我们打开kibana的时候,有时候能看到“X 个分片有 Y 个失败”的错误信息,这通常是个别索引存在未分配分片所导致的问题。 这时候我们的ES节点状态通常是Yellow或是Red状态。
先了解一下ES Shard的4种状态:
-
INITIALIZING
- 分片初始化状态,该状态下shard不可用。新建索引或刚启动节点的时候短暂的发生。 -
RELOCATING
- 新增节点或节点掉线的时候会重新分配shard导致shard的移动,也通常是短暂的。 -
STARTED
- shard活跃状态,该状态下可接收请求 -
UNASSIGNED
- shard分配失败
那什么情况下shard会分配失败?
分片无法分配通常有如下几个原因:
- 副本分片数设置过大,节点不够用于分配
- 节点掉线时的延时机制
- 分片数据丢失
- 节点磁盘空间不够用
- 集群中存在多个ES版本
首先查看集群状态 & 确认分片失败原因
首先通过Cluster health API,查看集群状态以及整体的分片的分配情况。
1
GET /_cluster/health\?pretty
返回如下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"cluster_name" : "my-application",
"status" : "yellow",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 565,
"active_shards" : 565,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 60,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 90.4
}
重点关注status,unassigned_shards,active_shards_percent_as_number, delayed_unassigned_shards等几个数据。
status为集群状态,有三个状态
- green - 所有分片已分配
- yellow - 所有主分片已分配,部分副本分片尚未分配,这时候如果某个节点发生故障部分数据将不可用
- red - 部分主分片尚未分配,部分数据将不可用,集群刚启动后分配主分片的时候也可能短暂发生
unassigned_shards
是尚未分配的分片数量,当看到分片失败提示的时候,这里是会有数据的。
active_shards_percent_as_number
是已分配分片的百分比,数值越小说明未分配的分片数量越多。
delayed_unassigned_shards
是节点掉线后,等待延时时间(index.unassigned.node_left.delayed_timeout
,默认1分钟)的待分配的shard数
然后通过cat shards API查看所有分片的分配情况
1
GET _cat/shards?h=index,shard,prirep,state,unassigned.reason
或通过shell只查看未分配的分片
1
curl -X GET localhost:9200/_cat/shards?h=index,shard,prirep,state,unassigned.reason| grep UNASSIGNED
查看分片未分配的具体原因:
1
GET /_cluster/allocation/explain?pretty
会有如下输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"index" : "my-index",
"shard" : 2,
"primary" : false,
"current_state" : "unassigned",
"unassigned_info" : {
"reason" : "CLUSTER_RECOVERED",
"at" : "2023-02-06T06:34:22.345Z",
"last_allocation_status" : "no_attempt"
},
"can_allocate" : "no",
"allocate_explanation" : "cannot allocate because allocation is not permitted to any of the nodes",
"node_allocation_decisions" : [
{
"node_id" : "dntxO1EFQVSzk7A4n45OLQ",
"node_name" : "node-1",
"transport_address" : "52.208.205.70:9300",
"node_attributes" : {
"ml.machine_memory" : "33737449472",
"xpack.installed" : "true",
"ml.max_open_jobs" : "20",
"ml.enabled" : "true"
},
"node_decision" : "no",
"deciders" : [
{
"decider" : "same_shard",
"decision" : "NO",
"explanation" : "the shard cannot be allocated to the same node on which a copy of the shard already exists [[my-index][2], node[dntxO1EFQVSzk7A4n45OLQ], [P], s[STARTED], a[id=ag-Komw1QJu9EKYkrsrdmw]]"
}
]
}
]
}
知道怎么确认原因了,下面看看各分片分配失败的情况及通常的解决方法。
如何解决?
副本分片数设置过大,节点不够用于分配
shard分为两种,一种是primary shard(主分片),而另一种是replica shard(副本分片)。每个索引文档都将归属于某个primary shard,primary shard数量由number_of_shards
属性来决定。这些primary shard一般会有各自的副本,具体的副本数量是由number_of_replicas
这个属性来指定的。我们通常是通过index template(索引模板)或创建索引的时候指定这些属性。
而当分配replica shard的时候,有一个重要的规则就是主节点不会将replica shard分配到其primary shard所在的节点上。虽说replica shard会提升查询性能,它存在的主要目的是创建冗余数据来实现高的可用。意思是当你的副本数大于等于节点数的时候,你的replica shard没地方可去。虽然对服务没有影响,但ES判断这种状态是不健康的。毕竟我们预期的number_of_replicas
没能分配上。最简单又比较常见的case就是单节点而设置number_of_replicas
为1(默认值)。
上一步执行的/_cluster/allocation/explain
结果就是这个场景,能看到如下错误:
1
the shard cannot be allocated to the same node on which a copy of the shard already exists
这时候解决方法是增加节点数或减少副本数。如下:
1
2
3
4
5
6
PUT /my-index-000001/_settings
{
"index" : {
"number_of_replicas" : 0
}
}
节点掉线时的延时机制
当节点掉线时会发生大规模的分片移动,这时候性能开销有点大成本有点高。集群环境中通常会发生短暂的网络故障,延迟什么的。如果每次发生网络延迟的时候都全部重新分配分片未免大动干戈了。为了解决这个问题ES有节点掉线时的分片延时分配机制。延时时间由index.unassigned.node_left.delayed_timeout
属性(默认1分钟)控制
如果我们确定某个节点要永久剔除,可以将index.unassigned.node_left.delayed_timeout
属性设置为0,让ES立即执行重新分配:
1
2
3
4
5
6
PUT _all/_settings
{
"settings": {
"index.unassigned.node_left.delayed_timeout": "0"
}
}
可根据实际需求增减该值。
分片数据丢失
假如我们有一个节点有尚未分配的primary shard,在没有任何副本的情况下节点宕机。这时候集群在全局的cluster state文件中检测到primary shard而在所有节点中没有找到该数据。这时候执行Cluster allocation explain API能看到如下错误:
1
"allocate_explanation" : "cannot allocate because a previous copy of the primary shard existed but can no longer be found on the nodes in the cluster",
这时候我们需要做个决策,让问题节点重新加入到集群,或通过 Cluster Reroute API 强制分配空的primary shard(会发生数据丢失)。
如果可以接受丢失该shard数据,决定强制重新分配可执行如下API:
1
2
3
4
5
6
7
8
9
10
11
12
13
POST /_cluster/reroute?pretty
{
"commands" : [
{
"allocate_empty_primary" : {
"index" : "<INDEX_NAME>",
"shard" : 0,
"node" : "<NODE_NAME>",
"accept_data_loss" : "true"
}
}
]
}
当使用allocate_empty_primary命令的时候,因为比较危险会强制要求你指定accept_data_loss为true。以确认你已经准备好丢失数据了。
节点磁盘空间不够用
当没有足够的节点和磁盘空间的时候,主节点无法分配分片。默认当磁盘空间使用率到底85%的时候,节点将被标记为Low disk watermark,不会被分配更多的分片。
可通过cat allocation API确认各节点的分片分配情况及磁盘空间使用率。
1
GET /_cat/allocation?v
可以通过删除没用的数据,增加节点或增加服务器的磁盘可用空间等方式解决这个问题。
当我们的磁盘空间足够大的时候,默认的Low disk watermark可能就太低了。这时候可以通过Cluster update settings API进行修改。
例如调整为90%,需要注意的是这是安全点位,我们应该根据实际的数据增长速率来设置一个合适的值。
1
2
3
4
5
6
PUT _cluster/settings
{
"persistent": {
"cluster.routing.allocation.disk.watermark.low": "90%"
}
}
如果只是需要临时修改该值可把上面的persistent替换未transient。如果想永久修改该值可照上面使用persistent做持久处理就可以。
集群中存在多个ES版本
这种情况应该要避免。不只是ES,所有支持集群的其他组件都一样,应保证集群内的所有节点都使用同样的版本。以免引起不可预期的问题。
结论
未分配的分片会直接体现为ES集群的亚健康状态。通过上文了解到索引的分片数设置不当,节点离线或节点磁盘空间不够都会引发产生UNASSIGNED Shard。
虽然说有些情况对服务没有影响,但如果ES看起当前集群不是最佳状态,会通过Shard状态、集群状态给我们一个告警,应给予重视。