集群健康度

22 May 2026 – wusfe · 4 min read

集群健康度:Green / Yellow / Red 意味着什么、怎么办

一、三种颜色的定义与判定条件

Elasticsearch 的集群健康状态通过 GET /_cluster/health 查询,返回 status 字段,只有三个可能值:

状态 含义 判定条件 是否影响读写 是否影响高可用
Green 一切正常 所有分片(主分片 + 副本分片)均已分配
Yellow 警告 所有主分片已分配,但至少一个副本分片未分配 否(写入正常) ——节点宕机会丢数据
Red 紧急 至少一个主分片未分配 ——该索引部分数据不可读写

核心原则:主分片决定能不能用,副本分片决定安不安全。

GET /_cluster/health

返回示例:

{
  "cluster_name"          : "my-cluster",
  "status"                : "yellow",
  "timed_out"             : false,
  "number_of_nodes"       : 3,
  "number_of_data_nodes"  : 3,
  "active_primary_shards" : 42,
  "active_shards"         : 78,
  "unassigned_shards"     : 6,
  "delayed_unassigned_shards": 0,
  "number_of_pending_tasks": 0
}

二、Yellow:原因分析与修复

2.1 常见原因

  1. 单节点集群:ES 默认 number_of_replicas = 1,单节点无法分配副本到同一节点(ES 不会把主分片和副本分片放在同一节点上,因为这样没有容灾意义)。
  2. 节点数不足:节点数量少于 number_of_replicas + 1
  3. 节点刚刚重启:分片正在恢复中,通常几秒到几分钟自动恢复。
  4. 磁盘水位线触发:当磁盘使用超过 cluster.routing.allocation.disk.watermark.low(默认 85%),ES 停止向该节点分配分片。
  5. 分片分配过滤规则限制了可选节点

2.2 修复步骤

场景一:开发/测试环境的单节点集群

# 临时方案:将副本数设为 0
PUT /_all/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

# 更规范的做法:只在特定索引上设置
PUT /my_index/_settings
{
  "index": {
    "number_of_replicas": 0
  }
}

场景二:生产环境节点不足

增加数据节点,或者在现有节点上调整副本数:

PUT /my_index/_settings
{
  "index": {
    "number_of_replicas": 2
  }
}

注意:副本数不能超过 数据节点数 - 1

场景三:磁盘水位线触发

# 检查磁盘使用情况
GET /_cat/allocation?v

# 临时调高水位线(治标不治本)
PUT /_cluster/settings
{
  "transient": {
    "cluster.routing.allocation.disk.watermark.low": "95%",
    "cluster.routing.allocation.disk.watermark.high": "97%"
  }
}

根本解决办法:扩容磁盘或清理过期数据。

三、Red:应急处理流程

红意味着业务数据不可读写,必须立即响应。 处理原则:先止血(止损),再修复(找根因)。

3.1 应急止血——5 分钟快速排查

Step 1:确认集群状态

GET /_cluster/health

Step 2:找到未分配的主分片

GET /_cat/shards?v&h=index,shard,prirep,state,unassigned.reason,node

关注 stateUNASSIGNEDprirepp 的行。

更精确的查询:

GET /_cat/shards?v&s=state:desc

这会把未分配的分片排在最前面。

Step 3:查看未分配原因

GET /_cluster/allocation/explain

返回示例(解读关键字段):

{
  "index"          : "orders-2026",
  "shard"          : 2,
  "primary"        : true,
  "current_state"  : "unassigned",
  "unassigned_info": {
    "reason"       : "NODE_LEFT",
    "last_allocation_status": "no_attempt"
  },
  "explanation"    : "cannot allocate because 3 nodes have left the cluster..."
}

常见 unassigned.reason

reason 说明 应急处理
NODE_LEFT 持有该分片的节点离线 检查节点状态,尝试恢复节点
ALLOCATION_FAILED 尝试分配但失败(通常是磁盘问题) 检查磁盘空间
REPLICA_ADDED 新建的副本等待分配 通常是短暂的,等待即可
CLUSTER_RECOVERED 集群恢复中 等待恢复完成
DANGLING_INDEX_IMPORTED 导入游离索引 检查 import 过程
INDEX_CREATED 新索引等待分配 正常现象

3.2 三种最常见的 Red 应急场景

场景一:节点宕机导致主分片丢失

# 确认哪些节点离线
GET /_cat/nodes?v&h=name,ip,node.role,heap.percent,ram.percent

# 尝试重启离线节点(运维层面)

# 如果是永久性节点丢失,绝望方案:强制分配空主分片
# 警告:这会导致数据丢失!仅在确认无法恢复节点时使用
POST /_cluster/reroute
{
  "commands": [
    {
      "allocate_empty_primary": {
        "index" : "orders-2026",
        "shard" : 2,
        "node"  : "node-3",
        "accept_data_loss": true
      }
    }
  ]
}

场景二:磁盘满了

磁盘满了会导致分片无法分配,已有的分片也可能变为只读。

# 检查磁盘
GET /_cat/allocation?v

# 如果索引已变为只读
PUT /_all/_settings
{
  "index.blocks.read_only_allow_delete": null
}

# 紧急扩容或删除数据
DELETE /old_index_name

场景三:分片损坏(corruption)

# 查看是否有损坏的分片
GET /_cluster/health?level=shards

# 如果确定分片损坏,force allocate 或从快照恢复
POST /_cluster/reroute
{
  "commands": [
    {
      "allocate_stale_primary": {
        "index" : "my_index",
        "shard" : 0,
        "node"  : "node-2",
        "accept_data_loss": true
      }
    }
  ]
}

3.3 reroute 常用命令

# 将未分配分片分配到指定节点
POST /_cluster/reroute
{
  "commands": [
    {
      "allocate_replica": {
        "index"   : "my_index",
        "shard"   : 0,
        "node"    : "node-3"
      }
    }
  ]
}

# 将分片从一个节点迁移到另一个节点
POST /_cluster/reroute
{
  "commands": [
    {
      "move": {
        "index" : "my_index",
        "shard" : 0,
        "from_node" : "node-1",
        "to_node"   : "node-2"
      }
    }
  ]
}

# 取消未完成的分片分配
POST /_cluster/reroute?retry_failed=false

四、监控告警策略

不要等到用户报故障才看集群状态。 建议的告警规则:

状态 告警级别 条件
Yellow P2 - 警告 持续超过 5 分钟(给分片恢复留时间)
Red P0 - 紧急 立即告警(1 分钟内未恢复)
节点离线 P1 - 严重 任意数据节点离线超过 2 分钟
磁盘使用 P2 - 警告 超过 80%(预留操作空间)

使用 Elasticsearch Watcher 或 Prometheus + Alertmanager 实现:

# Watcher 示例:Yellow 超过 5 分钟触发告警
PUT _watcher/watch/cluster_yellow_watch
{
  "trigger": { "schedule": { "interval": "1m" } },
  "input": {
    "http": {
      "request": {
        "host": "localhost",
        "port": 9200,
        "path": "/_cluster/health",
        "method": "GET"
      }
    }
  },
  "condition": {
    "script": {
      "source": "return ctx.payload.status == 'yellow' || ctx.payload.status == 'red'"
    }
  },
  "actions": {
    "log_alert": {
      "logging": { "text": "Cluster status is {{ctx.payload.status}}" }
    }
  }
}

五、踩坑案例

案例: 某电商团队 3 节点集群,一台数据节点因磁盘满挂掉,集群变 Red。
运维同事发现后,手动调高水位线 + 清理日志,节点恢复后分片自动重新分配,5 分钟后变回 Green。但因为没配监控,从 Red 发现到处理完成已经过了 25 分钟——期间订单搜索功能完全不可用。

教训: Red 状态必须配置即时告警。5 分钟对于在线业务就是一次事故。

六、小结

颜色 行动
Green 保持,持续监控
Yellow 尽快修复(副本分配),防止升级为 Red
Red 立即响应,先止血(reroute / 恢复节点),再找根因