在 Kubernetes 上运行 Milvus:Operator 与 KubeBlocks Addon 实测

  • 测试时间: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.6 KubeBlocks 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 CR OpsRequest
垂直扩容 修改 Milvus CR 并滚动更新 OpsRequest
查看操作历史 Kubernetes rollout/event OpsRequest 状态
Pod 自愈 已验证 已验证
认证 CR 配置 系统账户 + postProvision
TLS CR + Secret KubeBlocks issuer
内置 Backup 支持
实际备份机制 用户自建 milvus-backup Job ActionSet 调用 milvus-backup
恢复方式 恢复为新集合 恢复为新 Cluster

3. 测试环境与边界

3.1 软件版本与规则

组件 版本
Milvus 2.5.13
Milvus Operator chart 1.3.6
KubeBlocks Core 1.0.2
KubeBlocks Milvus Addon 1.0.2
KubeBlocks etcd Addon 1.0.2
KubeBlocks Kafka Addon 1.0.2
KubeBlocks MinIO Addon 1.0.2
milvus-backup 0.5.9

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

yaml 复制代码
registryConfig:
  defaultRegistry: apecloud-registry.cn-zhangjiakou.cr.aliyuncs.com
  defaultNamespace: apecloud

3.2 VKE 资源

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

4. Distributed 拓扑

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

组件 Milvus Operator KubeBlocks
Proxy 2 2
MixCoord 2,启用 Active-Standby 2
DataNode 2 2
IndexNode 2 2
QueryNode 2 2
etcd 3 3,kb-etcd-ha
Kafka 3 3,kb-kafka-ha KRaft combined
ZooKeeper 3 不使用
MinIO 4 4,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

bash 复制代码
helm upgrade --install milvus-operator \
  milvus-operator/milvus-operator \
  --version 1.3.6 \
  --namespace milvus-operator-system \
  --create-namespace

5.2 创建 Distributed 集群

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

yaml 复制代码
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

    bash 复制代码
    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:

bash 复制代码
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
yaml 复制代码
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

bash 复制代码
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:

yaml 复制代码
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 会持续报告:

text 复制代码
prohibits referencing credential variables from different namespaces,
service-ref: milvus-object-storage

原因并不复杂:不带凭据的 etcd/Kafka 地址可以跨命名空间描述,但 MinIO 用户名和

密码不能通过 serviceRefs 跨命名空间注入。因此,四个 Cluster 放在同一个

命名空间最省事。

6.3 创建 Milvus

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

yaml 复制代码
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:

yaml 复制代码
componentSpecs:
  - name: proxy
    tls: true
    issuer:
      name: KubeBlocks

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

text 复制代码
kb-dist       milvus   Running
kb-etcd-ha             Running
kb-kafka-ha   kafka    Running
kb-minio-ha            Running

6.4 MixCoord Active-Standby

原生 Addon 模板包含:

yaml 复制代码
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

bash 复制代码
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"

搜索请求:

json 复制代码
{
  "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,返回结果一致:

json 复制代码
{"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,关键命令如下:

bash 复制代码
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 → 3 Succeed 42 秒
Proxy 3 → 2 Succeed 33 秒
QueryNode 两副本改为 750m/1536Mi Succeed 86 秒

每个操作都有自己的 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 次搜索结果:

text 复制代码
ok=120 fail=0

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

9.3 KubeBlocks MixCoord Active-Standby

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

text 复制代码
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 端口 8080 HTTP 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:

yaml 复制代码
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 使用相同挂载,核心命令为:

yaml 复制代码
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 如下:

yaml 复制代码
backupMethods:
  - name: full
    actionSetName: milvus-full
    snapshotVolumes: false

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

bash 复制代码
./milvus-backup create -n "$BACKUP_NAME"

底层依旧是同一个 milvus-backup CLI,区别在于 KubeBlocks 把它纳入了

DataProtection 的资源模型。

正式备份前,先创建 Tool 模式的 MinIO BackupRepo。这里最关键的一步,是把 MinIO

系统账户 Secret 转换为 BackupRepo 需要的字段:

bash 复制代码
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 的关键内容如下:

yaml 复制代码
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 完成。

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

bash 复制代码
kbcli -n kb-milvus-distributed cluster backup kb-dist --method full

恢复同样通过 kbcli 发起:

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

恢复时,外部 etcd、Kafka 和 MinIO Cluster 保持运行,新的 Milvus Cluster 直接继承

TLS 配置。账户初始化、证书挂载和 post-ready restore 均成功。需要注意的是,恢复后的

集合不会自动加载;显式执行 collections/load 后,搜索成功返回 id=1。

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

12.1 方法

最后加一轮小型并发搜索。两边使用相同逻辑,客户端直接运行在 Proxy Pod 内,

尽量把外部网络噪声排除掉:

bash 复制代码
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 Operator KubeBlocks
成功率 1000/1000 1000/1000
总耗时 17.043 秒 18.291 秒
QPS 58.67 54.67
平均延迟 183.3 ms 211.8 ms
P50 194.5 ms 200.5 ms
P95 301.3 ms 320.0 ms
P99 327.1 ms 391.8 ms
最大延迟 400.7 ms 413.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. 参考资料