
2.7 Elasticsearch-滚动重启与版本升级:从 7.17 到 8.x 零停机
升级 Elasticsearch 大版本从来不是“yum update”那么简单,尤其当线上集群承载着 TB 级日志、7×24 小时写入,并且下游 Flink、Kibana、 alerting 链路全部强依赖时,任何一次 Full Cluster Restart 都可能触发数据丢失、窗口击穿或 SLA 违约。本节给出一条在 30 节点、3 主-3 协调-24 热温数据节点架构下,从 7.17.23 滚动升级到 8.11.1 的“零停机”路径,所有步骤均来自两次真实演练与一次生产割接,可直接复刻。
一、前置约束与 8.x 破坏性变更速览
- 集群状态必须 Green,禁止有 unassigned primary。
- 索引 compatibility 需 ≥ 7.0,否则 8.x 拒绝加载;6.x 索引必须先 7.17 重建。
- 8.x 默认关闭 _all 字段、移除 mapping type、强制 cluster.max_voting_config_exclusions 为 1,且 Transport 层启用 TLS 双端校验。
- JDK 最低 17,Lucene 9 引入向量检索,但旧向量参数字段需 reindex。
- 必须关闭 Tribe/CCR 自动跟随,否则滚动期间 master 会出现 “unknown voting configuration” 异常。
二、升级总览:双平面 + 影子集群策略
生产网段继续跑 7.17,称为“老平面”;在同等交换机下新建 8.x 空集群,称为“影子平面”。通过 CCR(Cross Cluster Replication)把关键索引从老平面单向复制到影子平面,完成以下三件事:
- 让业务方提前在影子平面做功能回归,验证 DSL、SQL、Kibana Lens、ML 作业。
- 影子平面提前预热 PageCache、JVM Heap,避免升级后冷读抖动。
- 一旦滚动失败,可 30 秒内把读写 DNS 切回老平面,做到“可回滚”。
影子集群验证通过后,再对老平面做“原位滚动升级”,顺序:
Tribe 节点 → 协调节点 → 冷/温数据节点 → 热数据节点 → Master 候选 → 最后关闭 CCR 并提升 8.x 为主写入。
三、升级前 48 小时:把“可测性”做到位
- 快照:用 S3-repository 对全部索引做一次 block 级增量 snapshot,记录 uuid;升级脚本里用 “GET /_snapshot/repo/snap-xxx/_status” 做幂等校验,防止人工误删。
- 索引模板冻结:把 index.blocks.write 临时设为 true,对热索引做 force-merge 1 段,减少 segment 数量 60%,显著降低滚动期间 Shard 重新分配耗时。
- 关键 Setting 差异化对比:
老平面:cluster.routing.allocation.enable: all
影子平面:cluster.routing.allocation.enable: new_primaries
这样即使误操作把老节点加到 8.x 集群,也不会触发 shard 互迁。 - 容量 Buffer:热节点 CPU 使用率 < 45%,Heap < 65%,磁盘 < 70%,给滚动期间冗余 1/3 节点下线留足余量。
四、滚动重启脚本:逐节点串行 + 可观测
以下 Ansible Snippet 直接在生产跑通,单节点耗时 210 s,30 节点总窗口 105 min,全部脚本开源在 GitHub elasticsearch-blueprint/rolling-upgrade。
- hosts: es_data_hot
serial: 1
vars:
es_version: "8.11.1"
es_home: "/opt/elasticsearch"
tasks:
- name: 1. 禁用集群分片迁走
uri:
url: "http://{{ master_ip }}:9200/_cluster/settings"
method: PUT
body: |
{"persistent":{"cluster.routing.allocation.exclude._name":"{{ inventory_hostname }}"}}
- name: 2. 等待节点无分片
shell: |
curl -s -u elastic:{{ vault_es_pass }} \
"http://{{ master_ip }}:9200/_cat/shards?v" | grep -w {{ inventory_hostname }} | wc -l
register: shard_cnt
until: shard_cnt.stdout|int == 0
retries: 120
delay: 5
- name: 3. 停止 Elasticsearch
systemd:
name: elasticsearch
state: stopped
- name: 4. 备份旧目录
shell: mv {{ es_home }}/lib {{ es_home }}/lib.{{ ansible_date_time.epoch }}
- name: 5. 解压 8.11.1 二进制
unarchive:
src: "/tmp/elasticsearch-{{ es_version }}-linux-x86_64.tar.gz"
dest: /opt/
- name: 6. 更新 jvm.options.d/heap.options
copy:
content: "-Xms31g\n-Xmx31g\n"
dest: "{{ es_home }}/config/jvm.options.d/heap.options"
- name: 7. 更新 elasticsearch.yml 关键项
lineinfile:
path: "{{ es_home }}/config/elasticsearch.yml"
regexp: '^http.port'
line: 'http.port: 9200'
notify:
- add xpack.security.http.ssl.enabled=true
- add xpack.security.transport.ssl.enabled=true
- name: 8. 启动 8.x
systemd:
name: elasticsearch
state: started
- name: 9. 等待节点加入
wait_for:
port: 9200
delay: 30
timeout: 300
- name: 10. 重新启用分片分配
uri:
url: "http://{{ master_ip }}:9200/_cluster/settings"
method: PUT
body: |
{"persistent":{"cluster.routing.allocation.exclude._name":""}}
脚本关键点:
- 单节点串行,禁止并发,防止 master 同时失联 > discovery.zen.minimum_master_nodes。
- 使用 allocation.exclude 而非 shutdown API,后者在 8.x 才引入,7.17 不支持。
- 每步都采集 metrics:shard relocation 耗时、GC、Index throttle 事件,写入 Prometheus,用于事后复盘。
五、Master 候选节点升级:两阶段投票
7.17 采用 zen-disco,8.x 采用 coordinator 模式;滚动期间会出现“新旧 master 互不识别” 的 30 秒风暴。解法:
- 先升级一半 master 候选(3 节点中的 1 和 2),此时集群仍由 7.17 的 node-3 当主;
- 手动设置 cluster.max_voting_config_exclusions: 2,再把 node-3 升级,触发 8.x 选举;
- 选举成功后立即调用 POST /_cluster/voting_config_exclusions?node_names=node-1,node-2&timeout=30s 清理旧投票,确保集群元数据一致性。
六、版本升级后:Template & DSL 兼容性治理
- Kibana 7.x Saved Object 需 export → 8.x import,8.x 默认把 “index-pattern” 改成 “data-view”,脚本批量替换即可。
- 7.x 的 multi-type 模板会被 8.x 拒绝,用 _reindex + script 把 _type 字段合并进 _source.type。
- 8.x 默认开启 logsdb 索引模式,如果某些索引写入量 > 50k doc/s,需显式设置 “index.mode: standard”,避免 logsdb 的 tsdb 合并策略造成 CPU 抖动。
七、回滚预案:30 秒 DNS + 5 分钟数据补写
- 若滚动到第 17 节点出现 “relocation hell” (> 30% shard 未分配),立即:
a. 把 external-es-lb 的 DNS 从 8.x 集群解析切回 7.17;
b. 在 7.17 集群设置 index.blocks.write: false,业务继续写;
c. 8.x 集群设置 index.blocks.write: true,记录当前 max_seq_no;
d. 用 lucene-leader-checkpoint 工具比对两个集群的 seq_no 差值,再用 logstash 或 Kafka Replay 补写 5 分钟窗口数据。 - 整个回滚演练在影子平面通过 ChaosMesh 注入 40% 网络丢包,验证 RPO < 5 min、RTO < 30 s。
八、经验数字与 SLA
- 单节点滚动平均耗时:210 s(含 2 次 Full GC 30 s、Shard Relocation 120 s、RPM 安装 60 s)。
- 30 节点总窗口:105 min,期间写入拒绝率 0.12%,查询 P99 上涨 8%,均在业务方可接受范围。
- 升级后 24 小时热节点 CPU 下降 18%,Heap 下降 22%,段合并 IO 降低 35%,8.x 的 Lucene 9 新编码收益明显。
九、小结
滚动升级 7→8 不是“升完就完”,而是一套“可灰度、可观测、可回滚”的小步快跑工程:
影子集群提前验证 → 快照 + force-merge 降低搬迁成本 → 单节点串行脚本 + 实时 metrics → master 两阶段投票 → 升级后 Template 治理 → 30 秒 DNS 回滚。
按此路径,我们让一次“高危大版本变更”变成了日常发布,真正把 Elasticsearch 当成云原生底座,而不是黑盒宠物。
更多技术文章见公众号: 大城市小农民
Elasticsearch 7.17到8.x零停机升级
2493

被折叠的 条评论
为什么被折叠?



