深入 Elasticsearch(19):生产部署拓扑与节点角色划分
三节点实验集群能验证发现、选举和分片复制,但不能直接推导出生产拓扑。生产设计必须同时回答四个问题:哪些节点参与控制面,哪些节点承担数据面,故障域如何隔离,以及任一节点或可用区退出后,剩余容量能否继续承载业务。
本文面向自建 Elasticsearch 8.x/9.x 集群,给出从小型混合角色集群到分层数据节点集群的参考拓扑。具体节点数、分片数和硬件规格仍应以目标版本和真实负载压测为准;Elastic Cloud Serverless 的资源模型不适用这里的自建配置。
先选拓扑,再写配置
生产集群没有一套固定节点清单。轻负载可以让节点混合承担 master、data 和 ingest;规模扩大后,再把控制面和高消耗的数据处理职责拆开。角色拆分的目标是隔离资源竞争和故障影响,不是让节点名字更多。
图 1:从实验集群到分层生产集群的演进路径
flowchart LR
A[单节点开发环境<br/>全部默认角色] --> B[三节点小型生产集群<br/>master + data + ingest]
B --> C[控制面分离<br/>3 个专用 master-eligible]
C --> D[数据面分层<br/>content / hot / warm / cold]
D --> E[按瓶颈增配<br/>ingest、coordinating、ML]
一种常见的大型自建拓扑如下。三台专用 master-eligible 节点分布在三个故障域;客户端通过负载均衡器访问数据入口;ingest、coordinating 和各数据层按实际压力决定是否独立扩容。
图 2:跨三个可用区的生产参考拓扑
flowchart TB
Client[应用客户端] --> LB[四层或七层负载均衡]
LB --> C1[coordinating-1]
LB --> C2[coordinating-2]
C1 --> I1[ingest-1]
C2 --> I2[ingest-2]
I1 --> H1[data_hot + data_content<br/>AZ-a]
I2 --> H2[data_hot + data_content<br/>AZ-b]
H1 <--> H2
H1 --> W1[data_warm<br/>AZ-a]
H2 --> W2[data_warm<br/>AZ-b]
W1 --> COLD[data_cold<br/>跨故障域]
COLD --> REPO[(快照仓库)]
M1[master-eligible<br/>AZ-a] --- M2[master-eligible<br/>AZ-b]
M2 --- M3[master-eligible<br/>AZ-c]
M3 --- M1
M1 -. cluster state .-> H1
M2 -. cluster state .-> H2
M3 -. cluster state .-> W1
这张图是职责关系,不是固定采购单。若查询和写入压力不高,coordinating、ingest 与数据角色可以合并;若没有时序数据,也不需要 hot、warm、cold 全套数据层。
节点角色不是机器类型
node.roles 决定节点能够承担的职责。一台机器可以列出多个角色;显式配置后,未列出的角色不会启用。每个节点都具备请求协调能力,coordinating 不是可配置角色,node.roles: [] 只是把其他角色全部移除后的 coordinating-only 节点。
图 3:常用节点角色与主要职责
flowchart LR
R[node.roles] --> M[master<br/>选举与集群状态]
R --> D[data / data_*<br/>持有分片并执行查询]
R --> I[ingest<br/>执行 ingest pipeline]
R --> ML[ml / transform<br/>后台计算任务]
R --> RC[remote_cluster_client<br/>跨集群连接]
R --> V[voting_only<br/>投票但不当选]
CO[node.roles: 空列表] --> C[coordinating-only<br/>路由、归并、Bulk 分发]
常用配置形态:
1 | |
Elasticsearch 不存在 search 节点角色。搜索由持有目标分片的 data 节点执行,接收请求的任意节点负责 scatter、gather 和最终归并。专用 master 节点仍有协调能力,但生产业务入口不应指向它,以免大查询和 Bulk 请求干扰控制面。
控制面与数据面
master 节点负责索引元数据、路由表和节点成员关系等 cluster state 变更。文档写入和搜索不经过当选 master,而是由接收请求的节点依据本地 cluster state 直接找到目标分片。
图 4:控制面与数据面的通信边界
flowchart LR
subgraph Control[控制面]
API[创建索引或修改 mapping] --> Master[当选 master]
Master --> State[发布 cluster state]
end
subgraph Data[数据面]
Search[搜索或写入] --> Coord[接收请求的节点]
Coord --> S1[目标 shard]
Coord --> S2[目标 shard]
S1 --> Coord
S2 --> Coord
end
State -. 本地更新 .-> Coord
State -. 本地更新 .-> S1
State -. 本地更新 .-> S2
cluster state 发布也不是“master 每次把完整状态广播给所有节点”。正常情况下发送相对上一版本的 diff;新加入节点或缺少基础版本的节点才接收完整状态。master 取得足够 master-eligible 节点确认后提交,再等待节点应用。
图 5:Cluster state 的发布、提交与应用
sequenceDiagram
participant M as 当选 master
participant Q1 as master-eligible A
participant Q2 as master-eligible B
participant D as data / ingest 节点
M->>Q1: 发布新状态(通常是 diff)
M->>Q2: 发布新状态(通常是 diff)
Q1-->>M: 确认接收
Q2-->>M: 确认接收
Note over M,Q2: 达到法定确认数后提交
M->>Q1: commit
M->>Q2: commit
M->>D: 发布并通知应用
D-->>M: applied
Note over M,D: 缺少基础版本时改发完整状态
三个 master-eligible 节点解决什么
高可用集群至少需要三个 master-eligible 节点,其中至少两个不能是 voting-only。三个节点能在失去一个节点后保留多数派,避免双主。它们最好分布在三个独立故障域;若只跨两个可用区,2+1 布局只能承受特定一侧故障。
图 6:三个 master-eligible 节点的多数派
flowchart TB
subgraph Healthy[正常:3 票中取得 2 票]
A[master A<br/>AZ-a] --- B[master B<br/>AZ-b]
B --- C[master C<br/>AZ-c]
C --- A
end
subgraph Failed[AZ-a 故障后]
X[master A 不可达] -. 网络中断 .-> Y[master B 存活]
Y === Z[master C 存活]
Q[2 票构成多数派<br/>可以继续选举]
Y --> Q
Z --> Q
end
三台节点并不自动等于生产高可用。还要确认数据副本跨故障域、客户端入口有冗余、必要角色至少有两个实例,并且故障后的剩余节点仍有足够 CPU、内存、磁盘和恢复带宽。
首次引导与日常发现
discovery.seed_hosts 告诉节点去哪里寻找 master-eligible 节点。cluster.initial_master_nodes 只用于全新集群第一次引导,集群形成后必须移除;加入现有集群、滚动重启和全量重启都不应再次设置它。
图 7:发现设置与首次集群引导的生命周期
flowchart LR
A[准备全新节点<br/>统一 cluster.name] --> B[配置 discovery.seed_hosts]
B --> C[仅首次配置<br/>cluster.initial_master_nodes]
C --> D[选出 master<br/>生成 cluster UUID]
D --> E[移除 initial_master_nodes]
E --> F[后续重启或扩容<br/>只通过 seed hosts 加入]
C -. 名称不一致 .-> G[可能形成错误集群或引导失败]
1 | |
搜索请求如何穿过集群
搜索分为 query 和 fetch 两阶段。协调节点将请求发到每个目标分片的一份副本,归并候选结果,再从命中所在分片获取文档。主分片和副本都能处理搜索,不存在永久绑定的“查询副本节点”。
图 8:搜索请求的 scatter-gather 路径
sequenceDiagram
participant C as Client
participant Q as Coordinating node
participant S0 as shard 0 的一份副本
participant S1 as shard 1 的一份副本
C->>Q: _search
par Query phase
Q->>S0: query + top K
Q->>S1: query + top K
end
S0-->>Q: doc IDs + scores
S1-->>Q: doc IDs + scores
Q->>Q: 全局归并与排序
par Fetch phase
Q->>S0: fetch 命中文档
Q->>S1: fetch 命中文档
end
Q-->>C: hits + aggregations
专用 coordinating 节点能隔离归并和请求入口,但它也接收 cluster state,并消耗堆内存处理大聚合和 Bulk 请求。只有当入口或归并成为可测量瓶颈时,才值得单独部署。
写入请求与 ingest pipeline
Bulk 请求先在协调节点按目标主分片拆分;若配置 ingest pipeline,则由 ingest 角色执行预处理;主分片完成写入后复制到 in-sync 副本。大规模 pipeline 处理适合独立 ingest 节点,简单轻量写入可以由数据节点兼任。
图 9:Bulk 写入、Ingest 与主副本复制路径
sequenceDiagram
participant C as Client
participant Q as Coordinating node
participant I as Ingest node
participant P as Primary shard
participant R as Replica shard
C->>Q: _bulk
Q->>I: 执行 pipeline
I-->>Q: 处理后的文档
Q->>P: 按 routing 写入
P->>R: 复制操作
R-->>P: ack
P-->>Q: item result
Q-->>C: bulk response
Content 与时序数据分层
data_content 面向商品、文章、用户档案等非时序内容,数据通常长期驻留。时序数据才沿 hot、warm、cold、frozen 生命周期迁移。cold 层既可以存普通索引及副本,也可以使用 fully mounted searchable snapshots;frozen 层只承载 partially mounted searchable snapshots,并依赖快照仓库。
图 10:Content 常驻路径与时序数据生命周期
flowchart TB
App[业务索引] --> Content[data_content<br/>长期驻留]
Stream[日志与指标] --> Hot[data_hot<br/>写入与高频查询]
Hot --> Warm[data_warm<br/>只读与较低频查询]
Warm --> Cold[data_cold<br/>普通索引或 fully mounted]
Cold --> Frozen[data_frozen<br/>partially mounted]
Frozen --> Delete[删除]
Cold --> Repo[(快照仓库)]
Frozen --> Repo
内建 data tier 角色配合 ILM 使用。Elasticsearch 通过 _tier_preference 选择可用层,例如 data_cold,data_warm,data_hot 表示优先 cold,无法使用时再尝试 warm 和 hot;它不是磁盘满后的自动溢出机制,其他分配限制仍可能阻止分片迁移。
1 | |
旧式 node.attr.data: hot 加自定义 allocation filter 仍然有效,但新建集群优先采用内建 data tier 角色。迁移旧集群时,需要同时检查节点角色、索引模板、ILM policy 和存量索引过滤条件,避免新旧规则互相约束。
让主副本跨故障域
节点属性和 allocation awareness 可以让 Elasticsearch 在分配分片时感知可用区。主分片和副本应落在不同故障域,否则单个机架或可用区故障可能同时带走两份数据。
图 11:Allocation awareness 下的跨可用区分片布局
flowchart LR
subgraph AZA[AZ-a]
A0[shard 0 primary]
A1[shard 1 replica]
end
subgraph AZB[AZ-b]
B0[shard 0 replica]
B1[shard 1 primary]
end
A0 <--> B0
B1 <--> A1
1 | |
forced awareness 在一个可用区退出后允许副本保持未分配,而不是把所有副本挤到剩余区域。此时集群可能是 yellow,但主分片仍可读写;代价是故障期间副本冗余下降。
图 12:Forced awareness 在可用区故障时的取舍
flowchart LR
A[AZ-a 故障] --> B[AZ-b 保留主分片]
B --> C[副本保持 unassigned]
C --> D[集群 yellow<br/>业务可能仍可用]
C --> E[避免在 AZ-b 复制全量副本<br/>防止磁盘和网络过载]
F[AZ-a 恢复] --> G[副本重新分配]
G --> H[集群恢复 green]
容量要按故障状态计算
容量规划不能只算“日增量乘保留天数”。至少要加入副本系数、段合并与水位余量、单节点或单区故障后的承载能力,以及恢复期间的网络和磁盘吞吐。
图 13:生产容量预算的组成
flowchart TB
Raw[原始数据量 × 保留周期] --> Index[索引膨胀或压缩系数]
Index --> Replica[副本与快照策略]
Replica --> Headroom[磁盘水位与 merge 余量]
Headroom --> Failure[N-1 节点或单区故障余量]
Failure --> Recovery[恢复带宽与恢复时间目标]
Recovery --> Test[真实查询与写入压测]
Test --> Size[节点数、分片数与硬件规格]
10–50GB 每分片、每分片少于约两亿文档可以作为初始检查范围,不是硬性最优值。最终参数应由真实文档、mapping、查询、聚合、写入速率和硬件压测决定。副本也不等于备份;受支持的集群备份方式是 snapshot,复制节点数据目录不能替代快照。
扩容、缩容和角色调整
增加数据节点后,Elasticsearch 会按分配规则迁移分片。缩容前必须先排空目标节点并等待 relocation 完成。移除 data 或 master 角色时,直接修改 YAML 后重启并不安全;使用新节点和空数据目录通常更简单。elasticsearch-node repurpose 会删除不再允许保留的磁盘数据,只适合明确理解后果的修复流程。
图 14:数据节点扩缩容与退役顺序
flowchart LR
A[新增空数据节点] --> B[加入现有 cluster UUID]
B --> C[观察 shard relocation]
C --> D[确认健康与容量稳定]
D --> E[对待退役节点设置 allocation exclude]
E --> F[等待分片迁出]
F --> G[停止并移除节点]
G --> H[清理临时分配设置]
滚动维护一次只处理一个节点
滚动重启和滚动升级的基本约束相同:每次只停止一个节点,节点重新加入并恢复到可接受状态后,再处理下一台。升级顺序应按目标版本的官方文档执行;混合版本只允许存在于升级窗口,不能作为长期运行形态。
图 15:滚动维护的单节点闭环
flowchart LR
A[记录健康状态与未分配原因] --> B[按文档调整临时 allocation]
B --> C[停止一个节点]
C --> D[维护或升级]
D --> E[启动并重新加入]
E --> F[等待节点和分片稳定]
F --> G{还有节点?}
G -- 是 --> C
G -- 否 --> H[恢复临时设置并复核健康]
部署检查表
| 检查项 | 最低要求 | 常见错误 |
|---|---|---|
| 控制面 | 至少三个 master-eligible,至少两个非 voting-only | 业务流量直接打到专用 master |
| 数据冗余 | 必要数据至少两份,跨故障域分配 | 主副本落在同一机架或可用区 |
| 角色冗余 | 必要的 data、ingest、入口角色至少两个实例 | 单个 coordinating 或 ingest 成为入口单点 |
| 故障容量 | 节点或可用区退出后仍能承载峰值 | 只按正常状态平均利用率采购 |
| 恢复能力 | 有快照、恢复演练和恢复时间目标 | 把 replica 或数据目录复制当备份 |
| 网络安全 | 客户端使用 HTTPS,节点间 transport TLS | 直接向不可信网络暴露 HTTP/transport 端口 |
部署完成后,至少检查 _cat/nodes 的实际角色、_cluster/health、_cluster/allocation/explain、快照状态和单节点退出场景。配置文件看起来正确,只能证明静态配置存在,不能替代故障演练和真实负载验证。


