集群部署与管理
本章介绍 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 | 端口 |
|---|---|---|---|
| node1 | 192.168.1.101 | 1 | 2181, 2888, 3888 |
| node2 | 192.168.1.102 | 2 | 2181, 2888, 3888 |
| node3 | 192.168.1.103 | 3 | 2181, 2888, 3888 |
| node4 | 192.168.1.104 | 4 | 2181, 2888, 3888 |
| node5 | 192.168.1.105 | 5 | 2181, 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 集群的部署与管理:
- 集群架构:Leader、Follower、Observer 三种角色
- 集群部署:配置文件、myid、启动流程
- 动态配置:运行时修改集群成员
- 监控运维:四字命令、JMX、Prometheus
- 性能调优:JVM、磁盘、网络优化
- 故障处理:常见问题排查和解决
- 安全配置:认证、SSL/TLS
下一章是 ZooKeeper 的速查表。