误入集群的节点引发的shard unassigned线上故障

背景是对线上的es7集群(这里简称为集群A)进行动态扩容,A集群配置3个node节点,在进行节点重启时误重启了其他集群(这里简称为集群B)废弃的节点,导致AB两个集群不可用,red状态。

现象

  • AB两个集群red状态
  • A集群有节点offline
  • B集群节点都online,但多个index的主副分片unassigned

解决办法

这里先交代下最后的解决办法:

A集群
对于A集群,找到正确的es安装地址,动态扩容后重启即可。

B集群
而对于B集群就比较复杂些,着重解决shard unassigned问题。

  1. 检查哪些index的shard出现unassigned问题
    可以通过命令:GET _cat/shards?h=index,shard,prirep,state,unassigned.reason定位到出现问题的index。如果你平常是使用kibana来查询监控集群的,当集群status=red时,kibana是无法连接到集群的,也就无法使用Dev Tools。这里推荐你使用Cerebro,一款提供对于ElasticSearch更加友好的可视化操作支持的工具。直接执行cat apis
  2. 三思而后行
    要从出现问题的index角度出发,如果是无关紧要或者是已经下线不在使用的,可以直接删除;反之删除index操作要慎重。
    根据unassigned.reason具体问题具体分析,根据官方的解释有如下:

  • INDEX_CREATED:由于创建索引的API导致未分配。
  • CLUSTER_RECOVERED :由于完全集群恢复导致未分配。
  • INDEX_REOPENED :由于打开open或关闭close一个索引导致未分配。
  • DANGLING_INDEX_IMPORTED :由于导入dangling索引的结果导致未分配。
  • NEW_INDEX_RESTORED :由于恢复到新索引导致未分配。
  • EXISTING_INDEX_RESTORED :由于恢复到已关闭的索引导致未分配。
  • REPLICA_ADDED:由于显式添加副本分片导致未分配。
  • ALLOCATION_FAILED :由于分片分配失败导致未分配。
  • NODE_LEFT :由于承载该分片的节点离开集群导致未分配。
  • REINITIALIZED :由于当分片从开始移动到初始化时导致未分配(例如,使用影子shadow副本分片)。
  • REROUTE_CANCELLED :作为显式取消重新路由命令的结果取消分配。
  • REALLOCATED_REPLICA :确定更好的副本位置被标定使用,导致现有的副本分配被取消,出现未分配。

根据目前查询的结果来看,我是属于DANGLING_INDEX_IMPORTED

我做了哪些尝试

❎由于出现故障,导致分配分片的5次(默认)重试机会用完了,所以不会再自动分配,需要进行retry

POST /_cluster/reroute?retry_failed=true

❎调整index的副本数为0

curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d '{"number_of_replicas": 0}'

❎调整index的刷新时间为5m

curl -XPUT 'localhost:9200/<INDEX_NAME>/_settings' -d ' { "settings": { "index.unassigned.node_left.delayed_timeout": "5m" } }'

✅手动分配分片,将主分片分配(只丢失部分数据,出现问题的index均为不在使用的)

POST _cluster/reroute
{
  "commands": [
    {
      "allocate_stale_primary": {
        "index": "index_name",
        "shard": 4,
        "node": "node1",
        "accept_data_loss" : true
      }
    }
  ]
}

分析

搜罗下网上归纳的出现unassigned的主要原因,建议直接阅读原文:How to Resolve Unassigned Shards in Elasticsearch | Datadog

补充下

不同版本的es的reroute语法

ES2.x的reroute命令:

curl -XPOST 'localhost:9200/_cluster/reroute' -d '{
    "commands" : [ {
        "move" :
            {
              "index" : "test", "shard" : 0,
              "from_node" : "node1", "to_node" : "node2"
            }
        },
        {
          "allocate" : {
              "index" : "test", "shard" : 1, "node" : "node3",
              "allow_primary":true  (表示接受主分片数据丢失)
          }
        }
    ]
}'

ES5.x升级之后,已经把主分片的恢复和副本的恢复进行了区分,Cluster Reroute | Elasticsearch Reference [5.6] | Elastic

curl -XPOST 'localhost:9200/_cluster/reroute' -d '{
    "commands" : [ {
        "move" :
            {
              "index" : "test", "shard" : 0,
              "from_node" : "node1", "to_node" : "node2"
            }
        },
        {
          "allocate_replica" : {
              "index" : "test", "shard" : 1, "node" : "node3"
          }
        },
        {
            "allocate_empty_primary": {
              "index" : "test", "shard" : 1, "node" : "node3",
              "accept_data_loss":true (接受数据丢失)
          }
        }
    ]
}'

不同情况下手动分配分片的流程

  • red情况下手动分配主分配操作流程
    • 先查询未分配的主分片原来是在哪个节点上 GET /order_orderlist/_shard_stores
    • 将主分片分配(只丢失部分数据)

POST _cluster/reroute
{
  "commands": [
    {
      "allocate_stale_primary": {
        "index": "index_name",
        "shard": 4,
        "node": "node1",
        "accept_data_loss" : true
      }
    }
  ]
}

如果数据不重要,可以不用放到原来的节点上,直接新建一个空分片替代

 POST _cluster/reroute
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index": "test_11",
        "shard": 2,
        "node": "node0",
        "accept_data_loss" : true
      }
    }
  ]
}

  • yellow情况下手动分配副本分片操作:

POST /_cluster/reroute
{
  "commands" :[
    {
      "allocate_replica" :
         {
           "index" : "index_name",
           "shard" : 0,
           "node": "node1"
         }
      }
   ]
 }

主分片的恢复用allocate_empty_primary,而副本的恢复用allocate_replica

补充:在unassigned的分片比较多的时候,可以使用脚本

#!/bin/bash
for index in $(curl  -s 'http://localhost:9200/_cat/shards' | grep UNASSIGNED | awk '{print $1}' | sort | uniq); do
      for shard in $(curl  -s 'http://localhost:9200/_cat/shards' | grep UNASSIGNED | grep $index | awk '{print $2}' | sort | uniq); do
          echo  $index $shard
          curl -XPOST 'localhost:9200/_cluster/reroute' -d "{
              'commands' : [ {
                    'allocate' : {
                        'index' : $index,
                        'shard' : $shard,
                        'node' : 'Master',
                        'allow_primary' : true
                    }
                  }
              ]
          }"

          sleep 5
      done
  done


可以参考的几篇文章:

  • 总结得最整的是 https://www.datadoghq.com/blog/elasticsearch-unassigned-shards/
  • 单独针对主shard出现unassigned的解决可以看http://blog.kiyanpro.com/2016/03/06/elasticsearch/reroute-unassigned-shards/
    https://t37.net/how-to-fix-your-elasticsearch-cluster-stuck-in-initializing-shards-mode.html
    http://www.wklken.me/posts/2015/05/23/elasticsearch-issues.html
  • 单独针对副本shard出现unassigned的解决可以看
    https://z0z0.me/recovering-unassigned-shards-on-elasticsearch/
    https://dpatil1410.wordpress.com/2016/09/24/its-red-how-do-i-recover-unassigned-elasticsearch-shards/