基于 MinIO 对象存储的 Vertica Eon on K8s 部署实战¶
作者:JiangChong | 撰写时间:2026年08月
部署环境:Kubernetes kubeadm 生产部署实战 搭建的 7 节点 K8s 集群(3 control-plane + 4 worker,Debian 13.6,2核4G,40GiB 系统盘,Flannel VXLAN CNI)。本文带你在此环境上部署一套 3 节点的 Vertica Eon 集群,公共存储使用 MinIO。
0 本文概览¶
本文是 Vertica Eon 模式在 K8s 上的实战部署指南——从 MinIO 部署到 vsql 查询验证,每一步都是可执行的命令。
与系列中 Vertica on Kubernetes 部署指南 的 kind 入门教程不同:kind 教程用单机假环境让你「认识 Vertica Operator」,本文用 Kubernetes kubeadm 生产部署实战 的真实 K8s 生产集群 + MinIO(官方支持的 S3 兼容存储),让你「会部署 Eon 模式」。
前置知识:Kubernetes 核心架构(K8s 组件)、Kubernetes 存储体系(PV/PVC/CSI)、Kubernetes 网络模型。
1. 概念速览¶
1.1 架构概览¶
flowchart TB
subgraph VPC["阿里云 ECS VPC (172.20.65.0/24)"]
subgraph W1["k8s-w1 (172.20.65.69)"]
P0["vertica-primary-0<br/>(depot: emptyDir)"]
end
subgraph W2["k8s-w2 (.74)"]
P1["vertica-primary-1"]
end
subgraph W3["k8s-w3 (172.20.65.70)"]
P2["vertica-primary-2<br/>(depot: emptyDir)"]
end
MinIO["MinIO(公共存储,S3 兼容)<br/>minio.minio.svc.cluster.local:9000<br/>/vertica-communal/"]
W1 --> MinIO
W2 --> MinIO
W3 --> MinIO
end
1.2 Vertica Eon 关键概念¶
- Eon 模式:存算分离架构。数据持久化在对象存储(MinIO),计算节点(K8s Pod)从 MinIO 读取数据,可弹性扩缩。
- Communal Storage:共享数据层,存所有数据。本文用MinIO(S3 兼容,Vertica 官方支持,部署在同一 K8s 集群内)。
- Depot:每个节点的本地缓存,加速热点数据访问。Pod 删除后可重建,数据不丢失(源头在 MinIO)。
- Catalog:元数据存储(表结构、权限等),对延迟极其敏感。Vertica Operator 自动管理。
- 子集群(Subcluster):一组功能相同的节点。可以分 primary(读写)和 secondary(只读)子集群。
- K-Safety:高可用机制。K-safety=1 表示任意 1 个节点故障不丢数据。
2. 前置准备¶
执行位置说明:除特别标注外,以下
kubectl命令均在 k8s-cp1 上执行(Kubernetes kubeadm 生产部署实战 3.3 节已配置$HOME/.kube/config)。
2.1 K8s 集群¶
| 节点 | IP | 角色 | 说明 |
|---|---|---|---|
| k8s-w1 | 172.20.65.69 | worker | Vertica Pod 调度目标 |
| k8s-w2 | 172.20.65.74 | worker | Vertica Pod 调度目标 |
| k8s-w3 | 172.20.65.70 | worker | Vertica Pod 调度目标 |
| k8s-w4 | 172.20.65.78 | worker | 2.3 节添加,满足 MinIO 4 节点要求 |
| k8s-cp1 | 172.20.65.73 | control-plane | 不跑 Vertica(默认有 NoSchedule taint) |
| k8s-cp2 | 172.20.65.72 | control-plane | 不跑 Vertica |
| k8s-cp3 | 172.20.65.71 | control-plane | 不跑 Vertica |
确认集群状态:
kubectl get nodes7 台全部Ready。
2.2 资源评估¶
本文环境(2核4G 40GiB): 以下是本文各组件的资源配置及各组件实际消耗。
| 每台 worker 运行 | Request | Limit | 备注 |
|---|---|---|---|
| OS + K8s(kubelet/containerd/Flannel) | — | ~1.5 Gi / 0.3 核 | 固定开销 |
| Vertica Pod | 1 Gi / 200m | 2 Gi / 1 核 | 启动峰值 ~1.5 Gi,稳态 ~512 Mi |
| MinIO Pod | 256 Mi / 100m | 512 Mi / 500m | 分布式模式,I/O 轻量 |
| 合计 request | ~1.25 Gi / 300m | 调度无障碍 | |
| 峰值(全到 limit 后 OOM 前) | ~2.5 Gi | 仍有 ~1.5 Gi 余量 | |
| 磁盘 | ~21 Gi(OS 8G + MinIO 5G + catalog 5G + 镜像 3G) | 40G 盘余 ~10 Gi |
结论:能跑通,但无余量。禁止开 Query Profiling、UDF 或大结果集查询。
生产最低配置(每个 Vertica 节点):
| 项目 | 最低 | 说明 |
|---|---|---|
| CPU | 4 核 | 至少 1 核给 OS/后台线程 + 3 核给 Vertica |
| 内存 | 16 Gi | MAXMEMORYSIZE 建议设为物理内存的 80%,实际可用约 13 Gi |
| 数据盘 | 500 GB SSD | depot + catalog 分盘部署更佳 |
| 网络 | 万兆 | 节点间 Spread 和公共存储 I/O 对延迟敏感 |
生产最佳实践:
| 项目 | 推荐 | 说明 |
|---|---|---|
| CPU | 8-16 核 | 高并发攒批/复杂分析查询受益明显 |
| 内存 | 32-64 Gi | 查询内存与 depot 缓存共享,越大命中率越高 |
| 数据盘 | 1 TB+ NVMe | depot 使用 NVMe 显著降低从公共存储回读的频率 |
| 网络 | 25 GbE+ | Eon 模式严重依赖公共存储带宽 |
| 公共存储 | MinIO 4 节点+ / 商业 S3 | 分布式 MinIO 生产推荐 ≥4 节点;商业 S3(AWS S3/阿里云 OSS)免运维 |
| 节点数 | worker ≥ Vertica 节点数 + 2 | 预留故障替换和滚动升级余量 |
以上为 Vertica Eon on K8s 场景建议。Enterprise 模式不依赖公共存储,数据盘需更大(数据存本地),其他相近。
2.3 添加第4台 Worker¶
MinIO 分布式模式要求最少 4 个节点才能组成纠删码集群(官方要求)。当前只有 3 台 worker,先加一台。
参照 Kubernetes kubeadm 生产部署实战 加入节点流程,创建同规格 ECS(Debian 13.6,2核4G,40GiB),分配 IP 172.20.65.78,命名为 k8s-w4:
# ① 新节点上安装 containerd + kubeadm + kubelet(参考「Kubernetes kubeadm 生产部署实战」3.1、3.2 节)
# ② 从 k8s-cp1 获取 join 命令(token 过期则重新生成)
ssh k8s-cp1 sudo kubeadm token create --print-join-command
# ③ 在新节点执行 join
sudo kubeadm join 172.20.65.73:6443 --token <token> --discovery-token-ca-cert-hash <hash>
kubectl get nodes
# NAME STATUS ROLES AGE
# k8s-w1 Ready <none> ...
# k8s-w2 Ready <none> ...
# k8s-w3 Ready <none> ...
# k8s-w4 Ready <none> 10s ← 新加入
4 台 worker 就绪后继续。
2.4 MinIO 准备(k8s-cp1)¶
MinIO 是 Vertica 官方支持的 S3 兼容对象存储。本文用 4 节点分布式模式部署在同一 K8s 集群内,数据通过纠删码分片到 4 台 worker 的本地磁盘,任意一台故障不影响服务。集群内延迟 <1ms。
分布式 MinIO 最少 4 节点,本文用 4 节点确保纠删码正常工作。
# ① 4 台 worker 上各创建数据目录
for host in k8s-w1 k8s-w2 k8s-w3 k8s-w4; do
ssh $host mkdir -p /data/minio
done
# ② cp1 上:创建 Namespace + PV + StatefulSet
kubectl create namespace minio
kubectl apply -n minio -f - <<'EOF'
# 4 个 hostPath PV(每台 worker 一个)
apiVersion: v1
kind: PersistentVolume
metadata:
name: minio-pv-0
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/minio}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w1]}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: minio-pv-1
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/minio}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w2]}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: minio-pv-2
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/minio}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w3]}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: minio-pv-3
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/minio}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w4]}
---
# Headless Service(Pod 间 DNS 发现,StatefulSet 必需)
apiVersion: v1
kind: Service
metadata:
name: minio-headless
spec:
clusterIP: None
ports:
- name: api
port: 9000
selector:
app: minio
---
# StatefulSet(4 节点分布式,Pod 反亲和自动分散到 4 台 worker)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
spec:
serviceName: minio-headless
replicas: 4
podManagementPolicy: Parallel
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- {key: app, operator: In, values: [minio]}
topologyKey: kubernetes.io/hostname
containers:
- name: minio
image: minio/minio:latest
args:
- server
- http://minio-{0...3}.minio-headless.minio.svc.cluster.local:9000/data
- --console-address
- :9001
env:
- name: MINIO_ROOT_USER
value: admin
- name: MINIO_ROOT_PASSWORD
value: admin123456
ports:
- containerPort: 9000
name: api
- containerPort: 9001
name: console
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: [ReadWriteOnce]
resources: {requests: {storage: 5Gi}}
storageClassName: ""
---
# 常规 Service(Vertica 访问入口,负载均衡到 4 个 Pod)
apiVersion: v1
kind: Service
metadata:
name: minio
spec:
ports:
- name: api
port: 9000
- name: console
port: 9001
selector:
app: minio
EOF
分布式模式关键参数解释: -
http://minio-{0...3}.minio-headless.minio.svc.cluster.local:9000/data—{0...3}是 MinIO 的端点展开语法(三个点),告诉 MinIO 集群有 4 个成员,数据通过纠删码分片存储。minio-headless是 Headless Service 名(StatefulSet 依赖它提供稳定的 Pod DNS)。 -podManagementPolicy: Parallel— 4 个 Pod 同时启动(MinIO 分布式模式要求半数以上节点同时在线才能形成 Quorum)。 -podAntiAffinity— 确保每个 Pod 在不同 worker 上,挂掉一台不影响其他。
# ③ 等待 4 个 Pod 全部 Running 后创建 Bucket
kubectl get pod -n minio -o wide
# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
# minio-0 1/1 Running 0 70s 10.244.3.4 k8s-w1 <none> <none>
# minio-1 1/1 Running 0 70s 10.244.4.6 k8s-w2 <none> <none>
# minio-2 1/1 Running 0 70s 10.244.5.4 k8s-w3 <none> <none>
# minio-3 1/1 Running 0 70s 10.244.6.4 k8s-w4 <none> <none>
kubectl wait --for=condition=Ready pod -l app=minio -n minio --timeout=120s
kubectl exec -n minio minio-0 -- sh -c '
mc alias set local http://localhost:9000 admin admin123456
mc mb local/vertica-communal
'
# ④ 创建 MinIO 凭证 Secret(VerticaDB 引用)
kubectl create namespace vertica 2>/dev/null || true
kubectl create secret generic vertica-minio-creds \
--namespace vertica \
--from-literal=accesskey=admin \
--from-literal=secretkey=admin123456
MinIO 集群内访问延迟 <1ms(对比 OSS VPC 内网约 38ms),Bootstrap 从 >5 分钟降到 30 秒以内,不会被 NMA 探针超时打断。
MinIO 的 endpoint 是如何构成的? 上面部署完 MinIO 后,Vertica 连接时填的地址是
http://minio.minio.svc.cluster.local:9000。这个地址由 K8s DNS 依据 Service 定义自动生成,不需要手动配置,公式为:
段 本文值 来源字段 service-nameminioService metadata.namenamespaceminiokubectl create namespace minio+kubectl apply -n miniosvc.cluster.local固定 K8s 默认集群域名(生产环境可能是 svc.{自定义域名})port9000Service spec.ports[].port(MinIO API 端口,非 console 9001)换到你的环境时,只需把
service-name和namespace换成你的值即可。
2.5 镜像中转(Docker Hub → 阿里云 ACR)¶
Vertica 被 OpenText 收购后,Operator 和软件镜像均在 Docker Hub opentext/ 命名空间下。Docker Hub 国内直连超时,DaoCloud 不代理该命名空间(403 Forbidden)。通过一台能访问 Docker Hub 的节点 + crane(Google 开源的镜像复制工具,单二进制文件,无需 Docker daemon)直接复制到 ACR。
① 创建 ACR 实例:阿里云控制台 → 容器镜像服务 ACR → 创建实例(个人版即可,免费)→ 创建命名空间(如 vertica)→ 访问凭证 → 设置固定密码。
② 在能访问 Docker Hub 的节点上执行(已开通公网 IP 的 ECS 或能科学上网的 Mac;Mac 用公网地址,ECS 可改用 -vpc 内网地址更快):
# 安装 crane(单文件 10MB,无需 Docker)
curl -fsSL https://github.com/google/go-containerregistry/releases/latest/download/go-containerregistry_Linux_x86_64.tar.gz \
| tar xz -C /usr/local/bin crane
chmod +x /usr/local/bin/crane
# 登录 ACR(地址在 ACR 控制台 → 实例概览 查看)
crane auth login crpi-<acr-id>.cn-wulanchabu.personal.cr.aliyuncs.com \
-u <阿里云账号> -p <ACR实例密码>
# 复制镜像——crane 直接从 Docker Hub 拉到 ACR,不占用本地磁盘
crane copy docker.io/opentext/verticadb-operator:25.4.0-0 \
crpi-<acr-id>-vpc.cn-wulanchabu.personal.cr.aliyuncs.com/vertica/verticadb-operator:25.4.0-0
crane copy docker.io/opentext/vertica-k8s:25.4.0-0 \
crpi-<acr-id>-vpc.cn-wulanchabu.personal.cr.aliyuncs.com/vertica/vertica-k8s:25.4.0-0
opentext/vertica-k8s镜像较大(~550MB),约需 3-5 分钟。如遇UNAUTHORIZED,用docker login登录 Docker Hub 后重试(部分仓库需登录才能拉取)。
2.6 安装 VerticaDB Operator(k8s-cp1)¶
# ⚠️ vertica/charts 和 vertica/vertica-kubernetes 是不同仓库,版本号不同步:
# CRD: charts v26.2.1-0 | operator.yaml: vertica-kubernetes v25.4.0-0
CRD_VER="v26.2.1-0"
OP_VER="v25.4.0-0"
# 下载 operator.yaml 到本地
curl -fsSL -o /tmp/operator.yaml \
https://github.com/vertica/vertica-kubernetes/releases/download/${OP_VER}/operator.yaml
# 替换镜像为 ACR 地址
sed -i "s|image: 'docker.io/opentext/verticadb-operator:[^']*'|image: 'crpi-<acr-id>-vpc.cn-wulanchabu.personal.cr.aliyuncs.com/vertica/verticadb-operator:25.4.0-0'|" /tmp/operator.yaml
# 安装(先 CRD 后 Operator)
kubectl apply --server-side=true --force-conflicts \
-f https://github.com/vertica/charts/releases/download/${CRD_VER}/crds.yaml
# customresourcedefinition.apiextensions.k8s.io/eventtriggers.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/vclusterservers.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/verticaautoscalers.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/verticadbs.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/verticareplicators.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/verticarestorepointsqueries.vertica.com serverside-applied
# customresourcedefinition.apiextensions.k8s.io/verticascrutinizers.vertica.com serverside-applied
kubectl get crd | grep vertica
# eventtriggers.vertica.com 2026-08-11T06:55:54Z
# vclusterservers.vertica.com 2026-08-11T06:55:54Z
# verticaautoscalers.vertica.com 2026-08-11T06:55:54Z
# verticadbs.vertica.com 2026-08-11T06:55:54Z
# verticareplicators.vertica.com 2026-08-11T06:55:54Z
# verticarestorepointsqueries.vertica.com 2026-08-11T06:55:54Z
# verticascrutinizers.vertica.com 2026-08-11T06:55:55Z
kubectl apply -f /tmp/operator.yaml
crds.yaml 是做什么的? K8s 原生只认识 Pod、Service、Deployment 等内置资源。
crds.yaml向集群注册VerticaDB这个新资源类型(CustomResourceDefinition),之后kubectl apply -f vertica-eon.yaml(kind: VerticaDB)才能被 K8s 识别。如果不先装 CRD,K8s 会直接报错:不认识kind: VerticaDB。这里用
--server-side=true而非普通的kubectl apply,是因为 CRD 的 OpenAPI schema 定义很长,标准 apply 会将其存为last-applied-configurationannotation,容易超出 annotation 的 256KB 限制。--server-side=true将配置存储在 etcd 的managedFields中,不受此限。CRD 和 Operator/镜像版本不一致会有问题吗? 不会——两者的版本号本来就不对应,因为它们来自不同仓库:
组件 仓库 职责 crds.yamlvertica/charts定义 Schema: VerticaDB有哪些字段、字段类型是什么operator.yaml+ 镜像vertica/vertica-kubernetes定义 行为:Controller 监听 VerticaDB资源并创建数据库可以理解为 CRD 是"合同模板",Operator 是"执行者"。只要合同上的字段执行者都认识,就没问题。实际操作中遵循一条原则:CRD 版本不要低于 Operator 版本。因为新版 Operator 可能引用新版 CRD 才有的字段,旧 CRD 不认识会导致 apply 报错。反之(CRD 比 Operator 新)是安全的——CRD 里多出的字段 Operator 不用就是了。
本文的组合
charts v26.2.1-0+operator v25.4.0-0是实际验证过的可用搭配,直接使用即可。如果你需要更换版本,先确认两个仓库各自的 release notes。
# 验证
kubectl get pods -n verticadb-operator -o wide
# NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
# verticadb-operator-manager-b75f7975b-npb7k 1/1 Running 0 16m 10.244.3.7 k8s-w1 <none> <none>
3. 部署 Vertica Eon 集群¶
3.1 创建 MinIO 凭证 Secret(已在 2.4 节创建,确认即可)¶
3.2 创建 dbadmin 密码 Secret¶
# 生产环境请替换为强密码
kubectl create secret generic vertica-db-password \
--namespace vertica \
--from-literal=password=Vertica123
3.3 Vertica 本地存储 PV¶
Eon 模式下 local 盘(catalog + depot)是缓存——数据源头在 MinIO,丢了也不影响数据完整性。但 VerticaDB Operator 始终为 local 创建 PVC,不提供 emptyDir 选项,需要在集群没有 StorageClass 时预建 hostPath PV。
# 在 3 台 worker 上创建数据目录
for host in k8s-w1 k8s-w2 k8s-w3; do
ssh $host 'mkdir -p /data/vertica-local && chown 5000:5000 /data/vertica-local'
done
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
name: vertica-local-pv-0
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/vertica-local}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w1]}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: vertica-local-pv-1
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/vertica-local}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w2]}
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: vertica-local-pv-2
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/vertica-local}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w3]}
EOF
hostPath PV 的局限:节点故障后 PVC 绑死在坏节点上,需手动删 PVC 让 Pod 漂移到备用节点(6.2 节详述)。生产环境推荐云盘 CSI(ESSD/EBS),PV 与节点解耦,故障自动漂移。
| 方案 | 故障漂移 | 配置复杂度 | 适用 |
|---|---|---|---|
| hostPath PV | ❌ 手动 | 中 | 学习/测试 |
| 云盘 CSI | ✅ 自动 | 需 StorageClass | 生产 |
3.4 编写 VerticaDB CR¶
cat > /tmp/vertica-eon.yaml <<'EOF'
apiVersion: vertica.com/v1
kind: VerticaDB
metadata:
name: vertica-eon
namespace: vertica
spec:
# 镜像:2.5 节已推送到 ACR
image: crpi-<acr-id>-vpc.cn-wulanchabu.personal.cr.aliyuncs.com/vertica/vertica-k8s:25.4.0-0
imagePullPolicy: IfNotPresent
initPolicy: Create
# === 公共存储(MinIO)===
communal:
path: "s3://vertica-communal/vertica"
endpoint: "http://minio.minio.svc.cluster.local:9000"
credentialSecret: vertica-minio-creds
local:
requestSize: "5Gi"
passwordSecret: vertica-db-password
subclusters:
- name: primary
type: primary
size: 3
serviceType: ClusterIP
resources:
requests:
cpu: 200m
memory: 1Gi
limits:
cpu: "1"
memory: 2Gi
EOF
⚠️ v1 API 与 v1beta1 有多处字段变化:
kSafety不再需要(由子集群 size 隐式决定)、volumes/volumeMounts改为depotSize、includeUIDInPath和subclusterTerminationPolicy已移除。Pod 反亲和在 v1 中由 Operator 自动管理。
关键字段说明:
| 字段 | 值 | 说明 |
|---|---|---|
communal.path |
s3://vertica-communal/vertica |
MinIO Bucket 的 S3 路径 |
communal.endpoint |
http://minio.minio.svc.cluster.local:9000 |
MinIO 集群内 Service,延迟 <1ms |
communal.credentialSecret |
vertica-minio-creds |
2.4 节创建的 Secret(admin/admin123456) |
local.requestSize |
5Gi |
catalog 目录大小(测试环境 <200MB,5Gi 足够) |
subclusters[0].size |
3 |
3 节点 = K-safety=1 的最低要求 |
3.5 部署¶
kubectl apply -f /tmp/vertica-eon.yaml
# 观察 Pod 创建过程(约 3-5 分钟首次启动)
kubectl get pods -n vertica -w
# vertica-eon-primary-0 0/1 Pending 0 0s
# vertica-eon-primary-0 0/1 ContainerCreating 0 5s
# vertica-eon-primary-0 1/1 Running 0 90s
# vertica-eon-primary-1 0/1 Pending 0 0s
# ...
3.6 验证集群就绪¶
# ① 看 VerticaDB 状态
kubectl get vdb -n vertica
# NAME SUBCLUSTERS VERSION READY AGE
# vertica-eon 1 v25.4.0-0 3/3 5m22s
# ② 进入 Pod 看节点状态(登进来是 root,server 进程以 dbadmin 运行,vsql 等工具不限制身份)
kubectl exec -it vertica-eon-primary-0 -n vertica -c server -- bash
bash-5.1$ vsql -Udbadmin -wVertica123 -c "SELECT node_name, node_state FROM nodes;"
# node_name | node_state
# -------------------+------------
# v_vertdb_node0001 | UP
# v_vertdb_node0002 | UP
# v_vertdb_node0003 | UP
bash-5.1$ vsql -Udbadmin -wVertica123 -c "SELECT version();"
# Vertica Analytic Database v25.4.0-0
bash-5.1$ exit
如果节点显示
DOWN,等 1-2 分钟再试——节点首次从 MinIO 加载 catalog 需要时间。
3.7 创建测试数据¶
kubectl exec -it vertica-eon-primary-0 -n vertica -c server -- vsql -Udbadmin -wVertica123 <<'SQL'
CREATE TABLE test_eon (id INT, val VARCHAR(100));
INSERT INTO test_eon SELECT n, 'row_' || n FROM (SELECT ROW_NUMBER() OVER () AS n FROM columns) t LIMIT 100;
SELECT COUNT(*) FROM test_eon;
SQL
4. 存储架构¶
4.1 三层存储¶
flowchart TB
subgraph S1["MinIO(Communal Storage)|主数据持久化"]
M1["所有数据在 MinIO 上持久化"]
M2["各 Pod 共享同一 Bucket"]
M3["S3 兼容协议,内网访问免流量费"]
end
subgraph S2["emptyDir — depot(本地缓存)|读写缓存"]
D1["从 MinIO 缓存热点数据"]
D2["Pod 删除后丢失(可从 MinIO 重建)"]
end
subgraph S3["local PVC — catalog(本地目录)|元数据"]
C1["数据库元数据、schema 定义"]
C2["Vertica Operator 自动管理 PVC"]
end
S1 ~~~ S2
S2 ~~~ S3
4.2 MinIO 配置要点¶
| 配置 | 推荐值 | 原因 |
|---|---|---|
| endpoint | http://minio.minio.svc.cluster.local:9000 |
集群内 Service,延迟 <1ms |
| 协议 | HTTP(同集群) | 集群内无需 TLS,MinIO 默认 HTTP 更快 |
| Bucket 权限 | 私有 + accesskey/secretkey | 2.4 节创建的凭证(admin / admin123456) |
公共存储的替代方案:Ceph RGW。 生产私有云也可以用 Ceph 的对象网关(RGW,S3 兼容)替代 MinIO 做公共存储——v26.2 官方文档 Supported Platforms 中有专门的 CEPH 章节(Red Hat Ceph Storage 渠道认证)。好处是与 Ceph RBD 块存储共用一套集群(见 4.3 一体化方案);代价是 RGW 的 bucket index 对海量小 IO 敏感,性能调优面比 MinIO 大,上线前必须做性能验收。公共存储是 Eon 的命根子,更换供应商前先确认 Vertica 支持范围与实际 IO 压测结果。
4.3 本地存储选型¶
| 方案 | 故障漂移 | 配置复杂度 | 适用场景 |
|---|---|---|---|
| hostPath PV | ❌ 手动删 PVC 重绑(6.2 节) | 低 | 学习/测试(本文方案;depot 还可直接用 emptyDir,丢了不影响) |
| 云盘 CSI(ESSD/EBS) | ✅ 自动 | 中(需 StorageClass) | 生产·公有云 |
| Ceph RBD + RGW 一体化 | ✅ 自动 | 高(需 Ceph 集群) | 生产·私有云(已有 Ceph 运维体系) |
Ceph 一体化方案(私有云生产推荐之一):Ceph RBD 做
local盘(块存储)+ Ceph RGW 做公共存储(S3 兼容,见 4.2)——一套集群通吃,运维面统一。RBD 是网络块存储,PV 与节点解耦:节点故障后 Pod 漂移到备用节点,CSI 自动 detach/attach 同一个卷,不需要 6.2 节的手动删 PVC 操作,且 catalog 缓存不丢、恢复更快。注意两点:
- RBD 是 RWO(同一时刻单节点挂载,可跨节点移动),正符合 local 盘需求;极端情况下卷卡住的手工处理发生在 Ceph 层面(rbd unmap/强制 detach),不是删 PVC
- depot 是本地缓存,放 RBD 后读写走网络,与 RGW 公共存储共享带宽,生产需按 2.2 节配置做性能评估;学习环境(2核4G)扛不动 Ceph,故本文用 hostPath
架构权衡:全 Ceph 一体化后,公共存储与块存储共享同一故障域——Ceph 集群级故障会让两者同时不可用。若无成熟 Ceph 运维团队,更稳妥的组合是「MinIO + 云盘 CSI」(故障域隔离)。
5. 网络设计¶
在 VPC 内网 + Flannel VXLAN 环境下,Vertica 网络配置已足够简洁:
- Pod 间通信:Flannel 提供跨节点 Pod 互通,Vertica 节点间通过 Pod IP 和 Service FQDN 通信
- Vertica 端口:5433(客户端)、5434(节点间)、5444(Spread)
Operator 默认创建 ClusterIP Service,仅集群内可访问。要外部连接,需要额外创建一个固定端口的 NodePort Service,与 Operator 管理的 Service 分离:
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Service
metadata:
name: vertica-vip
namespace: vertica
spec:
type: NodePort
ports:
- name: vsql
port: 5433
nodePort: 31421
selector:
app.kubernetes.io/instance: vertica-eon
vertica.com/subcluster-svc: primary
EOF
💡 为什么单独建 Service? 如果通过修改 CR 的
serviceType切换 NodePort,端口号由 K8s 随机分配且可能被 Operator 覆盖。独立 Service 端口固定31421,重建多少次都不变。⚠️ selector 字段容易写错:Pod 上 Operator 注入的标签是
vertica.com/subcluster-svc(带-svc后缀),不是vertica.com/subcluster。写错会导致 endpoints 为空、端口不监听。不知道用哪个标签时,两种方式确认:
# 方法一:直接看 Pod(什么标签、什么值一目了然) kubectl get pod -n vertica -l app.kubernetes.io/instance=vertica-eon --show-labels # 方法二:看 Operator 自建 Service 的 selector(它配的一定对) kubectl get svc vertica-eon-primary -n vertica -o jsonpath='{.spec.selector}' # {"app.kubernetes.io/instance":"vertica-eon","vertica.com/client-routing":"true","vertica.com/subcluster-svc":"primary"}
kubectl get svc -n vertica
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# vertica-eon ClusterIP None <none> 5434/TCP,4803/TCP,8443/TCP,5554/TCP 80m
# vertica-eon-primary ClusterIP 10.0.0.170 <none> 5433/TCP,8443/TCP 80m
# vertica-vip NodePort 10.0.0.169 <none> 5433:31421/TCP 3m38s ← 新加
从集群外任意机器连接:
生产推荐:SLB VIP 统一入口。 记多个 worker IP 不够稳定(节点可能更换),通过 SLB(阿里云负载均衡)挂一个 VIP 统一访问:
- 阿里云 SLB 控制台 → 监听管理 → 添加 TCP 监听器,端口
5433 - 后端服务器选 k8s-w1/w2/w3/w4,端口填上面分配的 NodePort(如
31421) - 健康检查选 TCP,端口
5433
之后只需记一个 VIP:
SLB 自动剔除不健康的 worker,相比直连 NodePort 多了故障自动切换能力。
6. 高可用设计¶
6.1 K-safety 与 Pod 反亲和¶
v1 API 中 K-safety 由子集群 size 隐式决定(primary ≥ 3 = K-safety=1),Pod 反亲和由 Operator 自动管理,CR 中无需手动配置。本文 CR 中 size: 3 确保 3 个 Pod 分布在 3 台 worker 上——如果 worker 数量不足,Pod 将无法调度。
6.2 节点故障恢复¶
节点宕机后的 K8s 调度链:~40s 标记 NotReady → ~5m40s Pod 驱逐 → 调度器尝试重建。hostPath PV 的 PVC 绑定在坏节点上会阻止自动恢复:
flowchart LR
A["Pod 被驱逐"] --> B["StatefulSet 重建 Pod"] --> C["Pod 尝试挂载同名 PVC"] --> D["PVC 仍绑定坏节点的 PV<br/>(nodeAffinity 指向宕机的节点)"] --> E["调度失败 ❌"]
需要手动删 PVC 释放旧绑定,让新 PVC 绑到备用节点的 PV(k8s-w4 的 vertica-local-pv-3):
kubectl delete pvc local-data-vertica-eon-primary-<N> -n vertica
# 有时还需清理 PV 的 claimRef
kubectl patch pv vertica-local-pv-<N> --type=json \
-p '[{"op":"remove","path":"/spec/claimRef"}]'
Eon 模式的核心保障:不管哪种方案,数据不会丢——所有数据都在 MinIO 公共存储上。local 盘只是缓存,重建不改变数据完整性。
💡 什么时候不需要本节手动操作? 若
local盘用的是网络块存储(云盘 CSI 的 ESSD/EBS,或 Ceph RBD),PV 与节点解耦——Pod 漂移到新节点后 CSI 自动把同一个卷 detach/attach 过去,PVC 全程不用动,且 catalog 缓存保留、恢复更快。手动删 PVC 只是 hostPath 本地盘的特有操作(见 4.3 方案对比)。
6.3 PodDisruptionBudget¶
PDB 是什么? 当 K8s 节点需要维护(升级、缩容)时,kube-controller 会驱逐节点上的 Pod。如果没有 PDB,K8s 可能一次性驱逐多个 Vertica Pod,导致 K-safety=1 的集群丢 Quorum 不可用。PDB 的作用就是给 K8s 一个约束:「这批 Pod 最少要保持几个存活,驱逐时逐个来」。
本例中 minAvailable: 2 表示:3 节点集群在任何时刻最多只允许 1 个 Pod 被主动驱逐,始终保持至少 2 个节点在线。
注意:PDB 只约束主动驱逐(节点维护、
kubectl drain等),不约束节点宕机这类被动故障。后者由 6.2 节的自动调度恢复处理。
kubectl create -f - <<EOF
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: vertica-eon-pdb
namespace: vertica
spec:
minAvailable: 2
selector:
matchLabels:
app.kubernetes.io/instance: vertica-eon
EOF
7. Day 2 运维¶
为什么叫 Day 2? 这是运维领域的术语,把软件生命周期分为三个阶段:
阶段 含义 本文对应 Day 0 规划选型 — Day 1 部署上线 第 2-6 节(把系统跑起来) Day 2 持续运维 第 7 节(扩缩容、升级、备份、License) 部署是一次性的,运维占系统生命周期的 95% 以上。Day 2 才是真正考验系统成熟度的阶段。
7.1 安装 License¶
⚠️ 26.1 版本重要变更:从 26.1 开始,社区版(CE)不再提供, 25.4 及之前的版本为没有30天时间限制的 CE License。CE 数据库升级到 26.1+ 会直接锁定,必须迁移到商业 License。新安装的评估用户为 30 天试用 License(官方文档)。
试用 License 的限制:
- 最多 3 节点、1TB 原始数据
- 有效期 30 天(从安装之日算起)
- 仅限全新安装,不能用于已有 CE 数据库
- 过期后数据库锁定,直到安装商业 License
获取方式:访问 OpenText Analytics Database Trial 注册账号,按邮件指引激活。
企业版需要商业 License 才能使用全部功能(无节点数/容量限制/不绑定机器)。以下步骤对试用 License 和企业 License 均适用——都是通过 Secret + CR 引用完成。
① 创建 License Secret
# 将 Vertica 提供的 license 文件(如 vertica_license.xml)创建为 Secret
kubectl create secret generic vertica-license \
--namespace vertica \
--from-file=license.dat=./vertica_license.xml
文件名必须为
license.dat——这是 VerticaDB Operator 读取 Secret 时的约定 key 名。
② 在 VerticaDB CR 中引用
③ 生效方式
⚠️
licenseSecret仅在initPolicy: Create(首次创建数据库)时由 Operator 自动安装。数据库已运行的情况下,patch CR 只是注册 Secret,不会自动触发安装,需要手动执行。
# 将 license 文件拷入 Pod 并手动安装
kubectl cp vertica_license.xml vertica/vertica-eon-primary-0:/tmp/vertica_license.xml -c server
kubectl exec -it vertica-eon-primary-0 -n vertica -c server -- \
vsql -Udbadmin -wVertica123 -c "select install_license('/tmp/vertica_license.xml');"
同时在 CR 中注册(后续新建子集群或重建时自动使用):
kubectl patch vdb vertica-eon -n vertica \
--type=merge \
--patch='{"spec":{"licenseSecret":"vertica-license"}}'
验证:
kubectl exec -it vertica-eon-primary-0 -n vertica -c server -- \
vsql -Udbadmin -wVertica123 -c "select display_license();"
注意:试用 License 过期前务必安装商业 License,否则数据库会锁定。商业 License 安装后不可降回试用/CE 版。升级前确认 License 有效期覆盖整个运维窗口。
7.2 添加 Worker 节点¶
k8s-w4 已在 2.3 节添加完毕(用于 MinIO 分布式集群)。当前 4 台 worker 中 3 台跑 Vertica,1 台空闲——下面 7.3 节可直接测试扩容到 4 节点。如需更多 worker(例如扩容到 5+ 节点或替换故障节点),参照 2.3 节流程重复操作即可。
7.3 扩缩容¶
⚠️ 扩容前确保目标节点有 Vertica 本地存储 PV。 每增加一个 Pod,Operator 自动创建一个 PVC(绑定 3.3 节创建的 PV)。
# 在新增 worker 上创建 `/data/vertica-local` + PV
ssh k8s-w4 'mkdir -p /data/vertica-local && chown 5000:5000 /data/vertica-local'
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolume
metadata:
name: vertica-local-pv-3
spec:
capacity: {storage: 5Gi}
accessModes: [ReadWriteOnce]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
hostPath: {path: /data/vertica-local}
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- {key: kubernetes.io/hostname, operator: In, values: [k8s-w4]}
EOF
# 扩容(社区版限制 3 节点,企业版可超过。本例仅示意 API 用法)
kubectl patch vdb vertica-eon -n vertica \
--type=merge \
--patch='{"spec":{"subclusters":[{"name":"primary","size":4}]}}'
kubectl get pod -n vertica
# NAME READY STATUS RESTARTS AGE
# vertica-eon-primary-0 2/2 Running 0 71m
# vertica-eon-primary-1 2/2 Running 1 (70m ago) 71m
# vertica-eon-primary-2 2/2 Running 1 (70m ago) 72m
# vertica-eon-primary-3 2/2 Running 0 47s
# 添加只读子集群(生产读写分离推荐)
kubectl patch vdb vertica-eon -n vertica \
--type=merge \
--patch='{"spec":{"subclusters":[{"name":"analytics","type":"secondary","size":2}]}}'
缩容同理,改小 size 即可。Operator 会先驱逐目标 Pod、从 Vertica 集群中移除节点、再删 Pod:
# 缩回 3 节点
kubectl patch vdb vertica-eon -n vertica \
--type=merge \
--patch='{"spec":{"subclusters":[{"name":"primary","size":3}]}}'
kubectl get pod -n vertica -w
# vertica-eon-primary-3 Terminating → 消失
Eon 模式下缩容不丢数据——数据在 MinIO 上,被移除的 Pod 只是释放了 depot 缓存和本地 catalog。
7.4 升级 Vertica 版本¶
# 升级 Operator:重新 apply 新版 YAML(kubectl 方式,同 2.6 节)
kubectl apply \
-f https://github.com/vertica/vertica-kubernetes/releases/download/v<新版本>/operator.yaml
# 升级 Vertica 软件版本(滚动升级,逐个节点替换)
kubectl patch vdb vertica-eon -n vertica \
--type=merge --patch='{"spec":{"image":"crpi-<acr-id>-vpc.cn-wulanchabu.personal.cr.aliyuncs.com/vertica/vertica-k8s:<新版本>"}}'
7.5 备份到 MinIO¶
opentext/vertica-k8s镜像做了精简,vbr工具不在其中。Eon 模式下数据本身就持久化在 MinIO 上,备份策略分两层:
① DDL 导出(表结构/权限/序列等元数据):
kubectl exec -it vertica-eon-primary-0 -n vertica -c server -- \
vsql -U dbadmin -w Vertica123 -c "SELECT EXPORT_OBJECTS('', '');"
② 数据层备份(直接复制 MinIO Bucket):
kubectl exec -n minio minio-0 -- sh -c "
mc alias set local http://localhost:9000 admin admin123456
BUCKET=vertica-backup-\$(date +%Y%m%d)
mc mb local/\$BUCKET
mc mirror local/vertica-communal local/\$BUCKET
"
Eon 模式的恢复只需在新集群的
communal.path指向备份 Bucket 复活集群即可。
8. 与 kind 教程的对比¶
| 维度 | Vertica on Kubernetes 部署指南(kind) | 本文(阿里云 Eon 实战) |
|---|---|---|
| K8s 环境 | kind 单机容器 | 7 节点 kubeadm 生产集群 |
| 对象存储 | Eon 模式(hostPath 本地目录) | MinIO(Eon 模式) |
| 存储 | hostPath | hostPath + MinIO(可替换 ESSD 云盘) |
| 部署方式 | CR(kubectl apply) |
kubectl apply + CR |
| 网络 | NodePort(kind 端口映射) | Flannel VXLAN + ClusterIP |
| 高可用 | 无 | K-safety + Pod 反亲和 + PDB |
| 数据持久化 | 节点本地 | MinIO 公共存储,Pod 可任意重建 |
9. 总结¶
本文在 Kubernetes kubeadm 生产部署实战 搭建的 7 节点集群上,完成了 Vertica Eon on K8s 的完整部署流程:
- MinIO 准备 → 部署 MinIO + 创建 Bucket + 凭证
- K8s 资源 → Secret(凭证/密码) + VerticaDB CR
- 部署验证 →
kubectl apply+ vsql - 运维操作 → 扩缩容、升级、备份
关键差异在于:Eon 模式的数据持久化完全依赖 MinIO 公共存储,K8s 节点上的 depot 只是缓存——这使 Pod 迁移和弹性扩缩成为可能,是「数据库 on K8s」的正确打开方式。
延伸阅读:Kubernetes kubeadm 生产部署实战(集群搭建)、Vertica on Kubernetes 部署指南(kind 入门)、Kubernetes 存储体系(PV/PVC/CSI)。
扩展阅读¶
- Kubernetes kubeadm 生产部署实战 — 本文的 7 节点集群搭建全流程
- Vertica on Kubernetes 部署指南 — kind 单机入门教程(与本文对比见第 8 节)
- Kubernetes 存储体系 — PV/PVC/CSI 基础
- Vertica 与 AliCloud - 1 - 使用阿里云对象存储OSS部署Eon数据库 — 阿里云 OSS 作为公共存储的 Eon 部署(姊妹篇)