ES 滚动升级与零停机变更

22 May 2026 – wusfe · 4 min read

ES 滚动升级与零停机变更

一、滚动升级(Rolling Upgrade)概述

滚动升级是 ES 官方推荐的零停机升级方式。基本思路:一次只停一个节点,升级后启动,等集群恢复 Green,再升级下一个。

节点 1 → 暂停分配 → 停服 → 升级 → 启动 → 恢复 → 等 Green → 节点 2 → ...

二、完整滚动升级流程

Step 0:升级前检查

# 检查集群健康状态(必须是 Green)
GET /_cluster/health

# 检查 deprecation API,找出待升级的配置
GET /_migration/deprecations

# 查看索引版本兼容性(ES 7.x 检查 6.x 索引是否需要 reindex)
GET /_migration/system_features?pretty

GET /_migration/deprecations 返回示例:

{
  "cluster_settings": [
    {
      "level": "warning",
      "message": "The [cluster.routing.allocation.disk.watermark.low] setting...",
      "url": "https://..."
    }
  ],
  "index_settings": {
    "my_index": [
      {
        "level": "critical",
        "message": "Index created in version 6.x should be reindexed in 7.x"
      }
    ]
  }
}

必须处理所有 critical 级别的 deprecation,否则升级后可能出现功能异常。

Step 1:暂停分片分配

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.enable": "primaries"
  }
}

这个设置的含义:

  • "all":允许所有分片分配(默认值)
  • "primaries":仅允许主分片分配
  • "new_primaries":仅允许新索引的主分片分配
  • "none":禁止所有分片分配

"primaries" 而不是 "none" 的好处:主分片仍然可以被分配,避免集群变 Red(如果主分片丢失);但不分配副本分片,避免节点重启引发大量不必要的分片搬迁。

Step 2:执行同步刷新(可选但推荐)

POST /_flush/synced

同步刷写将索引刷到磁盘,并在各分片上写入相同的 sync_id。节点重启后如果 sync_id 匹配,可以跳过昂贵的 segment 文件复制阶段,大幅加快恢复速度。

注意:如果集群中有正在写入的索引,同步刷新可能失败(这是正常的,不影响升级)。

Step 3:停止目标节点

# 方式一:systemd
sudo systemctl stop elasticsearch

# 方式二:直接 kill(SIGTERM 使 ES 优雅退出)
kill -SIGTERM <pid>

Step 4:升级 ES

# 小版本升级(同大版本,如 7.16.2 → 7.17.0)
# 直接替换二进制文件即可

# 大版本升级需要通过包管理器或手动替换
# RPM:
sudo rpm -Uvh elasticsearch-7.17.0-x86_64.rpm

# 或解压 tar.gz 到新目录,保留旧目录用于快速回滚

Step 5:启动升级后的节点

sudo systemctl start elasticsearch

# 确认节点加入集群
GET /_cat/nodes?v

Step 6:恢复分片分配

PUT /_cluster/settings
{
  "persistent": {
    "cluster.routing.allocation.enable": null
  }
}

设置为 null 会恢复默认值("all")。

Step 7:等待集群变 Green

GET /_cluster/health?wait_for_status=green&timeout=5m

wait_for_status=green 会使请求阻塞直到集群恢复 Green,或超时 5 分钟。

Step 8:重复 Step 1-7 升级下一个节点

三、完整脚本化流程

以下是一个升级单个节点的脚本模板(适合自动化):

#!/bin/bash
ES_HOST="localhost:9200"
NODE_NAME="node-2"

echo "=== Step 1: Disable shard allocation ==="
curl -s -X PUT "${ES_HOST}/_cluster/settings" -H 'Content-Type: application/json' -d '{
  "persistent": { "cluster.routing.allocation.enable": "primaries" }
}'

echo "=== Step 2: Synced flush ==="
curl -s -X POST "${ES_HOST}/_flush/synced"

echo "=== Step 3: Stop node ==="
ssh ${NODE_NAME} "sudo systemctl stop elasticsearch"
sleep 10

echo "=== Step 4: Upgrade ==="
ssh ${NODE_NAME} "sudo rpm -Uvh /path/to/elasticsearch-7.17.0.rpm"

echo "=== Step 5: Start node ==="
ssh ${NODE_NAME} "sudo systemctl start elasticsearch"
sleep 30

echo "=== Step 6: Re-enable allocation ==="
curl -s -X PUT "${ES_HOST}/_cluster/settings" -H 'Content-Type: application/json' -d '{
  "persistent": { "cluster.routing.allocation.enable": null }
}'

echo "=== Step 7: Wait for green ==="
curl -s "${ES_HOST}/_cluster/health?wait_for_status=green&timeout=10m"

echo "=== Node ${NODE_NAME} upgrade complete ==="

四、主版本升级的特殊注意事项

4.1 不能跳版本升级

当前版本 目标版本 可行? 升级路径
5.6.x 7.17.x 5.6 → 6.8 → 7.17
6.8.x 7.17.x 是(从最后一个 6.x) 6.8 → 7.17
7.17.x 8.x 7.17 → 8.x

原因:ES 每个大版本只确保能从上一个主要版本的最后一个小版本升级。跳版本会跳过必要的数据格式迁移步骤。

4.2 跨版本升级需要 reindex

从 6.x 升级到 7.x 后,在 6.x 中创建的索引仍然可以读写,但无法使用 7.x 的新特性(如新的 mapper 行为)。建议在升级完成后对关键索引进行 reindex:

POST _reindex
{
  "source": { "index": "orders_6x" },
  "dest": { "index": "orders_7x" }
}

4.3 REST API 兼容性变化

不同大版本的 REST API 可能有 breaking change。升级前务必阅读对应版本的 Breaking Changes 文档。常见变化包括:

  • 类型(_type)在 7.x 逐步废弃,8.x 完全移除。
  • mapping 参数名称变化(如 stringtext / keyword)。
  • 聚合和查询语法的参数调整。

五、回滚策略

5.1 小版本升级的回滚

小版本(同一个大版本内)通常可以降级:停止升级后的节点,替换回旧版本二进制,重启即可。

5.2 大版本升级的回滚

大版本升级后不能直接降级,因为磁盘上的数据结构可能已被新版本修改。回滚的唯一途径是从快照恢复

# 恢复升级前的快照到一个新的集群
POST /_snapshot/s3_backup_repo/pre_upgrade_snapshot/_restore

# 或者:关闭所有索引,从快照恢复
POST /my_index/_close
POST /_snapshot/s3_backup_repo/pre_upgrade_snapshot/_restore
{
  "indices": "my_index",
  "include_global_state": true
}

升级前一定要做快照! 这是回滚的最后保障。

六、小版本升级的安全窗口

版本类型 举例 风险 建议
Patch 版本 7.17.1 → 7.17.3 极低——仅修复 bug,不改数据结构 看到更新就升,快速跟随
Minor 版本 7.16 → 7.17 ——可能有新功能,但向后兼容 跟踪 release note,确认无 breaking change 再升
Major 版本 7.17 → 8.x ——有 breaking change,需充分测试 先在测试环境验证,准备快照和回滚方案

七、踩坑案例

案例 1:升级后 shard 无法分配
某团队将 3 节点集群从 6.8 升级到 7.16,升级后发现部分副本分片原因为 existing store is corrupt 无法分配。原因是第一个节点升级后,旧版本的节点无法识别新版本写入的分片元数据。正确做法:必须在 Step 1 禁用副本分配(设置为 primaries),等所有节点升级完再一次性恢复。

案例 2:跳版本升级导致无法启动
某团队试图从 5.4 直接升级到 7.10,节点启动失败,报 cannot upgrade from 5.4 to 7.10。最后只能先把数据通过 snapshot 导出,在新集群重建索引。

教训:

  1. 一定要先做快照。
  2. 遵守官方升级路径,不跳版本。
  3. 滚动升级过程中必须暂停副本分片分配。
  4. 先在非生产环境走一遍完整流程。