打开 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 等强力操作可能导致数据丢失,执行前要评估影响、准备快照;删索引、重置设置同样不可逆,都要三思。
企业QQ咨询




