在 Kubernetes 上为 ClickHouse Operator 部署 ZooKeeper:从快速启动到高级配置实战指南
2026/9/18 8:23:56 网站建设 项目流程

在 Kubernetes 上为 ClickHouse Operator 部署 ZooKeeper:从快速启动到高级配置实战指南

【免费下载链接】clickhouse-operatorAltinity Kubernetes Operator for ClickHouse creates, configures and manages ClickHouse® clusters running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/cl/clickhouse-operator

本文是 clickhouse-operator 仓库中 ZooKeeper 环境搭建的完整实战指南,覆盖从"一键快速启动"(Quick Start)到"逐项精细配置"(Advanced Setup)两条部署路径,并深入讲解 Service、Headless Service、PodDisruptionBudget、StorageClass、StatefulSet 等关键资源的配置要点与 ZooKeeper 集群的验证方法。读完本文,你将能够在 Kubernetes 上独立完成单节点或三节点 ZooKeeper 集群的部署、存储方案选型与运行状态检查,为 ClickHouse 复制表等高可用特性提供可靠的协调服务底座。

一、为什么 ClickHouse 集群需要 ZooKeeper

ClickHouse 的复制表(Replicated Table)依赖 ZooKeeper 来协调副本间的元数据、日志序号与 leader 选举。在 docs/replication_setup.md 与docs/chi-examples/目录下的04-replication-zookeeper-*.yaml系列示例中,均可以看到 ZooKeeper 作为复制协调服务被显式引用。因此,在生产环境为 ClickHouse Operator 部署一套高可用的 ZooKeeper 是前置条件之一。

本文档对应的部署脚本与清单全部位于 deploy/zookeeper/zookeeper-manually 目录,采用手工(manually)方式部署。仓库同时提供 zookeeper-with-zookeeper-operator 的 Operator 方式部署目录,可自行对比选用。

本文描述的 ZooKeeper 安装会创建/配置以下 Kubernetes 资源:

资源作用
Service为客户端访问 ZooKeeper 提供统一入口
Headless Service为每个 ZooKeeper 节点提供独立 DNS 名称
PodDisruptionBudget(中断预算)限制任意时刻可离线的 ZooKeeper Pod 数量
StorageClass(可选)指定 ZooKeeper 数据存储使用的存储类
StatefulSet管理并伸缩 ZooKeeper Pod 集合

ZooKeeper 安装分为两种方式:

  1. Quick start(快速启动):直接运行,不做过多询问,适合测试。
  2. Advanced setup(高级设置):精细配置存储类、副本数等内部细节,适合生产。

二、快速启动(Quick Start)

快速启动提供两种存储风格:

  1. 持久化卷(Persistent Volume):适合 AWS 等云环境,文件位于 deploy/zookeeper/zookeeper-manually/quick-start-persistent-volume。
  2. 本地 emptyDir 存储:适合单机本地运行,但没有真正的持久化能力(Pod 重建即数据丢失),文件位于 deploy/zookeeper/zookeeper-manually/quick-start-volume-emptyDir。

每种风格都提供两种集群规模:

  • 1 节点 ZooKeeper 集群zookeeper-1-前缀文件):不提供故障切换(failover)能力
  • 3 节点 ZooKeeper 集群zookeeper-3-前缀文件):提供故障切换能力,生产推荐。

选型建议:

  • 需要在 AWS 或任何其他云厂商测试:推荐使用 quick-start-persistent-volume 的持久化存储。
  • 本地测试:可以使用 quick-start-volume-emptyDir 的emptyDir

2.1 脚本方式安装

以 AWS 上 1 节点 ZooKeeper 集群为例,使用 quick-start-persistent-volume 目录下的脚本。仓库同时提供创建和删除两个脚本:

  • 创建:zookeeper-1-node-create.sh
  • 删除:zookeeper-1-node-delete.sh

从脚本实现可以看到命名空间的可定制机制(默认zoo1ns):

# zookeeper-1-node-create.sh ZK_NAMESPACE="${ZK_NAMESPACE:-zoo1ns}" kubectl create namespace "${ZK_NAMESPACE}" kubectl --namespace="${ZK_NAMESPACE}" apply -f "${CUR_DIR}/zookeeper-1-node.yaml"

即默认会创建名为zoo1ns的命名空间并应用zookeeper-1-node.yaml。若需自定义命名空间,可在执行前设置环境变量:

ZK_NAMESPACE=myzkns ./zookeeper-1-node-create.sh

删除脚本则直接删除整个命名空间:

# zookeeper-1-node-delete.sh kubectl delete namespace "${ZK_NAMESPACE}"

3 节点集群对应使用 zookeeper-3-nodes-create.sh 与 zookeeper-3-nodes-delete.sh,emptyDir风格目录下也有对应脚本。

2.2 手动方式安装

如果希望手动部署 ZooKeeper,按以下步骤执行。

创建命名空间
kubectl create namespace zoo1ns
部署 ZooKeeper
kubectl apply -f zookeeper-1-node.yaml -n zoo1ns

其中zookeeper-1-node.yaml位于 deploy/zookeeper/zookeeper-manually/quick-start-persistent-volume/zookeeper-1-node.yaml。该清单文件是一个多文档 YAML,一次性包含了 Service、Headless Service、PodDisruptionBudget 与 StatefulSet 四类资源(这也是快速启动"一条命令全部搞定"的体现)。

现在 ZooKeeper 应该已经启动运行,可以前往本文"验证 ZooKeeper 集群"一节查看如何确认集群状态。

IMPORTANT:快速启动的 ZooKeeper 安装主要面向测试目的。如需精细化调优的 ZooKeeper 部署,请参考下一节"高级设置"。

三、高级设置(Advanced Setup)

高级设置的清单文件位于 deploy/zookeeper/zookeeper-manually/advanced 目录,所有资源被拆分到不同的独立文件中,便于单独修改和按需配置。

高级设置同样提供两种存储选项:

  1. 持久化卷(Persistent Volume)
  2. emptyDir 卷(Volume)

每种选项都提供创建与删除脚本:

  • 持久化卷:zookeeper-persistent-volume-create.sh 与 zookeeper-persistent-volume-delete.sh
  • emptyDir 卷:zookeeper-volume-emptyDir-create.sh 与 zookeeper-volume-emptyDir-delete.sh

从创建脚本可以清楚看到资源的应用顺序(以持久化卷为例):

# zookeeper-persistent-volume-create.sh ZK_NAMESPACE="${ZK_NAMESPACE:-zoons}" YAML_FILES_LIST="\ 01-service-client-access.yaml \ 02-headless-service.yaml \ 03-pod-disruption-budget.yaml \ 04-storageclass-zookeeper.yaml \ 05-stateful-set-persistent-volume.yaml\ " source "${CUR_DIR}/zookeeper-create-universal.sh"

即默认命名空间为zoons,按编号顺序依次应用 5 个清单文件(通用逻辑封装在 zookeeper-create-universal.sh 中)。以下为逐步手动执行的详细说明。

3.1 创建命名空间

kubectl create namespace zoons

后续所有资源都会创建在该命名空间内。

3.2 Zookeeper Service(客户端访问服务)

kubectl apply -f 01-service-client-access.yaml -n zoons

预期输出:

service/zookeeper created

该 Service 为客户端访问所有 ZooKeeper 节点提供统一的 DNS 名称(形如zookeeper.zoons)。清单位于 01-service-client-access.yaml,核心配置如下:

apiVersion: v1 kind: Service metadata: # DNS would be like zookeeper.zoons name: zookeeper labels: app: zookeeper spec: ports: - port: 2181 name: client - port: 7000 name: prometheus selector: app: zookeeper what: node

其中 2181 是 ZooKeeper 客户端端口,7000 是 Prometheus 指标端口,selector 与 StatefulSet 的 Pod 标签app: zookeeper, what: node相对应。

3.3 Zookeeper Headless Service(无头服务)

kubectl apply -f 02-headless-service.yaml -n zoons

预期输出:

service/zookeeper-nodes created

无头服务(Headless Service,clusterIP: None)为每个 ZooKeeper 节点提供独立的 DNS 名称,是 StatefulSet 稳定网络标识的基础。清单位于 02-headless-service.yaml:

apiVersion: v1 kind: Service metadata: # DNS would be like zookeeper-0.zookeepers.etc name: zookeepers labels: app: zookeeper spec: ports: - port: 2888 name: server - port: 3888 name: leader-election clusterIP: None selector: app: zookeeper what: node

其中 2888 是 ZooKeeper 节点间数据同步端口,3888 是 leader 选举端口,均用于节点间通信而非客户端访问。

3.4 PodDisruptionBudget(中断预算)

kubectl apply -f 03-pod-disruption-budget.yaml -n zoons

预期输出:

poddisruptionbudget.policy/zookeeper-pod-distribution-budget created

中断预算向 k8s 声明任意时刻最多允许多少个 ZooKeeper 节点离线,用于在节点维护等自愿中断场景下保护 ZooKeeper 的法定人数(quorum)。清单位于 03-pod-disruption-budget.yaml:

apiVersion: policy/v1 kind: PodDisruptionBudget metadata: name: zookeeper-pod-distribution-budget spec: selector: matchLabels: app: zookeeper maxUnavailable: 1

3.5 StorageClass(存储类)

这部分并非直接照抄即可,可能需要与 k8s 实例管理员沟通确认

首先需要决定 ZooKeeper 使用哪种存储:

  • Persistent Volume(持久化卷):走05-stateful-set-persistent-volume.yaml
  • emptyDir 卷:走05-stateful-set-volume-emptyDir.yaml

简单来说,StorageClass 用于将 Persistent Volume 绑定在一起——这些 PV 要么由 k8s 管理员手工创建,要么由 Provisioner(动态供应)自动创建。无论哪种方式,PV 都是由 k8s 在外部提供给待部署应用的。因此应用必须在自己的持久化卷声明(Persistent Volume Claim)中向 k8s 请求一个具体的StorageClass 名称,该名称需要向 k8s 管理员索取,并填写在 StatefulSet 配置的.spec.volumeClaimTemplates.storageClassName参数中。对应清单文件为:

  • emptyDir 版:05-stateful-set-volume-emptyDir.yaml
  • 持久化卷版:05-stateful-set-persistent-volume.yaml

仓库的 04-storageclass-zookeeper.yaml 提供了一个默认全部注释掉的 StorageClass 模板,用于在集群没有默认存储类时启用:

#apiVersion: storage.k8s.io/v1 #kind: StorageClass #metadata: # name: storageclass-zookeeper #provisioner: kubernetes.io/no-provisioner ## Choose desired 'volumeBindingMode' ##volumeBindingMode: WaitForFirstConsumer #volumeBindingMode: Immediate

注意其 provisioner 为kubernetes.io/no-provisioner,即该模板面向"管理员预先准备 PV"的静态供应场景;如需动态供应,应替换为集群实际的 provisioner,并选择合适的volumeBindingModeWaitForFirstConsumerImmediate)。

3.6 StatefulSet(有状态应用集)

根据存储偏好编辑对应清单:

  • 05-stateful-set-volume-emptyDir.yaml
  • 05-stateful-set-persistent-volume.yaml

选择 emptyDir 存储时:确保.spec.template.spec.containers.volumes生效,形如:

volumes: - name: datadir-volume emptyDir: medium: "" #accepted values: empty str (means node's default medium) or Memory sizeLimit: 1Gi

同时确保.spec.volumeClaimTemplates处于注释状态

选择 Persistent Volume 存储时:确保.spec.template.spec.containers.volumes处于注释状态,并取消.spec.volumeClaimTemplates的注释:

volumeClaimTemplates: - metadata: name: datadir-volume spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi ## storageClassName has to be coordinated with k8s admin and has to be created as a `kind: StorageClass` resource storageClassName: storageclass-zookeeper

并确保storageClassName(本例为storageclass-zookeeper)已按"存储类"一节正确填写。关于storageClassName字段还有两个值得注意的语义(清单文件注释中已明确):

  • 不指定storageClassName:表示使用默认存储类;
  • 指定为空字符串"":表示不使用动态供应(do not use dynamic provisioning)。
StatefulSet 内部结构速览

持久化卷版与 emptyDir 版两份 StatefulSet 清单(05-stateful-set-persistent-volume.yaml)除存储部分外主体一致,关键设计如下,可帮助理解其工作原理:

apiVersion: apps/v1 kind: StatefulSet metadata: # nodes would be named as zookeeper-0, zookeeper-1, zookeeper-2 name: zookeeper spec: selector: matchLabels: app: zookeeper serviceName: zookeepers replicas: 3 updateStrategy: type: RollingUpdate podManagementPolicy: Parallel
  • serviceName: zookeepers绑定到前面创建的无头服务,Pod 因此获得zookeeper-0.zookeepers.zoons这类稳定 DNS;
  • replicas: 3对应三节点 ZooKeeper 集群;
  • podManagementPolicy: Parallel允许并行创建/更新所有 Pod(区别于快速启动版使用的OrderedReady,快速启动 zookeeper-1-node.yaml 中为OrderedReady,即按顺序逐个就绪)。

容器启动命令会动态生成 ZooKeeper 配置/conf/zoo.cfg,包括clientPort=2181tickTime=2000initLimit=300syncLimit=10maxClientCnxns=2000maxSessionTimeout=60000000autopurge.snapRetainCount=10autopurge.purgeInterval=14lw.commands.whitelist=*等参数,并通过解析 Pod 名称序号(zookeeper-0→ myid=1)写入myid文件、追加server.N=host:port:port配置,最终以zkServer.sh start-foreground前台方式启动。同时启用 Prometheus 指标(7000 端口)并通过 Pod 注解prometheus.io/scrape: 'true'prometheus.io/port: '7000'暴露抓取端点。此外还配置了:

  • 探针:readiness 与 liveness 均通过向127.0.0.1:2181发送ruok并检查返回imok来实现,用于判断 ZooKeeper 是否存活并就绪;
  • 安全上下文runAsUser: 1000fsGroup: 1000,以非特权用户运行;
  • 反亲和podAntiAffinity基于kubernetes.io/hostname强制要求不同 ZooKeeper Pod 调度到不同节点,避免单点故障;
  • 数据目录volumeMountsdatadir-volume挂载到/var/lib/zookeeper
应用 StatefulSet

清单准备就绪后,用kubectl应用:

kubectl apply -f 05-stateful-set.yaml -n zoons

预期输出:

statefulset.apps/zookeeper-node created

(注:实际清单文件名为05-stateful-set-persistent-volume.yaml05-stateful-set-volume-emptyDir.yaml,按所选存储方案替换上面命令中的文件名即可。)

现在可以查看部署在 k8s 中的 ZooKeeper 集群了。

四、验证 ZooKeeper 集群(Explore ZooKeeper Cluster)

4.1 DNS 名称

以高级设置的三节点集群为例,在zoons命名空间内,预期有 3 个 Pod,名称分别为:

zookeeper-0 zookeeper-1 zookeeper-2

这些 Pod 拥有如下短 DNS 名称:

zookeeper-0.zookeepers.zoons zookeeper-1.zookeepers.zoons zookeeper-2.zookeepers.zoons

其中zookeepers是 ZooKeeper 无头服务(Headless Service)的名称,zoons是 ZooKeeper 命名空间的名称。

完整 DNS 名称(FQDN)为:

zookeeper-0.zookeepers.zoons.svc.cluster.local zookeeper-1.zookeepers.zoons.svc.cluster.local zookeeper-2.zookeepers.zoons.svc.cluster.local

这些稳定的网络标识正是 ZooKeeper 集群节点间互相发现、以及 ClickHouse 复制表协调时所依赖的地址基础。若 ClickHouse 需要接入该 ZooKeeper,可在 CHI 的spec.configuration.clusters中通过zookeeper配置段引用这些地址(参见 04-replication-zookeeper-01-minimal.yaml 等复制示例)。

4.2 资源检查

查看 Pod
kubectl get pod -n zoons

预期输出类似:

NAME READY STATUS RESTARTS AGE zookeeper-0 1/1 Running 0 9m2s zookeeper-1 1/1 Running 0 9m2s zookeeper-2 1/1 Running 0 9m2s
查看 Service
kubectl get service -n zoons

预期输出类似:

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE zookeeper ClusterIP 10.108.36.44 <none> 2181/TCP 168m zookeepers ClusterIP None <none> 2888/TCP,3888/TCP 31m

可以看到zookeeper(客户端访问入口)暴露 2181 端口,zookeepers(无头服务)为ClusterIP: None并暴露 2888/3888 节点间通信端口。

查看 StatefulSet
kubectl get statefulset -n zoons

预期输出类似:

NAME READY AGE zookeepers 3/3 10m

如果一切正常,说明 ZooKeeper 集群已经成功运行。

五、与 ClickHouse Operator 的集成与进一步参考

  • 本文对应的全部手工部署清单与脚本位于 deploy/zookeeper/zookeeper-manually,其中 advanced 目录还提供了zookeeper-watch.sh等辅助脚本供观察部署过程。
  • 若希望以 Operator 方式管理 ZooKeeper,可参考 zookeeper-with-zookeeper-operator 目录下的安装脚本与 CR 示例(1 节点/3 节点,含自定义探针版本)。
  • 将 ZooKeeper 与 ClickHouse 复制表结合使用的完整流程,请阅读 docs/replication_setup.md 及 docs/chi-examples/ 中04-replication-zookeeper-*.yaml系列示例。
  • 监控方面,ZooKeeper 已通过 7000 端口暴露 Prometheus 指标,仓库的 deploy/prometheus/prometheus-alert-rules-zookeeper.yaml 提供了现成的告警规则示例,Grafana 仪表盘模板见 grafana-dashboard/Zookeeper_dashboard.json。
  • 若计划从 ZooKeeper 迁移到 ClickHouse Keeper,可参考 docs/keeper_migration_from_23_to_24.md 与 docs/keeper_reference.md。

总结

在 clickhouse-operator 项目中,ZooKeeper 的部署路径清晰可循:测试场景走 快速启动(一条命令即可拉起 1 节点或 3 节点集群),生产场景走 高级设置(按 Service → Headless Service → PDB → StorageClass → StatefulSet 的顺序逐项精细配置,并显式声明存储方案)。部署完成后,通过 Pod/Service/StatefulSet 的kubectl get输出以及ruok/mntr等四字命令即可确认集群健康状态,随后即可将 ZooKeeper 地址接入 ClickHouse 复制集群,构建完整的高可用数据链路。

【免费下载链接】clickhouse-operatorAltinity Kubernetes Operator for ClickHouse creates, configures and manages ClickHouse® clusters running on Kubernetes项目地址: https://gitcode.com/GitHub_Trending/cl/clickhouse-operator

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询