之前的第二个阶段解决了自动化的问题,节省了迁移效率,在自动化稳定运行的同时,也在探索提升稳定性与迁移速率,在1~2月期间,主要做了以下几个方面的探索与实践。
1)total_shards_per_node
index.routing.allocation.total_shards_per_node:设置单个索引在单个节点上最多的分片数(包括主副本)。默认无限量。
调整原则:保证单个索引在单个节点上保留最少的分片数,以避免数据分片倾斜的情况,已提升整体稳定性。
total_shards_per_node 的调整逻辑如下:
total_shards_per_node: shard_num/(nodes_count * 0.95(buffer系数) * 0.5)
需要注意的是:
索引级个性化的 template 参考如下:
{
"order": 99,
"index_patterns": [
"log_appcode-*"
],
"settings": {
"index": {
"number_of_shards": "278",
"routing": {
"allocation": {
"total_shards_per_node": "2"
}
},
"refresh_interval": "60s"
}
},
"mappings": {
"properties": {
#索引独立的结构
}
}
}
注:上面这个template 模板设定的索引分片单个节点最多分配两个分片。
2)node_left.delayed_timeout
index.unassigned.node_left.delayed_timeout : 节点脱离集群后多久分配unassigned shards(默认1min),相当于延迟恢复分配多久的时间。
这个参数相当重要,尤其是大集群中,节点宕机重启时有发生,如果不做设置,节点对应的数百副本分片就会进行恢复操作,期间会耗费大量的 IO 资源,影响集群稳定性。而且集群重启后,还会进一步做 rebalance 以平衡分片。
索引恢复主要有6个阶段,如下:
当机器宕机重启后,机器对应的所有分片会短暂成为 UNASSIGNED SHARD 状态,默认60s后集群 UNASSIGNED SHARD 进行恢复操作,此时会把原本宕机机器的未分配分片(副本)分配到其余节点,因为是副本分片同步,需要从主分片进行同步数据,比从本地恢复慢了不少,且极为影响性能。过程类似于:

基于此,针对 node_left 的设置做了两次的尝试:
a. index.unassigned.node_left.delayed_timeout: 120m
将所有的索引 template 设置延迟分配时间设置为120分钟。
当遇到节点宕机后,不会再次出现大量分片迁移的过程,当节点重启后,分片会从节点本地进行恢复,效率高,性能影响小。
但是如果是节点故障宕机,无法启动时,会出现在 delyed_timeout 时间到达后,进行集中大量的分片恢复,如果节点分片多,同样会有性能方面的影响和损耗。
b. index.unassigned.node_left.delayed_timeout: random time
在调大延迟分配时间后,还会出现集中分片恢复的情况,主要是因为对于单一节点的索引分片都是同一时间变成的 unassigned,也会在同一时间进行恢复。
基于此,将 delayed_timeout 设置为随机数值,在创建索引的时候,设置为100~300min区间的随机数,类似如下:
delayed_timeout = random.randint(100, 300)
PUT /index/_settings
{
"settings": {
"index.unassigned.node_left.delayed_timeout": delayed_timeout
}
}
调整后,在故障宕机观测,会发现恢复分片是一个持续间断过程,而非之前的突增突降。如图对比:


3)单机器单节点迁移
数据节点的物理机的配置为:32core 128g 14TB(为主,还有9TB、18TB)。
单物理机部署两个节点(data1,data2)实例,每个节点占用16core 64g资源,磁盘共用。
之前两个阶段的迁移是以物理机为单位进行迁移,批次迁移多台物理机数据,期间会因为大量的数据同步传输导致磁盘io问题,所以需要在高低峰进行批次调整,以保证稳定性。但是批次量无法提升,导致速率不太理想。
此次更换为单机器单节点迁移,exclude_node 中只选择同批次机器的data1,等 data1节点迁移完成后,迁移 data2。cluster exclude 设置如下:
PUT _cluster/settings
{
"transient" :{
"cluster.routing.allocation.exclude._name" : "data1_node1,data1_node2,..."
}
}
使用单节点迁移后,单批次节点在高峰期可以增加50%,低峰时期增加80%。在迁移过程中,集群整体写入,与同步节点的 cpu、磁盘 io 均比较稳定,当 data1迁移完成后,批次节点的 load 整体会控制在10以内,稳定性得到相应的提升。
4)cluster_concurrent_rebalance
cluster.routing.allocation.cluster_concurrent_rebalance: 用来控制集群内并发分片的 rebalance 数量,默认为2。
ES集群的 cluster_concurrent_rebalance 参数长期使用的在10(节点数多,增加分片的同步效率)。
在迁移节点过程中,同步的节点会非常多,出现很多的 relocating shards,且同时还会有 rebalance 的分片,在迁移之后,还会继续做 rebalance。
此处为了减少 rebalance 的频率与周期,设置 cluster_concurrent_rebalance 为0,相当于一直都不做 rebalance。
但是如果长期不进行 rebalance,分片会出现倾斜不均衡的情况,基于此,进行了3.3.4 手动 reroute 操作,来调整分片的均衡。
注:
cluster.routing.allocation.cluster_concurrent_rebalance:0。都相当于不做任何的分片均衡。
rebalance 节点主要由decider 控制器来决定的,而非当前分片使用的情况,这样会造成一个情况,机器在rebalance 后整体分片数均衡,但是写入不均衡(有些分片是当周分片正在写入数据,有些分片是上周分片,均衡之后可能会出现一些节点的当周分片数很多,导致写入压力大,磁盘 io 高,反而没有达到真正的均衡)。
现象是会有一些节点在高峰期出现持续高 load,磁盘 io 趋近100%的情况。不得不做一些手动的 reroute 分片,来缓解节点压力,遇到持续增高,可能会出现 node_left 重启的现象。
➁cluster_concurrent_rebalance为0带来的问题
因为3.3.3 中将 cluster_concurrent_rebalance 调为0,集群不会做分片均衡操作,新增索引分片会按照 decider 进行调度,最极端的情况会将某些分片数少的节点分配超过一倍的分片数。
举个例子:
上述两个现象直观导致的现象都是因为个别节点分片不均衡,导致性能受到影响,继而会出现 node_left 的情况。
分析了上述两个原因,决定从写入的分片均衡来入手。
因为是日志集群,索引是以周维度创建,每周日凌晨提前进行创建次周索引(tips:如果不提前创建,会在次周切换索引时,引发大量的 pending_tasks,造成集群写入大面积堆积),只要保证次周写索引的分片在索引节点均衡即可。
➂reroute new shard 调整分片均衡
在创建完新周索引后,可以对分片进行调整,流程图如下:

针对以上流程做简要说明:
主要的思路是:计算次周总的分片数/可用数据节点,得到平均分片,然后将分片多的迁移到分片少的节点。
因为是空索引,迁移时间基本可以忽略不计。
需要注意的是可用数据节点,从_cluster/health 可以获取到对应的可用 data node 数量,但是还需要将_cluster/settings exclude._name 中对应的节点数排除出去方是真实可用的数量。
同步完后,可以达到写索引的平衡(整体分片数不一定均衡)。
执行完同步脚本后分片均衡,以上问题顺利解决。
➃reroute new node
以上所有的措施已经能解决最核心的两个问题:稳定性、迁移速度。
按照当时迁移速度估算,可以在剩余1个月内完成全部的节点迁移,但是否有更快更好的方法做迁移?
在上个优化点 reroute new shard 调整分片均衡 中得到灵感,新分片迁移是很快的,因为没有数据,基本不费时间且对性能没有任何影响。那能否用于在迁移节点上来呢?答案是可以的。
在 reroute new shard 基础上,做了改进,并根据机器配置,做了差异化的处理(包括迁移节点和平衡分片),设计图如下:

以上设计图有几点需要了解:
顺序:先进行节点迁移,然后进行分片平衡。
时间:一般会提前1-2天创建次周索引,可以在创建索引后进行以上流程操作。
节点迁移增加新节点相当于是将需要迁移的分片迁移到新节点上,因为是新节点,基本上不会有 reroute 冲突。
可以根据机器的配置进行阈值的调整,比如迁移节点后计算节点分片阈值为80(每个节点平均80个次周分片),可以将高配置的机器阈值提高:
* 48c的机器 阈值增加20%
* 32c的机器 阈值降低20%
如果有 ssd 机器,可以在原有阈值上进一步提升,也可以做点对点分片均衡调整,将高配置节点、分片数较少节点统一作为被同步节点,将配置低节点、分片数远高于阈值节点作为同步节点,进行 reroute。
分片均衡,可以参考以下用法:
POST _cluster/reroute
{
"commands": [
{
"move": {
"index": "log_appcode-2023.18",
"shard": 59,
"from_node": "data2_node1",
"to_node": "data2_node10"
}
}
]
}
在做完以上步骤后,等到次周开始,集群写入都切到了新的索引后,可以进行 exclude 节点操作,此时由于批次节点都没有写入索引,迁移同步分片会非常快,且对性能影响非常小。
总结:
5)coordinate node迁移
coordinate node 主要用来做协调作用,负责接收集群的读写请求,继而传递到数据节点进行实际的数据交互。
本身协调节点不进行存储,主要需要 cpu、内存的资源。
ES日志集群的协调机器大致在40+,分批次每周5台迁移即可,迁移完成,需要对 Logstash 的 output 端进行重刷服务。
注:
6)master node迁移
master 节点迁移相对来说比较简单,只需要找到临时机器部署在机房 B,部署好对应服务,把机房 A 的 master 服务下掉,把机房 B 的 new master 节点起来即可。
注:
当然也可以通过负载和服务发现框架来实现动态调整 master 节点。
做完以上步骤,ES 集群就全部迁移到新机房了,后续就是对应的服务迁移与适配。
最终在近三个月的时间里,不断尝试中,完成了集群整体迁移,并且提前了一周多的时间,也做到了零故障。期间遇到元旦峰值,也得到了锻炼和考验。
总结下大规模 ES 集群节点迁移主要的要点有以下:
制定迁移计划:针对不同的节点类型制定不同的方案(提前制定好可预见的问题与应对方案,这点非常重要)。
能用自动化代替人工的,尽量多走自动化,提升效率,复用性高,稳定性好。
熟悉对应的底层原理与系统参数,可以更好指导技术层面的优化与实践。
技术层面
相信以上的经验和一些技术要点,不仅在集群节点迁移可以参考,同样也适用于大规模集群开发与维护。可以说,就是有了不断地集群平台维护开发的经验与总结,才逐步总结出各种优化方法。
小感想:有时候系统性的优化或者方案,从来都不是一蹴而就的,都要通过不断的尝试与调整,在对系统原理的把控上,进行方案的优化,从而取得持续的进步。