跳到主要内容

集群部署与管理

本章介绍 ZooKeeper 集群的部署、配置和运维管理。

集群架构

角色说明

ZooKeeper 集群中的服务器有三种角色:

角色说明参与投票
Leader处理所有写请求,发起投票
Follower处理读请求,参与投票
Observer处理读请求,不参与投票

集群规模

ZooKeeper 集群建议使用奇数个节点:

推荐配置:
- 3 节点:允许 1 个节点故障
- 5 节点:允许 2 个节点故障
- 7 节点:允许 3 个节点故障

公式:容忍故障数 = (节点数 - 1) / 2

为什么使用奇数节点

3 节点集群:需要 2 票通过,允许 1 个故障
4 节点集群:需要 3 票通过,允许 1 个故障(与 3 节点相同)

结论:4 节点与 3 节点的容错能力相同,但多了一个节点的开销

集群部署

规划集群

假设部署一个 5 节点的集群:

节点IP 地址myid端口
node1192.168.1.10112181, 2888, 3888
node2192.168.1.10222181, 2888, 3888
node3192.168.1.10332181, 2888, 3888
node4192.168.1.10442181, 2888, 3888
node5192.168.1.10552181, 2888, 3888

配置文件

在每个节点上创建 zoo.cfg

# 心跳间隔(毫秒)
tickTime=2000

# Follower 连接 Leader 的超时时间(心跳数)
initLimit=10

# Follower 与 Leader 同步的超时时间(心跳数)
syncLimit=5

# 数据目录
dataDir=/var/lib/zookeeper/data

# 事务日志目录(建议独立磁盘)
dataLogDir=/var/lib/zookeeper/log

# 客户端连接端口
clientPort=2181

# 最大客户端连接数
maxClientCnxns=60

# 最小会话超时(默认 2 * tickTime)
minSessionTimeout=4000

# 最大会话超时(默认 20 * tickTime)
maxSessionTimeout=40000

# 集群配置
# server.id=host:peerPort:leaderPort
server.1=192.168.1.101:2888:3888
server.2=192.168.1.102:2888:3888
server.3=192.168.1.103:2888:3888
server.4=192.168.1.104:2888:3888
server.5=192.168.1.105:2888:3888

# 自动清理配置
autopurge.snapRetainCount=3
autopurge.purgeInterval=1

# 4 字命令白名单
4lw.commands.whitelist=stat, ruok, conf, isro

创建 myid 文件

在每个节点上创建 myid 文件:

# node1
echo "1" > /var/lib/zookeeper/data/myid

# node2
echo "2" > /var/lib/zookeeper/data/myid

# 以此类推...

启动集群

# 在每个节点上启动
zkServer.sh start

# 查看状态
zkServer.sh status

# 输出示例(Leader 节点)
Mode: leader

# 输出示例(Follower 节点)
Mode: follower

Observer 配置

Observer 不参与投票,用于扩展读能力:

# 在 Observer 节点的配置中添加
peerType=observer

# 集群配置中标记 Observer
server.1=192.168.1.101:2888:3888
server.2=192.168.1.102:2888:3888
server.3=192.168.1.103:2888:3888
server.4=192.168.1.104:2888:3888:observer

Observer 的优势:

  • 不参与投票,减少选举时间
  • 可以部署任意数量
  • 扩展读能力而不影响写性能

动态配置

ZooKeeper 3.5.0 支持动态配置,可以在运行时修改集群成员:

动态配置文件

创建 zoo.cfg

# 静态配置
tickTime=2000
dataDir=/var/lib/zookeeper/data
clientPort=2181

# 动态配置文件路径
dynamicConfigFile=/etc/zookeeper/zoo.cfg.dynamic

创建 zoo.cfg.dynamic

server.1=192.168.1.101:2888:3888:participant
server.2=192.168.1.102:2888:3888:participant
server.3=192.168.1.103:2888:3888:participant

动态添加节点

# 连接到 ZooKeeper
zkCli.sh -server localhost:2181

# 添加新节点
reconfig -add server.4=192.168.1.104:2888:3888:participant

# 添加 Observer
reconfig -add server.5=192.168.1.105:2888:3888:observer

动态移除节点

# 移除节点
reconfig -remove 4

监控运维

四字命令

ZooKeeper 提供了一组四字命令用于监控:

# 检查服务器状态
echo stat | nc localhost 2181

# 检查服务器是否存活
echo ruok | nc localhost 2181

# 查看配置信息
echo conf | nc localhost 2181

# 查看连接的客户端
echo cons | nc localhost 2181

# 查看等待队列
echo dump | nc localhost 2181

# 查看环境变量
echo envi | nc localhost 2181

# 查看服务器角色
echo isro | nc localhost 2181

# 查看监控统计
echo mntr | nc localhost 2181

# 查看服务器统计
echo srvr | nc localhost 2181

# 查看会话信息
echo srst | nc localhost 2181

# 查看监视器
echo wchs | nc localhost 2181

# 查看监视器详情
echo wchc | nc localhost 2181

# 查看监视器路径
echo wchp | nc localhost 2181

mntr 命令输出详解

echo mntr | nc localhost 2181

# 输出示例
zk_version 3.8.4
zk_avg_latency 0
zk_max_latency 10
zk_min_latency 0
zk_packets_received 100
zk_packets_sent 99
zk_num_alive_connections 5
zk_outstanding_requests 0
zk_server_state leader
zk_znode_count 100
zk_watch_count 10
zk_ephemerals_count 5
zk_approximate_data_size 1024
zk_open_file_descriptor_count 50
zk_max_file_descriptor_count 1024
zk_followers 4
zk_synced_followers 4
zk_pending_syncs 0
zk_last_proposal_size 100
zk_min_proposal_size 100
zk_max_proposal_size 100

JMX 监控

启用 JMX 监控:

# 在 zkEnv.sh 中添加
export JMXDISABLE=false
export JMXPORT=9010
export JMXAUTH=false
export JMXSSL=false

使用 JConsole 或 VisualVM 连接:

service:jmx:rmi:///jndi/rmi://localhost:9010/jmxrmi

Prometheus 监控

使用 Prometheus exporter 监控 ZooKeeper:

# prometheus.yml
scrape_configs:
- job_name: 'zookeeper'
static_configs:
- targets:
- '192.168.1.101:7000'
- '192.168.1.102:7000'
- '192.168.1.103:7000'

性能调优

JVM 调优

编辑 zkEnv.sh

# 堆内存设置
export JVMFLAGS="-Xms2g -Xmx2g $JVMFLAGS"

# GC 设置
export JVMFLAGS="-XX:+UseG1GC -XX:MaxGCPauseMillis=100 $JVMFLAGS"

# GC 日志
export JVMFLAGS="-Xlog:gc*:file=/var/log/zookeeper/gc.log $JVMFLAGS"

磁盘优化

# 事务日志使用独立磁盘
dataLogDir=/disk1/zookeeper/log
dataDir=/disk2/zookeeper/data

# 预分配事务日志文件大小
preAllocSize=65536

# 快照间隔
snapCount=100000

网络优化

# 增加缓冲区大小
globalOutstandingLimit=1000

# 增加最大连接数
maxClientCnxns=100

备份与恢复

数据备份

# 备份数据目录
tar -czvf zookeeper-backup-$(date +%Y%m%d).tar.gz /var/lib/zookeeper

# 备份事务日志和快照
rsync -avz /var/lib/zookeeper/ backup-server:/backup/zookeeper/

数据恢复

# 停止 ZooKeeper
zkServer.sh stop

# 恢复数据
tar -xzvf zookeeper-backup-20250101.tar.gz -C /

# 启动 ZooKeeper
zkServer.sh start

快照恢复

# 使用 zkSnapshotToolkit 恢复快照
java -cp zookeeper.jar org.apache.zookeeper.server.SnapshotFormatter \
/var/lib/zookeeper/data/version-2/snapshot.xxx

故障处理

Leader 选举失败

症状:集群无法选出 Leader

排查步骤:

# 检查各节点状态
zkServer.sh status

# 检查网络连通性
ping <peer-host>
telnet <peer-host> 2888

# 检查日志
tail -f /var/log/zookeeper/zookeeper-*.out

解决方案:

  • 确保超过半数节点存活
  • 检查防火墙配置
  • 检查 myid 文件是否正确

会话频繁断开

症状:客户端会话频繁断开重连

排查步骤:

# 检查网络延迟
ping <zookeeper-host>

# 检查服务器负载
top
iostat -x 1

# 检查连接数
echo cons | nc localhost 2181

解决方案:

  • 增加会话超时时间
  • 检查网络稳定性
  • 检查服务器负载

磁盘空间不足

症状:无法写入事务日志

排查步骤:

# 检查磁盘空间
df -h

# 检查快照和日志数量
ls -la /var/lib/zookeeper/data/version-2/
ls -la /var/lib/zookeeper/log/version-2/

解决方案:

# 启用自动清理
autopurge.snapRetainCount=3
autopurge.purgeInterval=1

内存溢出

症状:OOM 错误

排查步骤:

# 检查 JVM 内存使用
jstat -gc <pid> 1000

# 检查堆内存
jmap -heap <pid>

解决方案:

# 增加堆内存
export JVMFLAGS="-Xms4g -Xmx4g $JVMFLAGS"

# 调整 GC 参数
export JVMFLAGS="-XX:+UseG1GC $JVMFLAGS"

安全配置

启用认证

# 启用 SASL 认证
authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
kerberos.removeHostFromPrincipal=true
kerberos.removeRealmFromPrincipal=true

SSL/TLS 配置

# 启用 SSL
secureClientPort=2281
serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
ssl.keyStore.location=/path/to/keystore.jks
ssl.keyStore.password=password
ssl.trustStore.location=/path/to/truststore.jks
ssl.trustStore.password=password

客户端 SSL 连接

import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.ExponentialBackoffRetry;

public class SslConnection {
public static void main(String[] args) {
System.setProperty("zookeeper.client.secure", "true");
System.setProperty("zookeeper.clientCnxnSocket",
"org.apache.zookeeper.ClientCnxnSocketNetty");
System.setProperty("zookeeper.ssl.keyStore.location",
"/path/to/keystore.jks");
System.setProperty("zookeeper.ssl.keyStore.password", "password");
System.setProperty("zookeeper.ssl.trustStore.location",
"/path/to/truststore.jks");
System.setProperty("zookeeper.ssl.trustStore.password", "password");

CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("localhost:2281")
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
.build();

client.start();
}
}

运维脚本

健康检查脚本

#!/bin/bash

ZK_HOST="localhost"
ZK_PORT="2181"

check_zookeeper() {
result=$(echo ruok | nc $ZK_HOST $ZK_PORT)
if [ "$result" = "imok" ]; then
echo "ZooKeeper is running"
return 0
else
echo "ZooKeeper is not responding"
return 1
fi
}

check_leader() {
stat=$(echo stat | nc $ZK_HOST $ZK_PORT | grep Mode)
echo "Server mode: $stat"
}

check_connections() {
cons=$(echo cons | nc $ZK_HOST $ZK_PORT | wc -l)
echo "Active connections: $cons"
}

check_zookeeper
check_leader
check_connections

集群状态检查脚本

#!/bin/bash

NODES=("192.168.1.101" "192.168.1.102" "192.168.1.103")

for node in "${NODES[@]}"; do
echo "=== Checking $node ==="
mode=$(echo stat | nc $node 2181 2>/dev/null | grep Mode | awk '{print $2}')
if [ -z "$mode" ]; then
echo "Status: DOWN"
else
echo "Status: UP, Mode: $mode"
fi
done

小结

本章学习了 ZooKeeper 集群的部署与管理:

  1. 集群架构:Leader、Follower、Observer 三种角色
  2. 集群部署:配置文件、myid、启动流程
  3. 动态配置:运行时修改集群成员
  4. 监控运维:四字命令、JMX、Prometheus
  5. 性能调优:JVM、磁盘、网络优化
  6. 故障处理:常见问题排查和解决
  7. 安全配置:认证、SSL/TLS

下一章是 ZooKeeper 的速查表。