首页
/ Milvus Helm 部署零停机滚动升级:rollingUpdate.sh 脚本的原理剖析与实战操作

Milvus Helm 部署零停机滚动升级:rollingUpdate.sh 脚本的原理剖析与实战操作

2026-09-05 20:26:52作者:董宙帆

Milvus 2.2.3 起支持滚动更新(Rolling Update),官方提供了 rollingUpdate.sh 脚本,可在不中断服务的前提下将 Helm 安装的 Milvus 集群逐组件升级到新版本。读完本文,你将掌握该脚本的完整参数与标准操作流程,并理解其“检查-开启 Active Standby-按序 kubectl patch-轮询 rollout 状态”的底层实现,以及零停机设计背后的原理与适用边界。

一、滚动升级的适用范围与前提

Milvus 从 2.2.3 版本开始支持滚动更新,deployments/upgrade/rollingUpdate.sh 脚本可帮助你在零停机的情况下完成版本升级(见 deployments/upgrade/README.md)。使用该脚本前必须确认以下四点限制:

  1. 源版本必须不低于 2.2.0:滚动更新依赖该版本之后引入的 Active Standby 等协调者高可用机制;
  2. 仅适用于 Helm 安装的 Milvus:通过 Milvus Operator 安装的集群不在支持范围内;
  3. 目前仅支持 update 操作:脚本的 -o 参数只接受 update,回滚(rollback)尚未实现(源码中 do_rollback 仅打印 kubectl rollout history,标注 “not in use now”);
  4. RocksMQ 的 standalone 不支持滚动更新:脚本在 prepare 阶段会主动检测并拒绝执行。

二、脚本参数详解

脚本通过 getopts "n:i:t:w:o:" 解析命令行选项(见 rollingUpdate.sh),完整参数如下:

参数 说明 默认值 是否必填
-i Milvus 实例名称(Helm release name) None
-n Milvus 安装的命名空间 default
-t 目标 Milvus 版本 None
-w 新的 Milvus 镜像 tag milvusdb/milvus:v2.2.3
-o 操作类型 update

脚本内置默认值定义在 rollingUpdate.sh

readonly STRATEGY='RollingUpdate'
NAMESPACE="default"
IMAGE_TAG="milvusdb/milvus:v2.2.3"
OPERATION="update"

参数校验逻辑(option_checkrollingUpdate.sh)的规则是:

  • 缺少 -i 直接报错退出;
  • 操作为 update 时,-t-w 二者缺一即报错;
  • -o 传入非 update 的值时打印 Usage 并退出。

若不传任何参数,脚本会打印 Usage 帮助(rollingUpdate.sh):

[Warning]: Based on kubectl
-n [namespace]                                   Default:[default]
                                                 The namespace that Milvus is installed in.
-i [instance name]                    [required] The name of milvus instance.
-t [target_version]                   [required] The milvus target version.
-w [image_tag]                        [required] The target milvus image tag.
-o [operation]                                   Only support [update] now.

RollingUpdate Example:
    sh rollingUpdate.sh -n default -i my-release -o update -t 2.2.3 -w 'milvusdb/milvus:v2.2.3'

三、滚动升级的标准操作流程

官方给出的升级步骤共四步(见 deployments/upgrade/README.md):

  1. 检查该 Milvus 实例的所有 Deployment,确认没有任何 Deployment 处于阻塞(blocked)状态;
  2. 逐个更新 Deployment,更新顺序目前硬编码在脚本中;
  3. 在命令中指定命名空间、实例名、目标版本与目标镜像;
  4. 运行脚本。以下示例将 Milvus 升级到 2.2.3:
sh rollingUpdate.sh -n default -i my-release -o update -t 2.2.3 -w 'milvusdb/milvus:v2.2.3'

两个关键实现细节(README 中的 Note):

  • 脚本通过 kubectl patch 修改 Deployment,并用 kubectl rollout status 观察其状态;
  • -t 指定的 target version 实质是 Deployment 的标签 app.kubernetes.io/version,同样通过 kubectl patch 写入,因此它既用于标记版本,也方便升级后按标签核对各组件版本。

四、脚本内部流程剖析

脚本的主入口是一条链式调用(rollingUpdate.sh):

function main() {
    pre_check && prepare && check_rollout_status && do_rollout && check_rollout_status true
    exit $?
}

整个升级过程可分为五段,逐一拆解如下。

4.1 预检查:pre_check

pre_check = env_check && option_check && check_instancerollingUpdate.sh),依次完成三项检查:

  • env_checkwhich kubectl 确认本机已安装 kubectl,否则直接退出;
  • option_check:即上节的参数必填性校验;
  • check_instance:通过标签选择器在目标命名空间查找该实例的 Deployment:
kubectl get deploy -n $NAMESPACE -l app.kubernetes.io/instance=$INSTANCE,app.kubernetes.io/name=milvus --output=jsonpath={.items..metadata.name}

查不到任何结果时说明 -i 传入的实例名有误,脚本退出。这里可以看出:脚本识别 Milvus 组件完全依赖 Helm Chart 注入的标准标签 app.kubernetes.io/instanceapp.kubernetes.io/name=milvus,这也是“仅适用于 Helm 安装”这一限制的根源。

4.2 开启滚动更新模式:prepare

prepare 阶段调用 set_rollingupdate_moderollingUpdate.sh),做两件事:

(1)拒绝 RocksMQ standalone。从名为 ${INSTANCE}-milvus 的 ConfigMap 中提取 messageQueue 配置:

local configmaps_name=${INSTANCE}-milvus
local mq=$(kubectl -n $NAMESPACE get configmaps $configmaps_name -o jsonpath='{.data}'|egrep -o 'messageQueue: \w*' --color|tail -1|awk '{print $2}')
if [[ $mq == "rocksmq" ]];then
    echo "standalone Milvus with mq:rocksmq don't support rolling update now" && exit 1
fi

(2)向 ConfigMap 的 user.yaml 中追加 Active Standby 配置。若检测到尚未开启,则通过 sed 拼接后 kubectl apply,为四个协调者注入 enableActiveStandby: true

rootCoord:
  enableActiveStandby: true
queryCoord:
  enableActiveStandby: true
dataCoord:
  enableActiveStandby: true
indexCoord:
  enableActiveStandby: true

这段配置正是零停机的核心机制,下节详述。注意脚本只对 user.yaml 字段做幂等检查(已包含则跳过重复写入)。

4.3 升级前的全局状态确认:check_rollout_status

do_rollout 执行前先对所有 Deployment 逐个调用 check_deploy_status false(不 watch),只要有一个未处于 rollout successful 状态,就打印该 Deployment 的 Pod 列表并退出,提示“可能正在部署或有故障,需先修复”(rollingUpdate.sh)。这保证了升级起点是一个“全部健康”的集群。

4.4 逐个更新:match_deploy 与 deploy_update

组件顺序是硬编码的match_deploy 按名称后缀从实例 Deployment 列表中筛出各组件,顺序为(rollingUpdate.sh):

rootcoord → datacoord → indexcoord → querycoord → indexnode → datanode → querynode → proxy → standalone

该顺序的设计意图是从元数据/调度类协调者开始,最后更新直接面向客户端的 proxy;standalone 作为单机部署形态被单独匹配。

每个 Deployment 的更新动作deploy_update 中(rollingUpdate.sh):

kubectl -n $NAMESPACE patch deployments.apps $deployment -p \
'{"metadata":{"labels":{"app.kubernetes.io/version":"'$TARGET_VERSION'"}},"spec":{'$wait_time'"strategy":{"type":"'$STRATEGY'"},"template":{"spec":{"containers":[{"image":"'$IMAGE_TAG'","name":"'$name'"}]}}}}'

即一次性 patch 三处:app.kubernetes.io/version 标签、镜像 tag、滚动更新策略 RollingUpdate。另有一个针对性优化——名称包含 coord 的 Deployment 会额外注入 "minReadySeconds":30,让协调者新 Pod 就绪后至少稳定运行 30 秒才继续滚动,降低协调者切换瞬间的抖动风险:

echo $deployment|grep coord >/dev/null && local wait_time='"minReadySeconds":30,' || local wait_time=''

patch 之后立即调用 check_deploy_status true $deploy,底层是:

kubectl -n $NAMESPACE rollout status deployment $deploy --watch=$watchOption | grep -i successful

任一 Deployment 的 rollout 未成功,脚本打印错误并退出,不会继续更新后续组件。

4.5 升级后的最终确认

maindo_rollout 之后再次调用 check_rollout_status true(watch 模式),对全部 Deployment 做一轮最终确认,确保整个实例所有组件均 rollout successful 后才以 0 退出。

五、零停机机制解析:Active Standby 与升级顺序

滚动升级能做到“零停机”,依赖两个配合的机制。

机制一:协调者 Active Standby。 Milvus 的四个协调者(rootCoord、queryCoord、dataCoord、indexCoord)各自支持 enableActiveStandby 配置项,当前仓库的 configs/milvus.yaml 中均默认关闭,例如:

querycoordv2 源码中可以看到该配置在服务启动时读取并生效(server.go):

s.enableActiveStandBy = Params.QueryCoordCfg.EnableActiveStandby.GetAsBool()

开启后,同一角色可存在多个副本、仅一个处于活跃状态,其余热备待命。滚动升级时旧 Pod 下线,热备 Pod 可接管协调者职责,从而避免“单副本协调者被替换”造成的服务空窗。脚本在 prepare 阶段自动写入这些配置,正是为升级过程临时搭建这条“安全垫”。

机制二:自底向上的更新顺序与逐组件门控。 脚本先更新协调者与节点,最后更新 proxy;且每 patch 一个 Deployment 都会阻塞等待其 rollout status 出现 successful,才处理下一个。从源码结构看,这保证了任意时刻集群中不存在“协调者尚在重启、数据节点也已重启”的叠加窗口。

需要说明的是,mixCoord 配置段在当前仓库中已出现(即协调者合并形态),而脚本针对的仍是 2.2.x 时代分立的 four coordinators 部署拓扑——脚本注入的 enableActiveStandby 键名与 rootcoord/datacoord/indexcoord/querycoord 的顺序都印证了这一点,用它升级更高大版本前建议先核对目标版本的组件名与配置结构。

六、局限性与注意事项

结合 READMErollingUpdate.sh 源码,使用时的实际边界包括:

  • 无自动回滚能力do_rollback 目前只调用 kubectl rollout history 打印历史(rollingUpdate.sh),失败时需借助 K8s 自身的 kubectl rollout undo 等机制自行处理;store_metadata 中也注明“若要支持强更新与回滚,需要保留状态信息”,即设计上的已知缺口。
  • 顺序硬编码:组件顺序写死在 match_deploy 中,若你的集群存在非标准 Deployment 命名(如经过二次加工的 Chart),可能匹配不到而实际跳过更新。
  • 仅基于 kubectl 标签与 ConfigMap 约定工作:实例识别依赖 app.kubernetes.io/instance/app.kubernetes.io/name=milvus 标签与 ${INSTANCE}-milvus 命名 ConfigMap,Operator 部署的集群不具备这些约定。
  • RocksMQ standalone 直接拒绝执行:RocksMQ 作为单机消息队列不具备分布式 HA 能力,无法保证滚动期间写入连续性。
  • 目标版本标签的副作用-t 会写入 app.kubernetes.io/version 标签,升级后可用 kubectl get deploy -l app.kubernetes.io/version=2.2.3 等标签选择器核对各组件版本,脚本中未启用的 check_versionrollingUpdate.sh)也正是基于该标签检测实例内是否混跑了多个版本。

七、小结

deployments/upgrade/rollingUpdate.sh 用一个约 180 行的 shell 脚本实现了 Helm 版 Milvus 的零停机滚动升级:以 kubectl 标签定位实例,预检查后自动为协调者开启 Active Standby,再按 rootcoord → datacoord → indexcoord → querycoord → indexnode → datanode → querynode → proxy(standalone)的硬编码顺序逐个 kubectl patch 镜像与版本标签,并对每个 Deployment 阻塞等待 rollout status 成功。掌握其参数与流程后,你可以将其作为 2.2.0 及以上版本集群的日常升级手段;同时应清楚它不支持回滚、不支持 Operator 安装形态与 RocksMQ standalone,失败恢复仍需依赖 Kubernetes 原生的 rollout 工具链。

登录后查看全文
热门项目推荐
相关项目推荐