Elasticsearch集群出现Yellow状态怎么办?副本分配与节点排查

打开 Kibana 或 curl 一下集群健康接口,看到 "status": "yellow",是 Elasticsearch 运维里最常见的告警之一。它不像 red 那么吓人(red 表示有主分片未分配,部分数据不可用),但也不能放着不管:yellow 意味着所有主分片都在,但至少有一个副本分片没有分配成功。数据还能读写,可一旦承载主分片的节点宕机,那份数据就真的丢了——因为在另一台机器上没有副本兜底。所以 yellow 是一个"目前能用、但失去了冗余保护"的状态。

这篇文章要解决的是:为什么集群会变 yellow、怎么一步步定位到底是哪个索引的哪个分片没分配、以及针对不同原因(单节点集群、磁盘水位、分片分配开关被关、节点掉线等)分别怎么处理。读者读完应该能用几条 _cat 和 _cluster/allocation/explain 命令精确找到原因,而不是靠重启节点碰运气。

文章先讲清 yellow 的本质和它与 red、green 的关系,再给出标准排查命令,然后按原因分类给解决方案,最后讲磁盘水位线这个高频诱因。文中涉及的参数如无特别说明按 ES 7.x/8.x 通用。

一、green、yellow、red 到底差在哪

集群健康状态由分片的分配情况决定:

  • green:所有主分片和副本分片都已分配。数据完整且有冗余,最理想。
  • yellow:所有主分片都已分配,但部分副本分片未分配。数据可读写,但冗余缺失。
  • red:至少有一个主分片未分配。对应索引的部分数据不可用,读写可能失败。

理解这三者的关键是分清"主分片"和"副本分片":主分片是数据的原始承载者,副本分片是它的拷贝。ES 规定副本分片不能和它的主分片在同一节点上,所以单节点集群如果给索引设置了副本数大于 0,就永远只能是 yellow——因为没地方放副本。这是新手最常踩的坑。

先看健康总览:

# 集群整体健康
curl -s 'http://localhost:9200/_cluster/health?pretty'

# 看未分配分片数量
curl -s 'http://localhost:9200/_cluster/health?pretty' | grep -i unassigned

返回里的 unassigned_shards 就是当前未分配的分片数量,active_shards 是已活跃分片数。

二、标准排查:先定位,再解决

不要一上来就改配置。先问三个问题:哪个索引不健康?哪些分片没分配?为什么没分配?

第一步:找出 yellow 的索引

# 按健康状态列出所有索引(只看 yellow/red)
curl -s 'http://localhost:9200/_cat/indices?v&health=yellow'
curl -s 'http://localhost:9200/_cat/indices?v&health=yellow,red'

# 看每个索引的分片分配情况(pri 主分片数,rep 副本数)
curl -s 'http://localhost:9200/_cat/indices?v&h=index,health,pri,rep,pri.store.size,docs.count' | sort -k2

第二步:找出未分配的具体分片

# 列出所有未分配分片
curl -s 'http://localhost:9200/_cat/shards?v&h=index,shard,prirep,state,node,unassigned.reason' | grep -i UNASSIGNED

# 或用 _cat/shards 全量看每个分片的节点归属
curl -s 'http://localhost:9200/_cat/shards/myindex?v'

输出的 unassigned.reason 列会给出初步原因,比如 NODE_LEFT(节点离开)、ALLOCATION_FAILED(分配失败)、CLUSTER_RECOVERED(集群恢复中)等。

第三步:用 allocation explain 精确定位原因

这是最关键的一步。ES 提供的分配解释接口会详细告诉你"为什么这个分片分配不了"。

# 让 ES 自动挑一个未分配分片来解释
curl -s 'http://localhost:9200/_cluster/allocation/explain?pretty'

# 指定具体索引和分片
curl -s -XGET 'http://localhost:9200/_cluster/allocation/explain?pretty' -H 'Content-Type: application/json' -d '
{
  "index": "myindex",
  "shard": 0,
  "primary": false
}'

返回里重点看 unassigned_info.reason 和 explanation 字段。比如 "the shard cannot be allocated to the same node as its primary shard" 就是"没有第二台节点可放副本","disk threshold exceeded" 就是磁盘水位问题。这一条信息通常能直接指明方向。

三、按原因分类处理

原因一:单节点集群(最常见)

现象:unassigned.reason 显示 ALLOCATION_FAILED 或直接是"cannot allocate to same node as primary",集群只有一台数据节点。

本质:副本无处可放。两种选择:

选择 A:如果暂时只有单节点,把副本数设为 0 消除 yellow。

curl -XPUT 'http://localhost:9200/myindex/_settings' -H 'Content-Type: application/json' -d '
{ "index": { "number_of_replicas": 0 } }'

这会立即让该索引变 green。代价是失去冗余,单节点挂了数据就没了。适合开发/测试环境,或明确接受该风险的场景。

选择 B:加节点。新增数据节点后,副本会自动分配,集群变 green。生产环境应选这条路。

原因二:节点掉线或离开

现象:unassigned.reason 为 NODE_LEFT,且节点列表里少了机器。

# 查看节点状态
curl -s 'http://localhost:9200/_cat/nodes?v&h=name,ip,heap.percent,disk.used_percent,node.role,master'

# 查看节点离开的记录
curl -s 'http://localhost:9200/_cluster/health?pretty&level=indices'

节点掉线可能是进程崩溃、机器宕机、网络分区或磁盘满导致。处理:先把节点拉起来(查该节点日志 elasticsearch.log),节点回归后分片会自动重新分配。如果节点确实无法恢复(机器报废),需要用 _cluster/reroute 手动把副本提升为主分片或强制分配。

# 手动把某未分配副本分片分配到指定节点
curl -XPOST 'http://localhost:9200/_cluster/reroute?pretty' -H 'Content-Type: application/json' -d '
{
  "commands": [
    { "allocate_replica": { "index": "myindex", "shard": 0, "node": "node-2" } }
  ]
}'

风险提示:_cluster/reroute 是强力操作,尤其是 allocate_empty_primary(强制分配空主分片)会导致数据丢失。只有在明确知道后果、且其他办法都无效时才用。执行前先评估影响,必要时先做快照。回滚方式:快照恢复。

原因三:磁盘水位线触发

现象:explanation 里提到 disk threshold 或 high watermark。

ES 默认有三条磁盘水位线,控制分片分配:

水位线 默认值 行为
low watermark 85% 不再往该节点分配新分片
high watermark 90% 尝试把该节点上的分片迁走
flood stage 95% 该节点所有索引被设为只读

查看水位:

curl -s 'http://localhost:9200/_cat/allocation?v'
curl -s 'http://localhost:9200/_cluster/settings?pretty&include_defaults' | grep -i watermark

当某个节点磁盘超过 85%,新分片就不会往它上面分配,如果其它节点也不低于 85%,副本就分配不了,集群变 yellow。

处理办法:清理磁盘(删旧索引、删快照)、扩容,或临时调整水位线(不推荐长期如此)。

# 临时把 high watermark 调到 92%(应急手段,非根治)
curl -XPUT 'http://localhost:9200/_cluster/settings' -H 'Content-Type: application/json' -d '
{
  "transient": {
    "cluster.routing.allocation.disk.watermark.high": "92%",
    "cluster.routing.allocation.disk.watermark.flood_stage": "97%"
  }
}'

风险提示:调高水位线只是掩盖磁盘紧张,磁盘继续涨到真的满,ES 会拒绝写入甚至数据损坏。应急后必须尽快清理或扩容,并把 transient 设置清掉(用 null 重置)。删索引是不可逆操作,删前务必确认不是业务需要的数据。

# 重置为默认(把 transient 值设为 null)
curl -XPUT 'http://localhost:9200/_cluster/settings' -H 'Content-Type: application/json' -d '
{ "transient": { "cluster.routing.allocation.disk.watermark.high": null } }'

原因四:分片分配被关闭

现象:之前做过"停止分片分配"的操作(比如为了维护、扩缩容),忘记打开。

# 查看分配开关状态
curl -s 'http://localhost:9200/_cluster/settings?pretty&flat_settings=true' | grep -i 'allocation.enable\|rebalance'

# 恢复分配
curl -XPUT 'http://localhost:9200/_cluster/settings' -H 'Content-Type: application/json' -d '
{ "persistent": { "cluster.routing.allocation.enable": "all" } }'

这是"改完忘了改回来"的典型,值得优先检查。

四、其他常见诱因

  • 分片总数过多:索引数量爆炸、每个索引分片数过多,超过了节点能承载的分片数(每节点分片数有上限,默认 1000/节点,可调)。分片分配会因"节点分片数达上限"而失败。
  • 节点属性/感知配置不匹配:用了 allocation.awareness 或 allocation.include/exclude 规则,导致某些分片找不到符合条件的节点。
  • 集群处于恢复中:刚重启后短暂 yellow 属正常,等恢复完成会转 green。可用 _cat/recovery 观察进度。
  • 索引设置了较大的 number_of_replicas 但节点不足:比如 3 副本 + 2 节点。
# 看集群恢复进度
curl -s 'http://localhost:9200/_cat/recovery?v&active_only=true'

# 看分片总数和上限
curl -s 'http://localhost:9200/_cat/allocation?v'
curl -s 'http://localhost:9200/_cluster/settings?include_defaults&flat_settings' | grep -i 'total_shards_per_node'

五、一套排查清单

步骤 命令 看什么
1. 看整体健康 _cluster/health status、unassigned_shards
2. 找问题索引 _cat/indices?health=yellow 哪些索引 yellow
3. 找未分配分片 _cat/shards | grep UNASSIGNED 分片、原因
4. 精确定位 _cluster/allocation/explain 具体原因说明
5. 看节点 _cat/nodes 节点数、磁盘、堆
6. 看磁盘 _cat/allocation 水位是否超 85%
7. 查开关 _cluster/settings 分配是否被关
8. 处置 加节点/设副本 0/清磁盘 恢复 green

绝大多数 yellow 在前四步就能定位原因。核心动作是用 allocation/explain 问 ES 到底为什么分配不了,而不是猜。

常见问题

Q:单节点集群一定是 yellow 吗?
如果索引副本数大于 0,是的。单节点放不了副本,只能把副本数设为 0 才变 green。生产环境应加节点而不是长期设副本 0。

Q:yellow 会影响读写吗?
不会直接影响。所有主分片都在,读写正常。但失去了冗余,节点故障时可能升级为 red 并丢数据,所以不能长期忽略。

Q:加了节点为什么还是 yellow?
可能新节点的磁盘水位已超 85%、或分片分配被关闭、或节点角色不符合要求(如只有 master 没有 data 角色)。用 allocation/explain 看具体原因,并确认 _cat/allocation 里新节点在列。

Q:可以随便把副本数调成 0 吗?
技术上可以,很快变 green。但它等于放弃了冗余,是"用风险换状态"。仅建议在单节点测试环境或明确知情的情况下用,生产环境应通过加节点解决。

Q:重启集群后变 yellow,要处理吗?
先等。重启后集群需要恢复分片,恢复期间 yellow 正常,用 _cat/recovery?active_only=true 看进度。如果恢复完成后仍是 yellow,再按上面的流程排查。

总结

yellow 的含义是"主分片齐全、副本未分配",数据可读写但失去冗余;单节点集群设置了副本就必然 yellow,这是最高频的原因。

排查要遵循"看健康 → 找索引 → 找未分配分片 → 用 _cluster/allocation/explain 精确定位原因"的顺序,别一上来就改配置或重启。

常见原因有四类:节点数量不足(含单节点)、节点掉线、磁盘水位线触发、分片分配开关被关闭,分别对应"加节点/设副本 0""恢复节点或 reroute""清磁盘/扩容""恢复分配"四种处置。

磁盘水位线(low 85%、high 90%、flood 95%)是最容易被忽视的诱因,临时调高只能救急,必须随后清理或扩容,并把临时设置重置回默认。

_cluster/reroute 和 allocate_empty_primary 等强力操作可能导致数据丢失,执行前要评估影响、准备快照;删索引、重置设置同样不可逆,都要三思。