Nacos 平滑升级不是把 StatefulSet 镜像标签改成新版本,然后等待三个 Pod 自动重启。真正需要保护的是三件事:数据库 schema、服务注册连续性、配置读取连续性。

系列导航:Kubernetes 重建迁移教程系列。升级前应先完成 Nacos 集群基线和客户端版本盘点。

本文假设当前是三节点 StatefulSet、使用外部 MySQL、应用通过普通 Service 连接,节点间通过 Headless Service 通信。

升级前先回答四个问题

在工单或变更记录中写清楚:

  1. 当前 Server 版本和目标 Server 版本是什么?
  2. 所有应用的 Nacos Client 主版本是什么?
  3. 目标版本是否修改数据库表结构、端口、环境变量和探针路径?
  4. 回滚旧镜像时,新 schema 是否仍与旧版本兼容?

官方升级手册当前说明:从 2.0.x 及以上升级到 3.2.x 需要对比目标版本数据库 schema;3.2.x Server 兼容 2.x 和 3.x Client,而 1.x Client 不再兼容。这个结论会随版本变化,执行前必须重新查阅官方升级手册。

第 1 步:导出版本和客户端清单

保存当前镜像与 Pod:

kubectl -n nacos-system get statefulset nacos \
  -o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}' \
  | tee reports/nacos-current-image.txt

kubectl -n nacos-system get pods -o wide \
  | tee reports/nacos-pods-before.txt

客户端版本不能靠猜。可以从 Maven 依赖树、Gradle dependencies、构建产物清单或镜像 SBOM 中收集:

mvn dependency:tree -Dincludes=com.alibaba.nacos:nacos-client

把结果整理成 CSV:

namespace,workload,client_version,owner,verified
app-test,order-api,<client-version>,team-a,false

只要还有官方不支持的 Client 主版本,升级就应暂停。

第 2 步:备份配置、数据库和 Kubernetes 资源

备份 StatefulSet、Service、ConfigMap 和 Secret 的资源定义:

kubectl -n nacos-system get statefulset,service,configmap -o yaml \
  > backup/nacos-kubernetes.yaml

umask 077
kubectl -n nacos-system get secret nacos-secret -o yaml \
  > backup/nacos-secret.yaml

数据库应使用一致性备份工具进行备份。以 MySQL 逻辑备份为例:

mysqldump --single-transaction --routines --triggers \
  -h <mysql-host> -u <backup-user> -p \
  <nacos-database> > backup/nacos-before-upgrade.sql

sha256sum backup/nacos-before-upgrade.sql > backup/nacos-before-upgrade.sha256

配置数据还应通过 Nacos 的导出功能生成独立归档。数据库备份和配置导出必须在隔离环境中至少恢复一次。

第 3 步:比较数据库 schema

从当前版本和目标版本发行包中分别取出 mysql-schema.sql:

diff -u current/mysql-schema.sql target/mysql-schema.sql \
  | tee reports/nacos-schema.diff

不要直接执行目标版本的完整建表脚本。提取增量 DDL,先在数据库副本演练:

恢复生产备份到临时库
→ 执行增量 DDL
→ 启动一个目标版本 Nacos
→ 验证配置读取和服务注册
→ 记录耗时与锁表影响

如果变更不可逆,回滚方案不能只写“改回旧镜像”,还必须包含数据库恢复或向后兼容证明。

第 4 步:比较配置、端口和探针

导出当前 StatefulSet:

kubectl -n nacos-system get statefulset nacos -o yaml \
  > reports/nacos-statefulset-before.yaml

逐项对照目标镜像文档:

  • 环境变量名称是否变化。
  • 8848、9848、9849、7848、8080 等端口用途是否变化。
  • HTTP API 与控制台路径是否变化。
  • 健康检查路径是否仍然有效。
  • JDK 和资源要求是否变化。

从 Nacos 3.0 起,控制台与主端口分离,代理层不能沿用针对旧版本的假设。gRPC 端口经过代理时要使用 TCP 转发,不能按普通 HTTP 处理。参考部署手册概览。

第 5 步:在影子环境完成升级演练

从生产备份恢复一个隔离数据库,创建单独命名空间:

kubectl create namespace nacos-upgrade-lab

把 Service 名称、数据库地址和 Secret 全部改为测试值,启动与生产相同的旧版本。先证明旧版本能读取备份数据,再升级到目标版本。

至少验证:

  • 登录与权限。
  • 命名空间、Data ID、Group 数量。
  • 配置发布、回读和历史版本。
  • 临时应用的注册、发现和注销。
  • 重启任意一个 Nacos Pod 后集群恢复。

影子环境失败时,不要去生产“再试一次”。

第 6 步:设置逐节点滚动策略

StatefulSet 默认从最大序号开始滚动更新。为了先升级一个节点进行观察,可以临时设置 partition:

kubectl -n nacos-system patch statefulset nacos --type merge -p '
{
  "spec": {
    "updateStrategy": {
      "type": "RollingUpdate",
      "rollingUpdate": {"partition": 2}
    }
  }
}'

然后只更新 Pod 模板中的镜像:

kubectl -n nacos-system set image statefulset/nacos \
  nacos=<image-registry>/nacos-server:<target-version>@sha256:<target-digest>

此时只应更新 nacos-2:

kubectl -n nacos-system get pods -w

第 7 步:观察第一个新版本节点

不要看到 Ready 就立刻继续。至少观察一个完整业务高峰窗口,检查:

kubectl -n nacos-system logs nacos-2 --since=30m \
  | grep -Ei 'error|exception|raft|grpc|database'

kubectl -n nacos-system get endpointslice \
  -l kubernetes.io/service-name=nacos-client -o wide

同时运行连续探测:

while true; do
  date
  curl -fsS 'http://<internal-nacos-address>/nacos/v1/console/health/readiness'
  sleep 5
done

应用侧检查配置监听、注册续约、调用错误率和登录链路。若出现实例列表突然为空、gRPC 连接失败或配置监听中断,立即停止。

第 8 步:逐步放开剩余节点

第一个节点稳定后,把 partition 改为 1:

kubectl -n nacos-system patch statefulset nacos --type merge -p '
{"spec":{"updateStrategy":{"rollingUpdate":{"partition":1}}}}'

等待 nacos-1 Ready,重复完整验证。最后把 partition 改为 0:

kubectl -n nacos-system patch statefulset nacos --type merge -p '
{"spec":{"updateStrategy":{"rollingUpdate":{"partition":0}}}}'

kubectl -n nacos-system rollout status statefulset/nacos --timeout=20m

三个节点之间至少留出明确的观察时间,不要在一个命令中同时删除三个 Pod。

第 9 步:做一次真实故障演练

选择一个非 Leader 或已确认可安全重建的 Pod:

kubectl -n nacos-system delete pod nacos-2
kubectl -n nacos-system wait --for=condition=Ready pod/nacos-2 --timeout=10m

删除期间持续发起配置读取和服务调用。目标不是零日志,而是客户端能够重连,业务请求不出现持续失败。

回滚方法

如果新旧版本允许混部、数据库变更向后兼容,可保持 partition,在故障节点先恢复旧镜像:

kubectl -n nacos-system set image statefulset/nacos \
  nacos=<image-registry>/nacos-server:<old-version>@sha256:<old-digest>

如果数据库 schema 不向后兼容:

  1. 停止所有 Nacos 写入和配置发布。
  2. 将 Nacos StatefulSet 缩容为零。
  3. 恢复升级前的数据库。
  4. 恢复旧版 Kubernetes 清单和 Secret。
  5. 启动旧版本,验证配置与实例。

这个流程会产生中断,因此不可逆 DDL 必须在维护窗口执行。

完成定义

  • 三个 Pod 均运行目标镜像 digest。
  • 数据库 schema 与目标版本一致,并保存执行记录。
  • 配置、服务实例、用户和权限数量与升级前基线一致。
  • 所有关键客户端版本在兼容矩阵内。
  • 删除一个 Pod 时配置读取和服务调用仍可继续。
  • 观察期内没有持续的注册失败、gRPC 断连或数据库异常。
  • 备份、旧镜像和回滚清单在观察期结束前不得删除。