ES 集群角色与架构演进
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_ip、method、url、geo(含国家/城市/经纬度)全部自动填充,message 原始字段已删除。
踩坑:Ingest 节点成了写入瓶颈
某团队把所有写入都过 Ingest 做 Grok 正则解析,高峰期 CPU 打满,写入延迟从 20ms 飙升到 2s。建议:
- Grok 解析放在 Logstash/Fluentd 中,不要在 ES Ingest 做
- Ingest 节点只做轻量操作(set、remove、date 转换)
- 独立部署 Ingest 节点,不要复用 Data 节点
5. Coordinator 节点
配置 node.roles: [](空角色)的节点就是纯协调节点。它不存数据、不参与选主,职责是:
- 接收客户端请求
- 解析查询,确定目标分片
- 广播子查询到各 Data 节点
- 收集排序合并结果,返回客户端
在大量聚合查询的场景下,协调节点内存消耗巨大——它需要把所有分片返回的分桶合并成全局 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 不要做太重操作避免成为瓶颈。