- 测试时间:2026-06-11
- 测试平台:火山引擎 VKE
- 测试目标 :把 Milvus Operator 和 KubeBlocks Milvus Addon 放进同一套三节点
Kubernetes 集群,以 Milvus 2.5.13 Distributed 拓扑逐项比较部署、运行模型、
扩缩容、故障恢复、安全、备份恢复和基础搜索性能。
两种方案都能把 Milvus Distributed 跑起来,但走的是两条不同的路:
Milvus Operator 更专注于 Milvus 本身,KubeBlocks 则更像一套统一的数据库运维平台。
下面不只看功能清单,而是直接看部署、扩缩容、删 Pod 和恢复数据时,
它们分别会交出怎样的答卷。
1. 结论摘要
- Milvus Operator 走的是"专而直接"的路线。 一个
MilvusCR 就能描述
Milvus 组件、etcd、Kafka/ZooKeeper 和 MinIO,认证、TLS 与 Milvus 参数也能直接配置。 - KubeBlocks 擅长把运维动作做成统一 API。 Milvus、etcd、Kafka 和 MinIO
都使用Cluster模型;扩缩容与变配有独立OpsRequest,备份恢复则交给
DataProtection CRD,操作过程更容易追踪。 - KubeBlocks 的 Distributed 部署不是"一份 YAML 全包"。 etcd、Kafka/Pulsar
和 MinIO 需要先作为外部 Cluster 部署,再通过serviceRefs接入;其中带凭据的
MinIO 引用不能跨命名空间。 - 基础能力并没有明显断层。 两种方案都完成了 Distributed 部署、依赖高可用、
扩缩容、Pod 自愈、认证、TLS、备份恢复和并发搜索验证。 - 怎么选,主要看团队的运维重心。 想要更贴近 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/milvus 的 cluster topology 只包含五类 Milvus
组件,不会顺手创建 etcd、消息队列和对象存储。因此部署天然分成两步:
- 创建三个依赖 Cluster。
- 创建 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 上的几个小门槛
-
ZooKeeper 和其他 EBS PVC 不能使用 5 GiB,需要统一调整为 10 GiB。
-
默认海外镜像拉取不够稳定,因此 Milvus 使用 ApeCloud 国内镜像,依赖镜像使用国内代理。
-
spec.components.toolImage已更新,但 Operator 1.3.6 没有把该镜像同步到所有Distributed Deployment 的
configinit container。这里需要对受影响的Deployment 额外执行一次
kubectl set image:bashkubectl -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/1Gi→750m/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: true、tlsMode: 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. 未覆盖范围
- Milvus 跨版本升级:按测试要求主动排除。
- ServiceMonitor/Prometheus 实际抓取 :VKE 没有相关 CRD,后续安装验证按要求
取消。两套 Proxy 的/metrics均能返回 Prometheus 文本,但这不等同于
ServiceMonitor 已验证。 - 整节点 drain/Node 宕机测试 :两种方案均未执行。Pod 删除测试只能验证副本
丢失与自动重建,不能替代完整的节点故障测试。 - 生产级容量上限 :只执行了小数据集并发搜索,没有百万级数据、索引构建、
混合读写、长期稳定性和成本测试。
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-fullActionSet 与 BackupRepo 在目标环境中的镜像、凭据和网络可达性;- 依赖与 Milvus 的命名空间规划;
- BackupRepo、凭据生命周期、恢复流程和状态判断。
14.3 共同上线要求
无论最后站哪一边,生产上线前都绕不开这些功课:
- 整节点故障、长期稳定性和组件滚动重启测试;
- 生产规模数据、索引构建和混合读写压测;
- 监控、告警和容量规划;
- VKE ENI、EBS、CoreDNS 和 Pod 反亲和规划;
- 定期备份恢复演练及恢复结果验证。
这场对比没有绝对赢家。Milvus Operator 把路径缩短,KubeBlocks 把运维动作标准化。
前者更像一把专用工具,后者更像一套平台能力。选型时,与其纠结小型压测中的几项
数字,不如先回答一个更实际的问题:团队接下来主要是在"运维 Milvus",还是在
"统一运维一批数据库"?