• 测试时间:2026-06-11
  • 测试平台:火山引擎 VKE
  • 测试目标:把 Milvus Operator 和 KubeBlocks Milvus Addon 放进同一套三节点
    Kubernetes 集群,以 Milvus 2.5.13 Distributed 拓扑逐项比较部署、运行模型、
    扩缩容、故障恢复、安全、备份恢复和基础搜索性能。

两种方案都能把 Milvus Distributed 跑起来,但走的是两条不同的路:
Milvus Operator 更专注于 Milvus 本身,KubeBlocks 则更像一套统一的数据库运维平台。
下面不只看功能清单,而是直接看部署、扩缩容、删 Pod 和恢复数据时,
它们分别会交出怎样的答卷。

1. 结论摘要

  1. Milvus Operator 走的是“专而直接”的路线。 一个 Milvus CR 就能描述
    Milvus 组件、etcd、Kafka/ZooKeeper 和 MinIO,认证、TLS 与 Milvus 参数也能直接配置。
  2. KubeBlocks 擅长把运维动作做成统一 API。 Milvus、etcd、Kafka 和 MinIO
    都使用 Cluster 模型;扩缩容与变配有独立 OpsRequest,备份恢复则交给
    DataProtection CRD,操作过程更容易追踪。
  3. KubeBlocks 的 Distributed 部署不是“一份 YAML 全包”。 etcd、Kafka/Pulsar
    和 MinIO 需要先作为外部 Cluster 部署,再通过 serviceRefs 接入;其中带凭据的
    MinIO 引用不能跨命名空间。
  4. 基础能力并没有明显断层。 两种方案都完成了 Distributed 部署、依赖高可用、
    扩缩容、Pod 自愈、认证、TLS、备份恢复和并发搜索验证。
  5. 怎么选,主要看团队的运维重心。 想要更贴近 Milvus 原生模型,选
    Milvus Operator;已经用 KubeBlocks 管理多类数据库,并看重统一操作 API,
    KubeBlocks 会更顺手。

2. 功能对比

能力Milvus Operator 1.3.6KubeBlocks Milvus Addon 1.0.2
部署 Distributed 架构已验证已验证
部署入口一个 Milvus CR依赖 Cluster + Milvus Cluster
依赖管理(etcd/Kafka/MinIO)内置声明(Helm)外部 Cluster
依赖 Day-2 管理修改 inCluster.values,依赖 Kubernetes/Helm独立 Cluster 和 OpsRequest
同类组件反亲和CR 配置Cluster componentSpec 配置
Coordinator Active-Standby已验证已验证
水平扩缩容修改 Milvus CROpsRequest
垂直扩容修改 Milvus CR 并滚动更新OpsRequest
查看操作历史Kubernetes rollout/eventOpsRequest 状态
Pod 自愈已验证已验证
认证CR 配置系统账户 + postProvision
TLSCR + SecretKubeBlocks issuer
内置 Backup 支持
实际备份机制用户自建 milvus-backup JobActionSet 调用 milvus-backup
恢复方式恢复为新集合恢复为新 Cluster

3. 测试环境与边界

3.1 软件版本与规则

组件版本
Milvus2.5.13
Milvus Operator chart1.3.6
KubeBlocks Core1.0.2
KubeBlocks Milvus Addon1.0.2
KubeBlocks etcd Addon1.0.2
KubeBlocks Kafka Addon1.0.2
KubeBlocks MinIO Addon1.0.2
milvus-backup0.5.9

为了减少海外镜像拉取带来的随机因素,KubeBlocks 使用以下国内镜像仓库配置:

registryConfig:
  defaultRegistry: apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com
  defaultNamespace: apecloud

3.2 VKE 资源

项目配置
Kubernetesv1.34.6-vke.7
节点3 个
单节点8 vCPU、约 32 GiB 内存
单节点可分配 CPU7.8 vCPU
单节点可分配内存约 27.2 GiB
CNIVKE VPC-CNI,Pod 使用 ENI IP
StorageClassebs-ssd
EBS 最小容量10 GiB
VolumeSnapshot CRD已安装
Metrics API

4. Distributed 拓扑

两边采用相同的 Milvus 组件副本数,依赖也尽量对齐:

组件Milvus OperatorKubeBlocks
Proxy22
MixCoord2,启用 Active-Standby2
DataNode22
IndexNode22
QueryNode22
etcd33,kb-etcd-ha
Kafka33,kb-kafka-ha KRaft combined
ZooKeeper3不使用
MinIO44,kb-minio-ha

4.1 Milvus Operator:一个入口,不等于一个进程

Milvus Operator 的 Milvus CR 可以同时声明 Milvus 组件和内置依赖,部署入口确实
只有一个。不过,etcd、Kafka 和 MinIO 并不是由各自的专用 Operator 管理,而是通过
spec.dependencies.<组件>.inCluster.values 把 Helm values 写入 Milvus CR,
再由 Milvus Operator 安装和协调对应的独立 Helm release。本文中的 etcd、Kafka、
ZooKeeper 和 MinIO 最终仍以 StatefulSet、Service、PVC 等 Kubernetes 资源运行。

这套模型的基础 Day-2 能力并不缺席:Pod 删除后会由 StatefulSet 自动重建,也可以修改
inCluster.values 调整副本、资源、存储和反亲和配置。但它没有类似
KubeBlocks OpsRequest 的独立操作对象,扩缩容、升级和 PVC 变更的安全性主要取决于底层
Helm chart、StorageClass 及组件自身机制,也不提供面向这些依赖的统一备份恢复
API。换句话说,如果团队希望对 etcd、Kafka 和 MinIO 分别做升级、备份、审计和
生命周期治理,外部托管服务或专用 Operator 会更合适。

4.2 KubeBlocks:先搭依赖,再接 Milvus

KubeBlocks ClusterDefinition/milvuscluster topology 只包含五类 Milvus
组件,不会顺手创建 etcd、消息队列和对象存储。因此部署天然分成两步:

  1. 创建三个依赖 Cluster。
  2. 创建 Milvus Cluster,通过 serviceRefs 注入连接地址和 MinIO 凭据。

5. Milvus Operator 部署流程

5.1 安装 Operator

helm upgrade --install milvus-operator \
  milvus-operator/milvus-operator \
  --version 1.3.6 \
  --namespace milvus-operator-system \
  --create-namespace

5.2 创建 Distributed 集群

下面保留决定部署模型的关键 Manifest。资源限制和重复配置先收起来,主干会更清楚:

apiVersion: milvus.io/v1beta1
kind: Milvus
metadata:
  name: milvus-dist
  namespace: milvus-operator-distributed
spec:
  mode: cluster
  config:
    common:
      security:
        authorizationEnabled: true
    rootCoord:
      enableActiveStandby: true
    queryCoord:
      enableActiveStandby: true
    dataCoord:
      enableActiveStandby: true
    indexCoord:
      enableActiveStandby: true
  dependencies:
    msgStreamType: kafka
    etcd:
      inCluster:
        values:
          replicaCount: 3
          persistence:
            size: 10Gi
            storageClass: ebs-ssd
          podAntiAffinityPreset: hard
    kafka:
      inCluster:
        values:
          replicaCount: 3
          defaultReplicationFactor: 3
          zookeeper:
            replicaCount: 3
    storage:
      inCluster:
        values:
          mode: distributed
          replicas: 4
  components:
    image: apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/milvus:v2.5.13
    proxy:
      replicas: 2
    mixCoord:
      replicas: 2
    dataNode:
      replicas: 2
    indexNode:
      replicas: 2
    queryNode:
      replicas: 2

5.3 VKE 上的几个小门槛

  1. ZooKeeper 和其他 EBS PVC 不能使用 5 GiB,需要统一调整为 10 GiB。

  2. 默认海外镜像拉取不够稳定,因此 Milvus 使用 ApeCloud 国内镜像,依赖镜像使用国内代理。

  3. spec.components.toolImage 已更新,但 Operator 1.3.6 没有把该镜像同步到所有
    Distributed Deployment 的 config init container。这里需要对受影响的
    Deployment 额外执行一次 kubectl set image

    kubectl -n milvus-operator-distributed \
      get deployment -l app.kubernetes.io/instance=milvus-dist -o name |
    while read -r deployment; do
      kubectl -n milvus-operator-distributed \
        set image "$deployment" \
        config=apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/milvus-operator:v0.9.17
    done
    

    执行前要先确认 Deployment 中确实存在名为 config 的 init container。

5.4 TLS

TLS 的操作路径很直接:生成带 Service DNS SAN 的证书,创建 Secret,再把证书目录
和 TLS 配置挂到 Milvus CR:

openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 \
  -subj "/CN=milvus-vke-test-ca" -out ca.pem

openssl x509 -req -in server.csr \
  -CA ca.pem -CAkey ca.key -CAcreateserial \
  -out server.pem -days 365 -sha256 -extfile ext.cnf

kubectl -n milvus-operator-distributed \
  create secret generic milvus-tls \
  --from-file=server.pem --from-file=server.key --from-file=ca.pem
common:
  security:
    authorizationEnabled: true
    tlsMode: 1
tls:
  serverPemPath: /certs/server.pem
  serverKeyPath: /certs/server.key
  caPemPath: /certs/ca.pem
components:
  volumes:
    - name: certs
      secret:
        secretName: milvus-tls
  volumeMounts:
    - name: certs
      mountPath: /certs
      readOnly: true

6. KubeBlocks 部署流程

6.1 安装 Core 与 Addon

helm upgrade --install kubeblocks kubeblocks/kubeblocks \
  --version 1.0.2 \
  --namespace kb-system \
  --create-namespace \
  --set registryConfig.defaultRegistry=apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com \
  --set registryConfig.defaultNamespace=apecloud

Core 就位后,再安装 Milvus、etcd、Kafka、MinIO 1.0.2 Addon。

6.2 创建依赖

三个依赖 Cluster 先登场。下面是关键规格,PVC 统一使用 ebs-ssd 和 10 GiB:

apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  name: kb-etcd-ha
  namespace: kb-milvus-distributed
spec:
  componentSpecs:
    - name: etcd
      componentDef: etcd
      serviceVersion: 3.6.1
      replicas: 3
      volumeClaimTemplates:
        - name: data
          spec:
            storageClassName: ebs-ssd
            resources:
              requests:
                storage: 10Gi
---
apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  name: kb-kafka-ha
  namespace: kb-milvus-distributed
spec:
  clusterDef: kafka
  topology: combined_monitor
  componentSpecs:
    - name: kafka-combine
      serviceVersion: 3.9.0
      replicas: 3
      services:
        - name: advertised-listener
          serviceType: ClusterIP
          podService: true
      env:
        - name: KB_CLUSTER_WITH_ZK
          value: "false"
---
apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  name: kb-minio-ha
  namespace: kb-milvus-distributed
spec:
  componentSpecs:
    - name: minio
      componentDef: minio
      replicas: 4
      env:
        - name: MINIO_BUCKETS
          value: kb-milvus-distributed

这里有几个不能忽略的细节:

  • etcd 和 Kafka 使用强 Pod 反亲和,三个副本分别位于三个节点。
  • MinIO 使用优先反亲和,最终分布为 2/1/1。
  • Kafka 的 data 和 metadata PVC 都必须为 10 GiB。
  • 三个依赖 Cluster 与 Milvus Cluster 必须位于
    kb-milvus-distributed 命名空间。

如果把依赖放进 kb-milvus-deps,KubeBlocks Controller 会持续报告:

prohibits referencing credential variables from different namespaces,
service-ref: milvus-object-storage

原因并不复杂:不带凭据的 etcd/Kafka 地址可以跨命名空间描述,但 MinIO 用户名和
密码不能通过 serviceRefs 跨命名空间注入。因此,四个 Cluster 放在同一个
命名空间最省事。

6.3 创建 Milvus

Proxy 负责定义公共 serviceRefs 和配置变量,其他组件直接复用,避免五份配置各写一遍:

apiVersion: apps.kubeblocks.io/v1
kind: Cluster
metadata:
  name: kb-dist
  namespace: kb-milvus-distributed
spec:
  clusterDef: milvus
  topology: cluster
  componentSpecs:
    - name: proxy
      serviceVersion: 2.5.13
      replicas: 2
      serviceRefs: &serviceRefs
        - name: milvus-meta-storage
          clusterServiceSelector:
            cluster: kb-etcd-ha
            service:
              component: etcd
              service: headless
              port: client
        - name: milvus-log-storage
          clusterServiceSelector:
            cluster: kb-kafka-ha
            service:
              component: kafka-combine
              service: advertised-listener
              port: broker
        - name: milvus-object-storage
          clusterServiceSelector:
            cluster: kb-minio-ha
            service:
              component: minio
              service: headless
              port: api
            credential:
              component: minio
              name: root
      configs: &configs
        - name: config
          variables:
            mq_type: kafka
            minio_bucket: kb-milvus-distributed
            minio_root_path: files
            minio_use_path_style: "true"
    - name: mixcoord
      serviceVersion: 2.5.13
      replicas: 2
      serviceRefs: *serviceRefs
      configs: *configs
    - name: datanode
      serviceVersion: 2.5.13
      replicas: 2
      serviceRefs: *serviceRefs
      configs: *configs
    - name: indexnode
      serviceVersion: 2.5.13
      replicas: 2
      serviceRefs: *serviceRefs
      configs: *configs
    - name: querynode
      serviceVersion: 2.5.13
      replicas: 2
      serviceRefs: *serviceRefs
      configs: *configs

Proxy 在首次创建时就可以直接启用 TLS:

componentSpecs:
  - name: proxy
    tls: true
    issuer:
      name: KubeBlocks

全部就绪后,四个 Cluster 都进入 Running

kb-dist       milvus   Running
kb-etcd-ha             Running
kb-kafka-ha   kafka    Running
kb-minio-ha            Running

6.4 MixCoord Active-Standby

原生 Addon 模板包含:

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

两个 MixCoord 都保持 Ready。日志显示其中一个副本进入 STANDBY;删除 ACTIVE
副本后,RootCoord、DataCoord、QueryCoord 和 IndexCoord 都记录了
quit STANDBY mode, this node will become ACTIVE

7. 数据功能验证

先做一轮基础数据验证。两套集群分别创建一个 8 维 COSINE 集合,写入 1000 行数据,
每批 100 行:

  • Operator:operator_dist_test_20260611
  • KubeBlocks:kb_dist_test_20260611

核心 REST 调用如下,实际脚本会以 100 行为一批循环构造 data

curl --cacert "$CA_CERT" \
  -H "Authorization: $AUTHORIZATION" \
  -H "Content-Type: application/json" \
  -d '{
    "collectionName": "kb_dist_test_20260611",
    "dimension": 8,
    "metricType": "COSINE",
    "primaryFieldName": "id",
    "vectorFieldName": "vector",
    "idType": "Int64",
    "autoId": false
  }' \
  "$ENDPOINT/v2/vectordb/collections/create"

curl --cacert "$CA_CERT" \
  -H "Authorization: $AUTHORIZATION" \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"kb_dist_test_20260611","data":[
    {"id":1,"vector":[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8]}
  ]}' \
  "$ENDPOINT/v2/vectordb/entities/insert"

curl --cacert "$CA_CERT" \
  -H "Authorization: $AUTHORIZATION" \
  -H "Content-Type: application/json" \
  -d '{"collectionName":"kb_dist_test_20260611"}' \
  "$ENDPOINT/v2/vectordb/collections/load"

搜索请求:

{
  "collectionName": "kb_dist_test_20260611",
  "data": [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8]],
  "annsField": "vector",
  "limit": 1,
  "outputFields": ["id"]
}

两边都命中 id=1,返回结果一致:

{"code":0,"data":[{"distance":0.99999994,"id":1}]}

8. 扩缩容

8.1 Milvus Operator

Operator 的扩缩容方式很朴素:直接修改 Milvus CR。水平扩缩容结果如下:

  • IndexNode:2 → 1 → 2;
  • Proxy:2 → 3 → 2;
  • 扩容组合变更 67 秒,恢复原副本数 60 秒;
  • 变更前后搜索成功。

纵向扩容也走同一条路径:

  • QueryNode request:500m/1Gi750m/1536Mi
  • Operator 使用新 Deployment group 滚动替换 QueryNode;
  • 73 秒完成;
  • 两副本恢复后搜索成功。

这套方式简单直接,但 Operator 没有独立的 OpsRequest 历史。想回看操作过程,
主要还是查 CR generation、Deployment rollout 和 Kubernetes 事件。

8.2 KubeBlocks

KubeBlocks 则把每次变更包装成 OpsRequest,关键命令如下:

kbcli -n kb-milvus-distributed \
  cluster scale-out kb-dist --components=proxy --replicas=1 \
  --name=proxy-out --auto-approve

kubectl -n kb-milvus-distributed \
  wait --for=jsonpath='{.status.phase}'=Succeed \
  opsrequest/proxy-out --timeout=12m

kbcli -n kb-milvus-distributed \
  cluster scale-in kb-dist --components=proxy --replicas=1 \
  --name=proxy-in --auto-approve

kbcli -n kb-milvus-distributed \
  cluster vscale kb-dist --components=querynode \
  --cpu=750m --memory=1536Mi \
  --name=query-vscale --auto-approve

实测结果:

操作结果用时
Proxy 2 → 3Succeed42 秒
Proxy 3 → 2Succeed33 秒
QueryNode 两副本改为 750m/1536MiSucceed86 秒

每个操作都有自己的 CR、状态、进度和持续时间,过程比直接改副本数更“有账可查”。
三次操作完成后,搜索均正常。

9. 故障与高可用

9.1 Milvus Operator 多 Pod 故障

扩缩容通过后,开始做一些不太温柔的操作。先同时删除一个 etcd、Kafka 和 MinIO Pod:

  • 三类依赖均自动重建;
  • Proxy 访问日志记录的 60 次搜索全部返回 HTTP 200;
  • Milvus CR 保持 Healthy

接着删除 ACTIVE MixCoord,并连续执行 120 次认证 HTTPS 搜索,结果为
ok=120 fail=0。原 STANDBY 副本接管 RootCoord、DataCoord、QueryCoord 和
IndexCoord,被删除的 Pod 自动重建。

9.2 KubeBlocks 多 Pod 故障

KubeBlocks 这边一次拿掉更多角色,同时删除:

  • 一个 etcd;
  • 一个 Kafka;
  • 一个 MinIO;
  • 一个 DataNode;
  • 一个 QueryNode。

连续 120 次搜索结果:

ok=120 fail=0

五个 Pod 都由 KubeBlocks/InstanceSet 自动补回,业务搜索没有出现失败。

9.3 KubeBlocks MixCoord Active-Standby

接下来单独删除 ACTIVE MixCoord,同时连续执行 120 次认证 HTTPS 搜索:

ok=120 fail=0

原 STANDBY 副本完成四类 Coordinator 接管,被删除的 Pod 也自动重建。至少在这套
Distributed 拓扑里,原生 Addon 的 MixCoord Active-Standby 经受住了实际删除测试。

10. 认证与 TLS

10.1 Milvus Operator

安全能力不只看配置有没有写进去,还要看正确请求能否通过、错误请求能否被挡住。
Operator 的认证与单向 TLS 结果如下:

请求结果
HTTPS + CA + root:Milvus搜索成功
HTTPS,不带认证code=1800, user hasn't authenticated
HTTP 请求 TLS 端口HTTP 400

Operator CR 可以直接挂载 Secret 并配置 Milvus TLS 路径,链路比较直观。

10.2 KubeBlocks 原生 Addon

KubeBlocks 也执行同样的正反向检查:

请求结果
HTTPS + KubeBlocks CA + 生成的 root 密码code=0
HTTPS,不带认证code=1800, user hasn't authenticated
HTTPS,错误密码code=1800, user hasn't authenticated
HTTP 请求 TLS 端口 8080HTTP 400

Proxy ComponentDefinition 已经把 root 系统账户、随机密码生成策略、TLS Secret
挂载和 postProvision 串了起来。证书由 KubeBlocks 签发,SAN 包含 localhost
和 Proxy Headless Service 域名;postProvision 也能直接通过 TLS 初始化随机
root 密码。

实际渲染配置包含 authorizationEnabled: truetlsMode: 1、HTTPS REST
端口 8080,以及四类 Coordinator Active-Standby。

11. 备份与恢复

11.1 Milvus Operator

备份恢复是两种方案差异最明显的部分。Milvus Operator 1.3.6 没有 Backup CRD,
因此需要自己组装一个 Job:为 milvus-backup 配置认证、TLS、MinIO 地址,并挂载
配置文件与 CA:

apiVersion: v1
kind: ConfigMap
metadata:
  name: milvus-backup-config
  namespace: milvus-operator-distributed
data:
  backup.yaml: |
    milvus:
      address: milvus-dist-milvus.milvus-operator-distributed.svc
      port: 19530
      authorizationEnabled: true
      tlsMode: 1
      user: root
      password: Milvus
      caCertPath: /certs/ca.pem
      serverName: milvus-dist-milvus.milvus-operator-distributed.svc
    minio:
      storageType: minio
      address: milvus-dist-minio.milvus-operator-distributed.svc
      port: 9000
      bucketName: milvus-dist
      rootPath: files
      backupStorageType: minio
      backupBucketName: milvus-dist
      backupRootPath: backup
---
apiVersion: batch/v1
kind: Job
metadata:
  name: operator-backup-create
  namespace: milvus-operator-distributed
spec:
  template:
    spec:
      restartPolicy: Never
      containers:
        - name: backup
          image: apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com/apecloud/milvus-backup:v0.5.9-yq
          command:
            - ./milvus-backup
            - create
            - --config
            - /config/backup.yaml
            - --name
            - operator_dist_backup
          volumeMounts:
            - name: config
              mountPath: /config
            - name: certs
              mountPath: /certs
      volumes:
        - name: config
          configMap:
            name: milvus-backup-config
        - name: certs
          secret:
            secretName: milvus-tls

恢复 Job 使用相同挂载,核心命令为:

command:
  - ./milvus-backup
  - restore
  - --config
  - /config/backup.yaml
  - --name
  - operator_dist_backup
  - --suffix
  - _restored
  - --rebuild_index

Job 跑完后的结果如下:

项目结果
工具milvus-backup:v0.5.9-yq
备份名operator_dist_backup
备份耗时2.28 秒
恢复目标operator_dist_test_20260611_restored
恢复耗时19.33 秒
恢复查询成功,返回 id=1

日志里出现了一个值得留意的小插曲:milvus-backup 的 REST segment describe
客户端没有使用自签 CA,因此报告证书校验失败。不过工具自动回退到对象存储文件列表,
最终备份、gRPC 认证/TLS、数据复制和恢复都成功完成。

功能能跑通,但下面这些平台能力仍要由团队自己补齐:

  • Job 编排;
  • 备份仓库凭据;
  • 调度和保留策略;
  • 状态采集与告警;
  • 恢复命名和冲突处理。

11.2 KubeBlocks

KubeBlocks 的起点不同:Milvus Addon 已经带上备份策略和对应的 ActionSet。
BackupPolicy/kb-dist-proxy-backup-policy 如下:

backupMethods:
  - name: full
    actionSetName: milvus-full
    snapshotVolumes: false

ActionSet/milvus-full 的备份命令是:

./milvus-backup create -n "$BACKUP_NAME"

底层依旧是同一个 milvus-backup CLI,区别在于 KubeBlocks 把它纳入了
DataProtection 的资源模型。

正式备份前,先创建 Tool 模式的 MinIO BackupRepo。这里最关键的一步,是把 MinIO
系统账户 Secret 转换为 BackupRepo 需要的字段:

kubectl -n kb-milvus-distributed \
  get secret kb-minio-ha-minio-account-root -o json |
  jq '{
    apiVersion: "v1",
    kind: "Secret",
    metadata: {
      name: "kb-milvus-backup-credentials",
      namespace: "kb-milvus-distributed"
    },
    type: "Opaque",
    data: {
      accessKeyId: .data.username,
      secretAccessKey: .data.password
    }
  }' |
  kubectl apply -f -

BackupRepo 的关键内容如下:

apiVersion: dataprotection.kubeblocks.io/v1alpha1
kind: BackupRepo
metadata:
  name: kb-milvus-backup-repo
  annotations:
    dataprotection.kubeblocks.io/is-default-repo: "true"
spec:
  storageProviderRef: minio
  accessMethod: Tool
  pvReclaimPolicy: Delete
  pathPrefix: milvus-backups
  credential:
    name: kb-milvus-backup-credentials
    namespace: kb-milvus-distributed
  config:
    endpoint: http://kb-minio-ha-minio.kb-milvus-distributed.svc:9000
    bucket: kb-milvus-distributed
    region: us-east-1
    insecure: "true"
    noCheckBucket: "true"

这个模式不依赖 S3 CSI Driver,也不走 VolumeSnapshot,数据复制由 ActionSet 中的
milvus-backup 完成。

仓库准备好后,备份命令只剩一行:

kbcli -n kb-milvus-distributed cluster backup kb-dist --method full

恢复同样通过 kbcli 发起:

kbcli -n kb-milvus-distributed cluster restore kb-dist-restored \
  --backup kb-secure-20260611 \
  --backup-namespace kb-milvus-distributed \
  --restore-after-cluster-running
项目结果
Backupkb-secure-20260611
状态Completed
仓库kb-milvus-backup-repo
Backup duration12 秒
恢复目标kb-dist-restored
Restore OpsRequestSucceed
post-ready restoreCompleted,26 秒
恢复查询加载原集合后成功,返回 id=1

恢复时,外部 etcd、Kafka 和 MinIO Cluster 保持运行,新的 Milvus Cluster 直接继承
TLS 配置。账户初始化、证书挂载和 post-ready restore 均成功。需要注意的是,恢复后的
集合不会自动加载;显式执行 collections/load 后,搜索成功返回 id=1。

12. 小型压力测试:请求跑起来表现如何

12.1 方法

最后加一轮小型并发搜索。两边使用相同逻辑,客户端直接运行在 Proxy Pod 内,
尽量把外部网络噪声排除掉:

payload='{
  "collectionName":"kb_dist_test_20260611",
  "data":[[0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8]],
  "annsField":"vector",
  "limit":10,
  "outputFields":["id"]
}'
export payload ENDPOINT AUTHORIZATION CA_CERT

seq 1 "$REQUESTS" |
  xargs -P "$CONCURRENCY" -I '{}' sh -c '
    curl -sS --max-time 10 --cacert "$CA_CERT" \
      -H "Authorization: $AUTHORIZATION" \
      -H "Content-Type: application/json" \
      -o "/tmp/response-$$" -w "%{time_total}" \
      -d "$payload" \
      "$ENDPOINT/v2/vectordb/entities/search"
  '

# 记录每次请求的耗时与成功状态,再用 awk/sort 计算
# QPS、平均延迟、P50、P95、P99 和最大延迟。

参数:

  • 数据量:1000 行、8 维向量;
  • 请求:1000 次搜索;
  • 并发:20;
  • TopK:10;
  • REST API;
  • 客户端运行在 Proxy Pod 内,避免外部网络影响。

12.2 结果

指标Milvus OperatorKubeBlocks
成功率1000/10001000/1000
总耗时17.043 秒18.291 秒
QPS58.6754.67
平均延迟183.3 ms211.8 ms
P50194.5 ms200.5 ms
P95301.3 ms320.0 ms
P99327.1 ms391.8 ms
最大延迟400.7 ms413.1 ms

12.3 解释限制

先说结论:这张表不能拿来做产品性能排名,原因包括:

  • 两次测试均启用了认证和单向 TLS;
  • 测试时间不同,节点即时负载和 EBS 后台状态不同;
  • 数据量太小,主要测量 Proxy、REST、认证/TLS 和调度开销;
  • 没有构建大规模 ANN 索引;
  • 没有持续写入、混合读写和 compaction 压力;
  • VKE 没有 Metrics API,无法关联 CPU、内存和磁盘吞吐。

这轮测试能说明的事情很有限,但也很明确:两套 Distributed 集群都能在并发 20 下
完成 1000/1000 次请求。它证明了基础链路可用,不代表生产容量上限。

13. 未覆盖范围

  1. Milvus 跨版本升级:按测试要求主动排除。
  2. ServiceMonitor/Prometheus 实际抓取:VKE 没有相关 CRD,后续安装验证按要求
    取消。两套 Proxy 的 /metrics 均能返回 Prometheus 文本,但这不等同于
    ServiceMonitor 已验证。
  3. 整节点 drain/Node 宕机测试:两种方案均未执行。Pod 删除测试只能验证副本
    丢失与自动重建,不能替代完整的节点故障测试。
  4. 生产级容量上限:只执行了小数据集并发搜索,没有百万级数据、索引构建、
    混合读写、长期稳定性和成本测试。

14. 选型建议

真正影响选型的,不是小数据集里几毫秒的延迟差,而是团队想要“更专注的 Milvus
管理”,还是“跨数据库的一致运维体验”。

14.1 选择 Milvus Operator

下面这些情况更适合 Milvus Operator:

  • 主要管理 Milvus,希望使用官方领域模型;
  • 需要直接配置认证、TLS 和 Coordinator Active-Standby;
  • 希望一个 CR 同时描述 Milvus 与依赖;
  • 团队能自行建设定时备份调度、状态跟踪、保留策略和恢复流程。

代价是平台侧要自己补齐:

  • 国内镜像与 init container 镜像同步;
  • 独立 milvus-backup 的调度、状态、告警和保留策略;
  • 恢复命名、冲突处理和自动化流程。

14.2 选择 KubeBlocks

下面这些情况更适合 KubeBlocks:

  • 已经使用 KubeBlocks 管理多类数据库;
  • 希望使用一致的 Cluster 模型管理 Milvus、etcd、Kafka 和 MinIO;
  • 重视可追踪的 OpsRequest 和统一的数据保护资源模型;
  • 需要统一的系统账户、随机密码和 TLS 证书管理;
  • 能接受先部署并维护 etcd、Kafka、MinIO Cluster。

上线前建议重点确认:

  • 首次部署和恢复流程中的账户、TLS 证书与 Secret 生命周期;
  • milvus-full ActionSet 与 BackupRepo 在目标环境中的镜像、凭据和网络可达性;
  • 依赖与 Milvus 的命名空间规划;
  • BackupRepo、凭据生命周期、恢复流程和状态判断。

14.3 共同上线要求

无论最后站哪一边,生产上线前都绕不开这些功课:

  • 整节点故障、长期稳定性和组件滚动重启测试;
  • 生产规模数据、索引构建和混合读写压测;
  • 监控、告警和容量规划;
  • VKE ENI、EBS、CoreDNS 和 Pod 反亲和规划;
  • 定期备份恢复演练及恢复结果验证。

这场对比没有绝对赢家。Milvus Operator 把路径缩短,KubeBlocks 把运维动作标准化。
前者更像一把专用工具,后者更像一套平台能力。选型时,与其纠结小型压测中的几项
数字,不如先回答一个更实际的问题:团队接下来主要是在“运维 Milvus”,还是在
“统一运维一批数据库”?

15. 参考资料

更多推荐