ES 集群角色与架构演进

22 May 2026 – wusfe · 4 min read

ES 集群角色与架构演进

1. 角色全景图

从 ES 5.x 到现在,角色体系逐渐细化。每类角色承担不同职责:

角色 配置项 职责 资源消耗
Master node.roles: [master] 集群管理、索引创建/删除、分片分配 中 CPU、低内存
Data node.roles: [data] 存储数据、执行搜索、聚合、写入 高 IO、高内存
Data (hot/warm/cold) data_hot / data_warm / data_cold 按数据热度分层 递减
Ingest node.roles: [ingest] 文档写入前预处理(Pipeline) 中 CPU
Coordinator node.roles: [] 转发请求、合并结果 中内存、高网络
ML node.roles: [ml] 机器学习模型推理 高 CPU、高内存
Transform node.roles: [transform] 持续转换/聚合任务 中 CPU、高内存
Remote Cluster Client node.roles: [remote_cluster_client] 跨集群搜索 高网络

2. Master 节点与选主

Master 节点不存储业务数据,只负责"指挥调度":

  • 索引的创建与删除
  • 分片的分配与迁移
  • 维护集群元数据(ClusterState)
  • 节点的加入与退出管理

选主过程(Bully 算法变种)

ES 使用 Zen Discovery(7.x 后重构为基于 Raft 的一致性层):

1. 每个 Master-Eligible 节点有唯一 nodeId
2. 发现阶段:Ping 所有已知节点
3. 选主阶段:按 nodeId 排序,Id 最小的非投票弃权节点成为 Master
4. 新 Master 发布 ClusterState
5. 投票数必须 ≥ (master_eligible_nodes / 2) + 1 才有效(防止脑裂)

关键配置

# elasticsearch.yml
cluster.name: my-es-cluster
node.name: master-1
node.roles: [master]
discovery.seed_hosts:
  - master-1:9300
  - master-2:9300
  - master-3:9300
cluster.initial_master_nodes:
  - master-1
  - master-2
  - master-3

踩坑:为什么 Master 要奇数台

假设 2 台 Master,1 台宕机,剩余 1 台无法达到 2/2+1=2 的法定人数,集群直接不可用(Split-Brain 保护)。3 台 Master 可以容忍 1 台宕机(3/2+1=2 票即可),5 台可以容忍 2 台宕机。

Master 节点数 可容忍宕机数 法定人数
1 0(不推荐生产) 1
2 0(脑裂风险) 2
3 1 2
5 2 3

生产绝对不要用偶数台 Master。

3. Data 节点热温冷分层

这是 ES 最经典的成本优化手段——按数据访问频率存放不同硬件:

# elasticsearch.yml
node.roles: [data_hot]          # SSD + 大内存,存放当天/本周数据
node.roles: [data_warm]         # HDD,存放上月数据
node.roles: [data_cold]         # 廉价 HDD/对象存储,存放归档数据

索引生命周期管理(ILM)自动将数据在不同 Tier 间流转:

PUT _ilm/policy/logs_retention
{
  "policy": {
    "phases": {
      "hot": {
        "actions": {
          "rollover": {
            "max_size": "50GB",
            "max_age": "1d"
          }
        }
      },
      "warm": {
        "min_age": "7d",
        "actions": {
          "allocate": {
            "require": {
              "data": "warm"
            }
          },
          "shrink": {
            "number_of_shards": 1
          },
          "forcemerge": {
            "max_num_segments": 1
          }
        }
      },
      "cold": {
        "min_age": "30d",
        "actions": {
          "allocate": {
            "require": {
              "data": "cold"
            }
          }
        }
      },
      "delete": {
        "min_age": "90d",
        "actions": {
          "delete": {}
        }
      }
    }
  }
}

踩坑:warm 阶段忘记 forcemerge

某日志系统 ILM 配置了 warm 阶段迁移到低配节点,但忘记 forcemerge 把 Segment 合并成 1 个。结果索引带到 warm 层的是 50~100 个碎 Segment,每个 Segment 打开一个文件句柄再加上 DocValues 的搜索开销,查询反而比 hot 层慢 5 倍。

4. Ingest 节点:写入前的数据预处理

Ingest 节点在文档索引前通过 Pipeline 进行转换,无需应用层额外编码:

PUT _ingest/pipeline/enrich_logs
{
  "description": "日志增强管道",
  "processors": [
    {
      "grok": {
        "field": "message",
        "patterns": ["%{IP:client_ip} %{WORD:method} %{URIPATHPARAM:url}"]
      }
    },
    {
      "geoip": {
        "field": "client_ip",
        "target_field": "geo"
      }
    },
    {
      "script": {
        "lang": "painless",
        "source": "ctx.timestamp = ZonedDateTime.parse(ctx.@timestamp);"
      }
    },
    {
      "remove": {
        "field": "message"
      }
    }
  ]
}

PUT logs/_doc/1?pipeline=enrich_logs
{
  "message": "192.168.1.1 GET /api/order",
  "@timestamp": "2024-01-15T10:30:00Z"
}

处理结果client_ipmethodurlgeo(含国家/城市/经纬度)全部自动填充,message 原始字段已删除。

踩坑:Ingest 节点成了写入瓶颈

某团队把所有写入都过 Ingest 做 Grok 正则解析,高峰期 CPU 打满,写入延迟从 20ms 飙升到 2s。建议:

  • Grok 解析放在 Logstash/Fluentd 中,不要在 ES Ingest 做
  • Ingest 节点只做轻量操作(set、remove、date 转换)
  • 独立部署 Ingest 节点,不要复用 Data 节点

5. Coordinator 节点

配置 node.roles: [](空角色)的节点就是纯协调节点。它不存数据、不参与选主,职责是:

  1. 接收客户端请求
  2. 解析查询,确定目标分片
  3. 广播子查询到各 Data 节点
  4. 收集排序合并结果,返回客户端

在大量聚合查询的场景下,协调节点内存消耗巨大——它需要把所有分片返回的分桶合并成全局 TopN。如果某个聚合产生 100 万个桶,协调节点就要在内存中创建 100 万个桶对象。

# elasticsearch.yml
node.roles: []   # 纯协调节点
# 适当调大堆内存用于聚合合并
# 但注意堆不要超过 32GB(压缩指针失效)

踩坑:聚合 OOM

搜索"订单统计",Date Histogram 按小时分桶 × 商品分类 = 10 万桶。协调节点堆内存 4GB,100 万桶 × 每条桶对象 ~4KB = 4GB → OOM。解决方案:

  • 限制聚合桶数:"size": 1000
  • execution_hint: map 分散聚合
  • 聚合放在 Data 节点计算,协调节点只做最终归并

6. 生产集群最小拓扑建议

                     [LB/客户端]
                         │
           ┌─────────────┼─────────────┐
           │             │             │
     [Coordinator]  [Coordinator]  [Coordinator]
           │             │             │
    ┌──────┴──────┬──────┴──────┬──────┴──────┐
    │             │             │             │
  [Ingest]    [Master-1]   [Master-2]   [Master-3]
    │             │             │             │
    └──────┬──────┴──────┬──────┴──────┬──────┘
           │             │             │
      [Data Hot]    [Data Hot]    [Data Hot]
      [Data Warm]   [Data Warm]   [Data Warm]
角色 数量 规格建议
Master 3 台(必须奇数) 2C4G,SSD 100G(仅 OS + ES binary)
Data Hot 3 台 16C64G,NVMe SSD 2TB
Data Warm 2 台 8C32G,HDD 4TB
Ingest 1~2 台 8C16G,轻度
Coordinator 2 台 8C32G(内存在大聚合时消耗快)
总计 至少 8 台

足够撑起亿级文档、日均千万级写入的生产规模。

7. 总结

ES 的角色分离让每个节点各司其职——Master 负责集群大脑、Data 负责体力活(读写搜索)、Ingest 负责预处理、Coordinator 负责路由和聚合。生产部署要严格按角色拆分,最小化交叉影响。Master 必须奇数台,Data 建议按热温冷分层节省成本,Ingest 不要做太重操作避免成为瓶颈。