ZooKeeper 集群部署
1.ZooKeeper 集群介绍
ZooKeeper集群用于解决单点和单机性能及数据高可用等问题。
集群结构
Zookeeper集群基于Master/Slave的模型
处于主要地位负责处理写操作)的主机称为Leader节点,处于次要地位主要负责处理读操作的主机称为 follower 节点
当进行写操作时,由Master(leader)完成,再同步到其它Slave(follower)节点,而且要求在保证写操作在所有节点的总数过半后,才会认为写操作成功
生产中读取的方式一般是以异步复制方式来实现的。
对于n台server,每个server都知道彼此的存在。只要有>n/2台server节点可用,整个zookeeper系统保持可用。因此zookeeper集群通常由奇数台Server节点组成
官方链接: 下图表示读的比例越高,性能越好
ZooKeeper: Because Coordinating Distributed Systems is a Zoo
号 | 角色 | 职责描述 |
---|---|---|
1 | 领导者(Leader) | 负责处理写入请求的,事务请求的唯一调度和处理者,负责进行投票发起和决议,更新系统状态 |
2 | 跟随者(Follower) | 接收客户请求并向客户端返回结果,在选Leader过程中参与投票 |
3 | 观察者(Observer) | 转交客户端写请求给leader节点,和同步leader状态 和Follower唯一区别就是不参与Leader投票,也不参与写操作的"过半写成功"策略 |
4 | 学习者(Learner) | 和leader进行状态同步的节点统称Learner,包括:Follower和Observer |
5 | 客户端(client) | 请求发起方 |
选举过程
节点角色状态:
LOOKING:寻找 Leader 状态,处于该状态需要进入选举流程
LEADING:领导者状态,处于该状态的节点说明是角色已经是Leader
FOLLOWING:跟随者状态,表示 Leader已经选举出来,当前节点角色是follower
OBSERVER:观察者状态,表明当前节点角色是 observer
选举 ID:
ZXID(zookeeper transaction id)事务id:每个改变 Zookeeper状态的操作都会自动生成一个对应的zxid。ZXID最大的节点优先选为Leader
myid:服务器的唯一标识(SID),通过配置 myid 文件指定,集群中唯一,当ZXID一样时,myid大的节点优先选为Leader
ZooKeeper 集群选举过程:
当集群中的 zookeeper 节点启动以后,会根据配置文件中指定的 zookeeper节点地址进行leader 选择操作,过程如下:
每个zookeeper 都会发出投票,由于是第一次选举leader,因此每个节点都会把自己当做leader 角色进行选举,每个zookeeper 的投票中都会包含自己的myid和zxid,此时zookeeper 1 的投票为myid 为 1,初始zxid有一个初始值0x0,后期会随着数据更新而自动变化,zookeeper 2 的投票为myid 为2,初始zxid 为初始生成的值。
每个节点接受并检查对方的投票信息,比如投票时间、是否状态为LOOKING状态的投票。
对比投票,优先检查zxid,如果zxid 不一样则 zxid 大的为leader,如果zxid相同则继续对比myid,myid 大的一方为 leader成为 Leader 的必要条件: Leader 要具有最高的zxid;当集群的规模是 n 时,集群中大多数的机器(至少n/2+1)得到响应并从 follower 中选出的 Leader。
心跳机制:Leader 与 Follower 利用 PING 来感知对方的是否存活,当 Leader无法响应PING 时,将重新发起 Leader 选举。
当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB(Zookeeper Atomic Broadcast) 协议就会进入恢复模式并选举产生新的Leader服务器。这个过程大致如下:
Leader Election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。
Discovery(发现阶段):在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。
Synchronization(同步阶段):同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。同步完成之后 准 leader 才会成为真正的 leader。
Broadcast(广播阶段) :到了这个阶段,Zookeeper 集群才能正式对外提供事务服务,并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步
ZAB 协议介绍
ZAB(ZooKeeper Atomic Broadcast 原子广播) 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。 在 ZooKeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,ZooKeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间的数据一致性。
ZooKeeper 集群特性
整个集群中只要有超过集群数量一半的 zookeeper工作是正常的,那么整个集群对外就是可用的
假如有 2 台服务器做了一个 Zookeeper 集群,只要有任何一台故障或宕机,那么这个 ZooKeeper集群就不可用了,因为剩下的一台没有超
过集群一半的数量,但是假如有三台zookeeper 组成一个集群, 那么损坏一台就还剩两台,大于 3台的一半,所以损坏一台还是可以正常运
行的,但是再损坏一台就只剩一台集群就不可用了。那么要是 4 台组成一个zookeeper集群,损坏一台集群肯定是正常的,那么损坏两台就
还剩两台,那么2台不大于集群数量的一半,所以 3 台的 zookeeper 集群和 4 台的 zookeeper集群损坏两台的结果都是集群不可用,以此类推 5 台和 6 台以及 7 台和 8台都是同理
Zookeeper 事务日志和快照
ZooKeeper集群中的每个服务器节点每次接收到写操作请求时,都会先将这次请求发送给leader,leader将这次写操作转换为带有状态的事
务,然后leader会对这次写操作广播出去以便进行协调。当协调通过(大多数节点允许这次写)后,leader通知所有的服务器节点,让它们将这
次写操作应用到内存数据库中,并将其记录到事务日志中。
当事务日志记录的次数达到一定数量后(默认10W次),就会将内存数据库序列化一次,使其持久化保存到磁盘上,序列化后的文件称为"快照
文件"。每次拍快照都会生成新的事务日志。
2.ZooKeeper 集群部署
官方文档:
https://zookeeper.apache.org/doc/r3.6.2/zookeeperAdmin.html#sc_zkMulitServerSetup
环境准备
#三台ubuntu18.04,20.04,22.04
zookeeper-node1.wang.org 10.0.0.101
zookeeper-node2.wang.org 10.0.0.102
zookeeper-node3.wang.org 10.0.0.103
#在三个节点都安装JDK8或JDK11
[root@zookeeper-node1 ~]#apt update && apt -y install openjdk-11-jdk
[root@zookeeper-node1 ~]#apt update && apt -y install openjdk-8-jdk
[root@zookeeper-node2 ~]#apt update && apt -y install openjdk-8-jdk
[root@zookeeper-node3 ~]#apt update && apt -y install openjdk-8-jdk
在所有节点下载并解压缩 ZooKeeper 包文件
[root@zookeeper-node1 ~]#wget -P /usr/local/src https://downloads.apache.org/zookeeper/stable/apache
zookeeper-3.6.3-bin.tar.gz
[root@zookeeper-node1 ~]#tar xf /usr/local/src/apache-zookeeper-3.6.3-bin.tar.gz -C /usr/local/
[root@zookeeper-node1 ~]#ln -s /usr/local/apache-zookeeper-3.6.3-bin /usr/local/zookeeper
[root@zookeeper-node1 ~]#echo 'PATH=/usr/local/zookeeper/bin:$PATH' > /etc/profile.d/zookeeper.sh
[root@zookeeper-node1 ~]#. /etc/profile.d/zookeeper.sh
准备配置文件
#三个节点都要创建数据目录
[root@zookeeper-node1 ~]#mkdir /usr/local/zookeeper/data
#基于模板配置文件生成配置文件
[root@zookeeper-node1 ~]#cp /usr/local/zookeeper/conf/zoo_sample.cfg /usr/local/zookeeper/conf/zoo.cfg
#修改配置文件
[root@zookeeper-node1 ~]#vim /usr/local/zookeeper/conf/zoo.cfg
#配置文件内容
[root@zookeeper-node1 ~]#grep -v "\^\#" /usr/local/zookeeper/conf/zoo.cfg
tickTime=2000 #服务器与服务器之间的单次心跳检测时间间隔,单位为毫秒
initLimit=10 #集群中leader 服务器与follower服务器初始连接心跳次数,即多少个 2000 毫秒
syncLimit=5 #leader 与follower之间连接完成之后,后期检测发送和应答的心跳次数,如果该follower在设置的时间内(5*2000)不能与 leader 进行通信,那么此 follower将被视为不可用。
dataDir=/usr/local/zookeeper/data #自定义的zookeeper保存数据的目录
clientPort=2181 #客户端连接 Zookeeper 服务器的端口,Zookeeper会监听这个端口,接受客户端的访问请求
maxClientCnxns=128 #单个客户端IP 可以和zookeeper保持的连接数
autopurge.snapRetainCount=3 #3.4.0中的新增功能:启用后,ZooKeeper 自动清除功能,会将只保留此最新3个快照和相应的事务日志,并分别保留在dataDir 和dataLogDir中,删除其余部分,默认值为3,最小值为3
autopurge.purgeInterval=24 #3.4.0及之后版本,ZK提供了自动清理日志和快照文件的功能,这个参数指定了清理频率,单位是小时,需要配置一个1或更大的整数,默认是 0,表示不开启自动清理功能
#格式: server.MyID服务器唯一编号=服务器IP:Leader和Follower的数据同步端口(只有leader才会打开):Leader和Follower选举端口(L和F都有)
server.1=10.0.0.101:2888:3888
server.2=10.0.0.102:2888:3888
server.3=10.0.0.103:2888:3888
#如果添加节点,只需要在所有节点上添加新节点的上面形式的配置行,在新节点创建myid文件,并重启所有节点服务即可
[root@zookeeper-node1 ~]#scp /usr/local/zookeeper/conf/zoo.cfg 10.0.0.102:/usr/local/zookeeper/conf/
[root@zookeeper-node1 ~]#scp /usr/local/zookeeper/conf/zoo.cfg 10.0.0.103:/usr/local/zookeeper/conf/
在各个节点生成ID文件
注意: 各个myid文件的内容要和zoo.cfg文件相匹配
[root@zookeeper-node1 ~]#echo 1 > /usr/local/zookeeper/data/myid
[root@zookeeper-node2 ~]#echo 2 > /usr/local/zookeeper/data/myid
[root@zookeeper-node3 ~]#echo 3 > /usr/local/zookeeper/data/myid
各服务器启动 Zookeeper
#注意:在所有三个节点快速启动服务,否则会造成集群失败
[root@zookeeper-node1 ~]#zkServer.sh start
[root@zookeeper-node2 ~]#zkServer.sh start
[root@zookeeper-node3 ~]#zkServer.sh start
#注意:如果无法启动查看日志
[root@zookeeper-node1 ~]#cat /usr/local/zookeeper/logs/zookeeper-root-server-zookeeper-node1.wang.org.out
查看集群状态
#follower会监听3888/tcp端口
[root@zookeeper-node1 ~]#zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
[root@zookeeper-node1 ~]#ss -ntl |grep 888
LISTEN 0 50 [::ffff:10.0.0.101]:3888 *:*
#只有leader监听2888/tcp端口
[root@zookeeper-node2 ~]#zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: leader
[root@zookeeper-node2 ~]#ss -ntl|grep 888
LISTEN 0 50 [::ffff:10.0.0.102]:3888 *:*
LISTEN 0 50 [::ffff:10.0.0.102]:2888 *:*
[root@zookeeper-node3 ~]#zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/local/zookeeper/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
[root@zookeeper-node3 ~]#ss -ntl|grep 888
LISTEN 0 50 [::ffff:10.0.0.103:3888 *:*
一键安装 ZooKeeper-cluster 脚本(注:改ip地址)
#!/bin/bash
#支持在线和离线安装
ZK_VERSION=3.8.1
#ZK_VERSION=3.8.0
#ZK_VERSION=3.6.3
#ZK_VERSION=3.7.1
#ZK_URL=https://archive.apache.org/dist/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz
#3.5.0及以下版本下载链接
ZK_URL=https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-${ZK_VERSION}/apache-zookeeper-${ZK_VERSION}-bin.tar.gz
#ZK_URL="https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/stable/apache-zookeeper-${ZK_VERSION}-bin.tar.gz"
#ZK_URL=https://archive.apache.org/dist/zookeeper/zookeeper-${ZK_VERSION}/zookeeper-${ZK_VERSION}.bin.tar.gz
#ZK_VERSION=3.4.14
#3.5.0以下版本下载链接
#ZK_URL=https://archive.apache.org/dist/zookeeper/zookeeper-${ZK_VERSION}/zookeeper-${ZK_VERSION}.tar.gz
NODE1=10.0.0.201
NODE2=10.0.0.202
NODE3=10.0.0.203
. /etc/os-release
HOST=`hostname -I|awk '{print $1}'`
color () {
RES_COL=60
MOVE_TO_COL="echo -en \\033[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \\033[1;32m"
SETCOLOR_FAILURE="echo -en \\033[1;31m"
SETCOLOR_WARNING="echo -en \\033[1;33m"
SETCOLOR_NORMAL="echo -en \E[0m"
echo -n "$1" && $MOVE_TO_COL
echo -n "["
if [ $2 = "success" -o $2 = "0" ] ;then
${SETCOLOR_SUCCESS}
echo -n $" OK "
elif [ $2 = "failure" -o $2 = "1" ] ;then
${SETCOLOR_FAILURE}
echo -n $"FAILED"
else
${SETCOLOR_WARNING}
echo -n $"WARNING"
fi
${SETCOLOR_NORMAL}
echo -n "]"
echo
}
zk_myid () {
read -p "请输入node编号(默认为 1): " MYID
if [ -z "$MYID" ] ;then
MYID=1
elif [[ ! "$MYID" =~ ^[0-9]+$ ]];then
color "请输入正确的node编号!" 1
exit
else
true
fi
}
install_jdk() {
if [ $ID = 'centos' -o $ID = 'rocky' ];then
yum -y install java-1.8.0-openjdk-devel || { color "安装JDK失败!" 1; exit 1; }
else
apt update
apt install openjdk-11-jdk -y || { color "安装JDK失败!" 1; exit 1; }
#apt install openjdk-8-jdk -y || { color "安装JDK失败!" 1; exit 1; }
fi
java -version
}
install_zookeeper() {
if [ -f apache-zookeeper-${ZK_VERSION}-bin.tar.gz ] ;then
cp apache-zookeeper-${ZK_VERSION}-bin.tar.gz /usr/local/src/
else
wget -P /usr/local/src/ --no-check-certificate $ZK_URL || { color "下载失败!" 1 ;exit ; }
fi
tar xf /usr/local/src/${ZK_URL##*/} -C /usr/local
ln -s /usr/local/apache-zookeeper-*-bin/ /usr/local/zookeeper
echo 'PATH=/usr/local/zookeeper/bin:$PATH' > /etc/profile.d/zookeeper.sh
. /etc/profile.d/zookeeper.sh
mkdir -p /usr/local/zookeeper/data
echo $MYID > /usr/local/zookeeper/data/myid
cat > /usr/local/zookeeper/conf/zoo.cfg <<EOF
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/usr/local/zookeeper/data
clientPort=2181
maxClientCnxns=128
autopurge.snapRetainCount=3
autopurge.purgeInterval=24
server.1=${NODE1}:2888:3888
server.2=${NODE2}:2888:3888
server.3=${NODE3}:2888:3888
EOF
cat > /lib/systemd/system/zookeeper.service <<EOF
[Unit]
Description=zookeeper.service
After=network.target
[Service]
Type=forking
#Environment=/usr/local/zookeeper
ExecStart=/usr/local/zookeeper/bin/zkServer.sh start
ExecStop=/usr/local/zookeeper/bin/zkServer.sh stop
ExecReload=/usr/local/zookeeper/bin/zkServer.sh restart
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now zookeeper.service
systemctl is-active zookeeper.service
if [ $? -eq 0 ] ;then
color "zookeeper 安装成功!" 0
else
color "zookeeper 安装失败!" 1
exit 1
fi
}
zk_myid
install_jdk
install_zookeeper
测试:通过关闭node2查看集群状态和选举的流程,在关闭node3查看集群状态和选举的流程,在开启node2和node3查看集群状态和选举的流程
[root@zookeeper-node1 ~]#tail -f /usr/local/zookeeper/apache-zookeeper-3.8.4-bin/logs/zookeeper--server-zookeeper-node1.out
[root@zookeeper-node2 ~]#tail -f /usr/local/zookeeper/apache-zookeeper-3.8.4-bin/logs/zookeeper--server-zookeeper-node2.out
[root@zookeeper-node3 ~]#tail -f /usr/local/zookeeper/apache-zookeeper-3.8.4-bin/logs/zookeeper--server-zookeeper-node3.out