ZooKeeper
数据模型与集群原理
ZooKeeper 为分布式应用提供协调服务,常用于注册发现、配置、选主和锁。它维护树形 znode,支持持久节点、临时节点及顺序节点。临时节点随会话结束删除;网络短暂中断不等于会话立即过期。
客户端请求可连接任意 Server,写入通过 Leader 排序并由多数派确认。3 个投票节点可容忍 1 个故障,5 个可容忍 2 个;偶数节点通常不会增加故障容忍数。Observer 不参与投票,不能计入 quorum。
传统 Watch 是一次性通知,触发后需要重新注册并重新读取状态;支持持久 Watch 的版本也需要处理断线、会话过期和重新同步。不要把 Watch 当成不会丢失的消息队列。
Kubernetes 三节点部署
下面基于 ZooKeeper 3.9 系列,使用官方镜像、Headless Service、StatefulSet 和每节点独立 PVC。生产固定经过审核的补丁版本或 digest。要求至少 3 台可调度工作节点和默认 StorageClass;没有默认存储类时在 PVC 中指定 storageClassName。
保存为 zookeeper.yaml:
apiVersion: v1
kind: Service
metadata:
name: zk-hs
spec:
clusterIP: None
publishNotReadyAddresses: true
selector:
app: zk
ports:
- name: client
port: 2181
- name: quorum
port: 2888
- name: election
port: 3888
---
apiVersion: v1
kind: Service
metadata:
name: zk-cs
spec:
selector:
app: zk
ports:
- name: client
port: 2181
---
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: zk
spec:
maxUnavailable: 1
selector:
matchLabels:
app: zk
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: zk
spec:
serviceName: zk-hs
replicas: 3
podManagementPolicy: Parallel
selector:
matchLabels:
app: zk
template:
metadata:
labels:
app: zk
spec:
terminationGracePeriodSeconds: 60
securityContext:
fsGroup: 1000
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app: zk
topologyKey: kubernetes.io/hostname
containers:
- name: zookeeper
image: zookeeper:3.9.3
command: ["/bin/bash", "-ec"]
args:
- |
export ZOO_MY_ID="$(( ${HOSTNAME##*-} + 1 ))"
exec /docker-entrypoint.sh zkServer.sh start-foreground
env:
- name: ZOO_SERVERS
value: "server.1=zk-0.zk-hs:2888:3888;2181 server.2=zk-1.zk-hs:2888:3888;2181 server.3=zk-2.zk-hs:2888:3888;2181"
- name: ZOO_TICK_TIME
value: "2000"
- name: ZOO_INIT_LIMIT
value: "10"
- name: ZOO_SYNC_LIMIT
value: "5"
- name: ZOO_AUTOPURGE_PURGEINTERVAL
value: "24"
- name: ZOO_AUTOPURGE_SNAPRETAINCOUNT
value: "5"
- name: ZOO_4LW_COMMANDS_WHITELIST
value: "srvr,ruok"
- name: JVMFLAGS
value: "-Xms512m -Xmx512m"
ports:
- name: client
containerPort: 2181
- name: quorum
containerPort: 2888
- name: election
containerPort: 3888
resources:
requests:
cpu: 250m
memory: 768Mi
limits:
memory: 1Gi
startupProbe:
exec:
command: ["/bin/bash", "-ec", "zkServer.sh status"]
periodSeconds: 10
failureThreshold: 30
readinessProbe:
exec:
command: ["/bin/bash", "-ec", "zkServer.sh status"]
periodSeconds: 10
timeoutSeconds: 5
volumeMounts:
- name: data
mountPath: /data
- name: data
mountPath: /datalog
subPath: transaction-log
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
Parallel 允许首次同时启动多个成员,避免首个 Pod 等待 quorum 而后续成员未创建。publishNotReadyAddresses 让尚未就绪的成员能互相发现。数据卷与事务日志共用每个成员自己的 PVC,生产可按 I/O 需求拆成两个独立 PVC。
kubectl apply --dry-run=server -f zookeeper.yaml
kubectl apply -f zookeeper.yaml
kubectl rollout status statefulset/zk --timeout=5m
kubectl get pods,pvc -l app=zk
kubectl exec zk-0 -- zkServer.sh status
kubectl exec zk-1 -- zkServer.sh status
kubectl exec zk-2 -- zkServer.sh status
kubectl exec -it zk-0 -- zkCli.sh -server zk-0.zk-hs:2181,zk-1.zk-hs:2181,zk-2.zk-hs:2181
PVC 没有自动附加 Pod 的 app 标签,必要时使用 kubectl get pvc 查看 data-zk-*。运行后核对一个 Leader、两个 Follower,以及各节点 myid 与对应的服务器编号。
本例尚未配置认证与 TLS,只能用于受限测试网络。对外提供服务前补齐下节安全配置,不能直接创建公网 NodePort 或 LoadBalancer。
会话、ACL 与 TLS
客户端连接串列出多个节点,设置合理的连接与会话超时;通过成熟客户端库实现重连、锁和选主,避免自行拼接临时节点逻辑。分布式锁还应使用 fencing token 等机制阻止过期持有者继续操作外部资源。
测试环境可在 zkCli.sh 中验证 digest ACL,REPLACE_WITH_STRONG_PASSWORD 必须替换:
addauth digest app:REPLACE_WITH_STRONG_PASSWORD
create /app "" auth::cdrwa
getAcl /app
ACL 绑定在节点上,不会自动递归继承到所有子节点;创建子节点时也必须使用合适 ACL。ACL 认证不加密传输,生产使用 TLS 客户端端口和 quorum TLS,配置受限证书、信任库与网络策略。启用安全端口后使用匹配的 Netty 客户端配置,不能直接把明文客户端连到 TLS 端口。
持久化、备份与恢复
快照与事务日志共同构成恢复数据。autopurge 负责清理旧文件,不是备份。备份保留服务器配置、myid、所需快照和对应事务日志,使用停止的成员或经验证的一致性存储快照获取文件,并把备份放在独立故障域。
恢复前在隔离环境验证数据树与 ACL。单成员损坏和整个 ensemble 恢复是不同流程;不要把其他成员的 myid 原样复制,也不要在运行集群中随意删除数据目录。完整集群灾难恢复按目标版本官方步骤重建成员关系,再允许客户端连接。
监控、故障与升级
监控请求延迟、未完成请求、连接数、watch 数、znode 数、同步状态、磁盘 fsync 和 Leader 变动。ruok=imok 仅说明进程响应,不证明集群有 quorum;结合角色、同步情况与实际读写验收。
| 现象 | 检查方向 |
|---|---|
| 集群无法选主 | 节点数、DNS、2888/3888 连通性、myid、时钟与事务日志 |
| Pod Pending | PVC/StorageClass、反亲和性与可用工作节点数量 |
| 客户端频繁 session expired | GC 停顿、磁盘延迟、网络抖动、会话超时和连接数 |
| NoAuth | 节点 ACL、客户端 addauth、认证方案及目标路径 |
| 磁盘持续增长 | autopurge、日志保留、备份策略和数据规模 |
升级逐个成员进行,操作前确认 quorum、备份和版本兼容。PDB 仅约束支持 eviction 的自愿中断,不能防止手动删除、硬件故障或所有维护操作。
评论