
Elasticsearch 的滚动升级我拖了很久才写。原因很简单官方文档把步骤列得清清楚楚但真正在生产环境操作每一步都可能遇到文档外的情况。上个月刚把一套承载线上日志检索的 ES 集群从 7.14.0 升级到了 7.17.10整个过程走的是基于 RPM 包的滚动升级业务写入全程没有停过连接 ES 的客户端也没有任何感知。这篇把整个过程展开讲一遍为什么能无中断、升级前要准备什么、具体命令怎么敲、还有我踩过的坑。如果你正在规划 ES 升级或者只是想把一个 7.x 的老集群收拾利索这篇应该能帮你省不少时间。1. 升级前必看滚动升级原理与 7.14→7.17 路线选择1.1 滚动升级能零中断的根本原因Elasticsearch 集群能一边升级一边对外服务靠的是两个底层特性。第一个是副本分片机制同一份索引数据通常有多份拷贝分布在多个节点上只要集群里还有节点在线、副本能正常提供服务即使某个节点短暂下线集群整体依然可以继续读写。第二个是同一大版本内允许混合版本节点共存在 7.x 范围内新版本节点可以正常与旧版本节点通信、参与主节点选举、同步分片数据。这两点结合起来就形成了一套标准的滚动升级玩法——一次只停一个节点替换二进制包重启等集群恢复再操作下一个节点。整个过程对于调用方来说只是某几个分片的查询偶尔变慢了一点不会有连接中断。给没接触过的人打个比方这就像给一辆行驶中的列车换零件你不能整车停下来让乘客下车所以只能一节车厢一节车厢地检修。ES 的副本机制就是保证这节车厢检修时乘客还能从其他车厢找到替代座位。理解了这个思想后面每一步操作你都能知道自己在干什么。1.2 为什么目标版本选 7.17.107.17 是 7.x 系列的最后一个版本分支官方对它的修复维护持续了很久。7.14 这个版本不算老但它的问题在于后续暴露的一些安全漏洞和 bug 只在 7.15 之后的补丁版本里修复了你停留在 7.14等于一直把这些风险敞着。在同一大版本内官方明确支持从任意 7.x 小版本直接升到任意更新的 7.x 小版本所以从 7.14.0 一步跳到 7.17.10 是合法的升级路径不需要逐个小版本爬。这能省不少事也少几次重启窗口。截至这次操作的时间点7.17.10 是 7.17 系列里修复比较到位的补丁版本包含了对 Log4j 相关漏洞的后续修复、多项稳定性改进以及若干之前版本遗留的 regression 修复。如果你的网络策略允许也可以选中 7.17.x 最新补丁号原则就是一条尽量选补丁号最新的 7.17 版本不要停留在 7.17.0 这种偏早期的补丁版本上。另外升级前我建议顺手确认一下当前版本和 7.17 使用的 Lucene 版本差异7.14 和 7.17 用的都是 Lucene 8.11 系列段格式没有变化这也是这次升级风险相对低的原因之一。跨大版本就完全不是这么回事了8.x 底层变更很多那是另一套升级流程。1.3 为什么不直接 yum update而要手动 RPM 升级我知道有人会问yum update 不也能升吗能但有几个问题。yum update 会把系统里所有带更新包的软件一起升级你可能只想动 Elasticsearch结果发现依赖关系把别的包也捎带更新了升级范围瞬间不可控。另外yum 升级很难精细控制“先停旧版本进程、逐个节点升级”的节奏它更倾向于一次性把所有节点上的包都更新完这不符合滚动升级的流程。手动 RPM 升级的好处是目标明确只操作当前节点升级命令敲下去之前服务已经手动停好了什么时候启动也由自己控制配合 systemd 管理服务整个流程完全在掌控之中每步都可回退。RPM 安装还有一个特点值得了解程序文件在 /usr/share/elasticsearch配置文件在 /etc/elasticsearch数据目录默认在 /var/lib/elasticsearch日志在 /var/log/elasticsearch。RPM 升级本质上是替换 /usr/share/elasticsearch 下的二进制文件其他目录保留不动。这就解释了为什么升级完配置和数据都还在根本原因是目录职责分离得清楚。2. 升级前检查与备份健康状态、磁盘水位、快照一个都不能少2.1 先确认集群健康和所有节点版本别一上来就升级。第一步是登到集群里看清楚现状。我习惯先把版本和健康状态一起查curl -s localhost:9200/_cluster/health?pretty curl -s localhost:9200/_nodes?filter_pathnodes.*.versionpretty第一条返回 status、number_of_nodes、active_shards 等信息。status 必须是 green如果有 yellow先查清楚是哪个索引、哪个分片的问题再往下走如果已经是 red那在这个状态下去做滚动升级风险会成倍放大。第二条命令能一次看到所有节点的版本号正常情况下应该全部是 7.14.0。如果发现某个节点版本不一致说明之前就有过不规范操作先把版本统一了再来升级否则后面排查问题的时候会混入很多干扰项。2.2 分片副本、磁盘水位线检查不能省滚动升级过程中要停节点如果某个索引没有副本而它的主分片恰好落在即将停机的节点上这个索引在停机期间就变成 red无法读写。所以升级前先扫一遍索引副本配置生产库建议重要索引至少保留 1 个副本。可以用curl -s localhost:9200/_cat/indices?v初步扫一下也可以更精细地检查副本为 0 的索引。这一步看起来简单但很多线上事故就是在升级前没注意副本数导致某个索引在节点重启期间彻底不可用。磁盘水位线也容易被忽略。ES 默认的 disk.watermark.low 是 85%high 是 90%升级期间副本分片会重新流转如果磁盘本来就很满可能触发分片搬迁失败甚至节点 local storage 溢出。我见过升级过程中一个分片反复 relocation最后查下来就是磁盘余量不足。建议升级前把节点磁盘使用率控制在 70% 以下再开始操作留出余量给 rebalance。2.3 配置与快照备份两种备份一起做备份这步看起来像废话但真出事的时候能救命。先说配置/etc/elasticsearch 整个目录打包下来里面包含 elasticsearch.yml、jvm.options、log4j2.properties 以及可能存在的证书目录。RPM 升级一般会保留已有配置把新版本自带的默认配置写成 .rpmnew 文件但万一配置权限被改坏或者语法出问题手头有备份就能快速恢复。数据备份推荐用 ES 快照功能这是最靠谱的方式。先确认快照仓库是否配置curl -s localhost:9200/_snapshot?pretty。如果已经配置过仓库直接创建一个快照curl -s -X PUT localhost:9200/_snapshot/my_backup/snapshot_upgrade_$(date %Y%m%d)?wait_for_completiontrue如果没有配置过仓库就需要先注册一个文件系统仓库路径必须在所有节点的 path.repo 白名单里。快照创建完成后用_snapshot/_status确认快照状态是 SUCCESS。这个步骤不能省虽然小版本升级理论上不会动底层数据但 RPM 升级脚本在极端情况下可能导致数据目录权限变化有快照在手回滚时才有底气。2.4 插件兼容性与弃用配置核查ES 装过插件的话升级时插件版本也要跟着升。比如 analysis-icu、analysis-ik 这类插件RPM 升级只会替换主程序/usr/share/elasticsearch/plugins 目录下的插件不会自动更新。遇到不兼容新版 ES 的插件节点启动时可能直接报错。所以升级前先列出已装插件/usr/share/elasticsearch/bin/elasticsearch-plugin list然后逐个去插件官方页面确认对应 7.17.10 的版本号提前下载好。另外翻一遍 elasticsearch.yml看看有没有已弃用的配置项。7.14 到 7.17 之间动态配置项变化不大但有些旧配置如果之前显式设置过建议对照 7.17 的 reference 文档检查一遍。新版不会直接拒绝启动但会打 deprecation 日志趁这个时间点清理掉对后续升 8.x 也有好处。3. RPM 包准备与 YUM 源配置在线离线两种方式都跑通3.1 .rpm 是什么RPM 安装方式的特点先补一点基础。.rpm 是 Red Hat Package Manager 的包格式相当于 Windows 上的 .exe 安装程序、macOS 上的 .dmg。RPM 包在安装时会自动把文件放到预设目录创建 elasticsearch 用户注册 systemd 服务执行各种初始化脚本。和 tar.gz 解压就能用相比RPM 的好处是生命周期管理标准化安装、升级、卸载都走统一命令服务的启停也交给 systemd 接管。ES 在 7.x 版本中内置了匹配的 JDK不管你是 7.14 还是 7.17RPM 包都不需要额外装 JDK升级时 JDK 也会被一起替换成配套版本。3.2 在线环境配置官方 YUM 仓库并锁定版本如果服务器能访问外网可以把官方 YUM 仓库加入系统。按官方文档导入 GPG key 和仓库配置后用下面命令就能看到仓库里所有可用的 ES 版本yum list elasticsearch --showduplicates确认 7.17.10 在列表中然后执行yum install elasticsearch-7.17.10不过我实际更推荐“下载 RPM 包再手动 rpm -Uvh”的方式而不是直接用 yum install。原因是 yum 可能会触发依赖解析把其他相关组件一起更新升级粒度不好控制而先下载好 RPM 包可以提前在测试环境验证再到生产服务器上用一条 rpm 命令精确替换逻辑最简单出问题也能快速定位。3.3 离线环境下载、校验、拷贝一条龙很多生产环境是内网访问不了外网。这时候需要在一台能联网的机器上预先下载 RPM 包然后拷贝进内网。下载地址走官方 artifacts 站点文件命名一般长这样elasticsearch-7.17.10-x86_64.rpm elasticsearch-7.17.10-x86_64.rpm.sha512下载后务必校验文件完整性和来源真实性。先对一下 sha512sha512sum -c elasticsearch-7.17.10-x86_64.rpm.sha512输出 OK 说明文件完整。再导入官方 GPG key 做签名校验rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch rpm -K elasticsearch-7.17.10-x86_64.rpm如果返回 OK 或至少没有明显的 NOKEY 报错包就可以信任了。离线环境尤其要做这一步因为你无法靠在线依赖检查包来源只能靠数字签名确认。顺便说一句如果执行 rpm 命令提示 command not found多半不是 rpm 没装而是系统本身不是 RPM 系比如 Debian/Ubuntu 用 dpkg此时应该选官方提供的 .deb 包或 tar.gz 包。还有一种冷门情况是 CentOS 上有人误升级了系统自带的 Python导致 yum/rpm 异常这种情况不要在这台机器上继续折腾建议先修复系统基础环境或者直接换一台干净的机器部署 ES。4. 滚动升级实操流程6 步完成单个节点零中断4.1 第一轮禁用分片自动分配升级第一个节点前先给集群发一个动态设置把分片分配策略改为 primariesPUT _cluster/settings { persistent: { cluster.routing.allocation.enable: primaries } }这段配置的意思是允许主分片继续分配但不允许副本分片在节点上下线时触发 rebalance。为什么只允许主分片因为当你停掉一台节点后如果允许分片自动分配ES 会立刻把这台节点上的副本分片复制到其他节点产生大量 IO 和网络开销拖慢升级节奏。限制为 primaries 后主分片依然保持可用副本分片暂时不搬迁等节点恢复后再重新平衡。执行后可以用GET _cluster/settings?include_defaultstrue确认生效。这里要特别提醒有的教程会写成 none这在某些场景下会导致主分片也无法分配集群服务能力大幅下降并不适合滚动升级。官方推荐的写法就是 primaries别随手抄错。4.2 停机前手动 flush缩短恢复时间节点停机前如果索引还在持续写入translog 会积累不少未落盘的增量数据重启时 ES 需要从 translog 恢复这些数据耗时和 translog 大小成正比。手动触发一次 flush可以把内存中的 segment 安全落盘清空 translogPOST /_flush?wait_for_ongoingtrue在 7.14 上也可以试POST /_flush/synced这个接口从 7.6 开始标记为弃用但在 7.x 中仍然可以执行只是会打 deprecation 警告。如果集群里的索引不多、写入量也一般直接用普通 flush 就够用了。升级前如果能暂时停掉定时任务、批量写入程序把写入量降下来效果会更好如果业务不允许停写就挑一个写入低峰期来操作。4.3 停服务、替换 RPM、启动新版本确认集群状态正常后开始处理第一个节点。先停掉目标节点上的 ES 服务sudo systemctl stop elasticsearch等几秒确认进程已经退出ps -ef | grep elasticsearch | grep -v grep确认没有 Java 进程残留执行 RPM 升级sudo rpm -Uvh elasticsearch-7.17.10-x86_64.rpm参数 -U 表示升级如果检测到旧版本会先卸载旧包再安装新包-v 和 -h 只是显示详细信息和进度条。全新安装应该用 -i但在已有 7.14.0 的主机上用 -U 就能实现替换程序文件、保留数据目录。执行完成后先看下 /etc/elasticsearch 目录如果出现 elasticsearch.yml.rpmnew 或 jvm.options.rpmnew说明新版自带了一个默认配置但你的旧配置没被覆盖。这时候不用慌diff 一下新旧配置确认没有缺少必要默认项旧配置可以继续使用。启动服务sudo systemctl start elasticsearch启动后先不要急着操作下一个节点。在本地确认节点版本和集群状态curl -s localhost:9200/_nodes/process?filter_pathnodes.*.versionpretty curl -s localhost:9200/_cluster/health?pretty版本应该是 7.17.10集群状态如果从 yellow 慢慢回到 green说明这个节点已经顺利融入新版本集群分片也恢复正常了。4.4 恢复分片自动分配并确认集群 green单个节点升级成功只是局部成功此时要把之前设置的 persistent 配置取消掉让集群恢复正常自动分配PUT _cluster/settings { persistent: { cluster.routing.allocation.enable: null } }注意只写 null不要写 all。null 是清除之前的持久化设置让 ES 回到默认状态显式写 all 虽然看起来效果一样但相当于把一个临时配置永远钉在集群设置里以后排查分片问题时容易产生干扰。清除后再次确认集群状态curl -s localhost:9200/_cluster/health?wait_for_statusgreentimeout60s如果副本分片较多恢复可能需要几分钟甚至更久耐心等待。期间可以用_cat/recovery查看恢复进度。4.5 逐节点重复节奏比速度更重要第一个节点稳定后用同样流程升级第二个、第三个节点。每次只操作一个节点每轮升级完必须等集群回到 green 再操作下一个。不要贪快不要同时停两台机器。节点多的时候建议把流程做成 checklist每完成一个节点就打勾防止中途被事情打断后忘记操作到哪一步。如果集群规模很大可以写脚本辅助但脚本里的等待和确认逻辑一定要严格至少要在下一步执行前解析出集群 health 的 status 为 green。生产环境的滚动升级节奏比速度重要宁可慢一点也不要冒险。5. 升级后验证清单与回滚预案先确认没问题再收工5.1 升级后必须过一遍的验证清单所有节点都升到 7.17.10 之后跑一遍验证再结束维护窗口。我每次都会按这个清单过版本验证GET /返回的 version.number 是 7.17.10GET _nodes里没有其他版本节点。集群状态GET _cluster/health为 greennumber_of_pending_tasks 为 0。索引状态GET _cat/indices?v没有 red 索引yellow 数量为 0。分片状态GET _cat/shards?vhindex,shard,prirep,state,storesindex所有分片 STARTED。日志检查在 /var/log/elasticsearch 下 grep ERROR 和 Exception确认升级过程中没有隐藏异常。上层应用连通性挑一个依赖 ES 的业务接口实际请求一次确认读写链路正常。Wiki.js 这类强依赖 ES 的应用升级后最好在管理端触发一次全文检索甚至重建一次索引确认新版本下映射和分词都没有问题。监控指标看 JVM heap、CPU、磁盘 IO、节点间网络流量和升级前对比有没有异常偏离基线。不用把这些全写成脚本但至少状态类检查一定要在升级结束后记录一次 baseline方便后面异常时对比。5.2 回滚预案能降级和只能靠快照的分界线先说结论小版本升级官方不承诺支持降级但实践中如果符合条件可以快速回到旧版本。条件是新版本启动后没有产生大量新数据——也就是刚升级完发现问题就立刻回滚数据目录里的文件还是旧版本写入的旧 RPM 还能正常读取。操作方法是sudo rpm -Uvh --oldpackage elasticsearch-7.14.0-x86_64.rpm sudo systemctl start elasticsearch如果升级后已经运行了一段时间、有新数据写入就别再用降级来赌了。此时正确的回退姿势是用快照在原版本集群上做恢复确认数据完整后再切换流量。因为新版本可能已经改写了底层结构7.14 到 7.17 之间很少见但跨大版本很常见旧版本直接启动可能报错甚至损坏数据。不要在生产环境测试这种边界情况教训已经够多了。滚动升级期间如果在第二个节点时就发现问题可以考虑把第一个节点也回滚到旧版本让集群整体回到升级前如果已经升级超过一半节点就需要评估是继续升完更现实还是全量回滚更快。这个判断最好提前和团队对好别临时拍脑袋。5.3 顺手清理旧版本遗留资源升级到 7.17.10 后建议顺手清理几样东西。一是检查集群里是否还挂着旧版本创建的索引模板、ingest pipeline、生命周期策略这些不是你手动创建的而是某些历史插件或旧版本功能自动创建的里面可能引用旧版本语法虽然 7.17 还能兼容但留着总归是隐患。二是把升级过程中产生的 .rpmnew 文件处理掉要么合并进正式配置要么归档别让它们留在 /etc/elasticsearch 下干扰后续维护。三是把监控面板的版本阈值、告警规则更新成 7.17.10避免未来误报。6. 常见问题与避坑实录从 rpm 命令到插件兼容性一次讲清6.1 升级中高频问题速查表现象可能原因排查方向执行 rpm 命令提示 not found系统不是 RPM 系或 PATH 异常确认发行版类型用 which rpm、/bin/rpm 验证升级后节点没有加入集群版本不兼容、discovery 配置异常查看节点日志、检查 network.host 和 discovery.seed_hosts集群一直 yellow分片分配被限制、磁盘水位线触发检查 _cluster/settings、_cat/allocationsystemctl start 长时间没反应数据恢复慢、启动超时查看 /var/log/elasticsearch 日志临时调大 TimeoutStartSec日志出现 deprecation warning配置或 API 用了旧语法按日志提示修改对应配置或调用方式升级后某个索引 red该索引无副本、主分片曾在停机节点上先恢复分片分配必要时用快照恢复该索引表格可以打印出来贴在工位上实操时对照着看能少掉不少头发。6.2 三个真实踩坑记录第一个坑恢复分片分配时写了 all。当时图省事直接写cluster.routing.allocation.enable: all结果这条配置一直留在集群 persistent 设置里。它和默认行为没有差异但后面某次排查分片分配问题时我盯着这个设置看了好久最后发现是虚惊一场。从那以后我恢复这类设置一律用 null 清除不再手动赋值。第二个坑升级前忽略了插件兼容性。有一次升级完节点一直启动失败日志里报插件无法加载。后来发现是 analysis-icu 插件版本和 7.17 不匹配。RPM 升级不会帮你更新插件需要自己手动装对应版本。现在我的升级 checklist 里第一项就是“列出插件并提前下载对应版本”。第三个坑磁盘水位线。升级过程中因为设置了 primaries 分配副本暂时不迁移看起来没事。当我恢复自动分配后海量副本分片开始 rebalance某台磁盘使用率接近 90% 的节点瞬间触发 high watermark大量分片往其他节点迁移集群 IO 拉满查询延迟明显上升。自那以后我升级前都会强制要求磁盘使用率在 70% 以下宁可等一等也不冒险。6.3 关于 Windows 与 Wiki.js 调试的几句题外话热词里有人问 Windows 启动 Elasticsearch简单说两句。Windows 上装 ES 一般用 zip 包解压后运行 bin\elasticsearch.bat生产环境很少见 Windows 跑 ES但本地开发调试完全够用。要注意 JDK 版本和 ES 官方支持矩阵匹配7.17 官方支持 Java 11 和 Java 17如果本地配的不是这两个版本要么改 JAVA_HOME要么直接用 ES 自带的 JDK。至于 Wiki.js 这类应用调试 ES重点通常是确认索引是否正常创建、分词器是否符合预期升级后触发一次全文检索就能验证。Windows 环境下想模拟升级流程也很简单停服务、替换整个解压目录、再启动思路和 RPM 升级一致只是少了一层包管理的保护。我自己每次滚动升级完成都不会立刻关闭维护窗口至少再观察一周重点盯 JVM 老年代 GC 频率、慢查询数量和磁盘 IO。小版本升级改动虽然不大但默认配置和 JVM 参数的微妙变化有时在高负载场景下才显现。既然到了 7.17.10其实就是站在了通往 8.x 的跳板上抽空把集群里的废弃配置、旧模板、遗留 pipeline 收拾干净后面做跨大版本升级时会轻松很多。