这篇文章需要你有一定的 zookeeper 知识
zookeeper 部署有两种模式:单机模式和集群模式。
单机模式
在 linux 服务器上解压 zookeeper 的压缩包,然后在安装目录创建一个 data 目录,用来配置 dataDir 配置项。
把 conf 目录中的 zoo_sample.cfg 文件复制一份改名为 zoo.cfg(zookeeper 默认配置文件名就是这个),然后修改 zoo.cfg 如下:
# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
dataDir=/root/software/zookeeper/data
# the port at which the clients will connect
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
#
# https://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpHost=0.0.0.0
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true
只要修改 dataDir 配置项即可,其他不变。
这里有个小插曲,启动 zookeeper 的时候报找不到 JAVA_HOME,但是检查一遍确实已经配置了 JAVA_HOME。
$ sh zkServer.sh start
zkServer.sh: 73: /root/software/zookeeper/bin/zkEnv.sh: [[: not found
-p: not found
java is /root/software/jdk8/bin/java
Error: JAVA_HOME is not set and java could not be found in PATH.
这个问题是 zkEnv.sh 文件有个语法问题,参考:codeantenna.com/a/Y0ESznahn…,用 bash 代替 sh 来启动。
$ bash zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /root/software/zookeeper/bin/../conf/zoo.cfg
Starting zookeeper ... FAILED TO START
这次没有报之前的错误了,但是还是启动失败,查看 zookeeper 的日志文件发现端口被占用了。
2023-10-11 23:00:18,106 [myid:] - ERROR [main:o.a.z.s.ZooKeeperServerMain@86] - Unable to start AdminServer, exiting abnormally
org.apache.zookeeper.server.admin.AdminServer$AdminServerException: Problem starting AdminServer on address 0.0.0.0, port 8080 and command URL /commands
at org.apache.zookeeper.server.admin.JettyAdminServer.start(JettyAdminServer.java:199)
at org.apache.zookeeper.server.ZooKeeperServerMain.runFromConfig(ZooKeeperServerMain.java:155)
at org.apache.zookeeper.server.ZooKeeperServerMain.initializeAndRun(ZooKeeperServerMain.java:113)
at org.apache.zookeeper.server.ZooKeeperServerMain.main(ZooKeeperServerMain.java:68)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:141)
at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:91)
Caused by: java.io.IOException: Failed to bind to /0.0.0.0:8080
at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:349)
at org.eclipse.jetty.server.ServerConnector.open(ServerConnector.java:310)
at org.eclipse.jetty.server.AbstractNetworkConnector.doStart(AbstractNetworkConnector.java:80)
at org.eclipse.jetty.server.ServerConnector.doStart(ServerConnector.java:234)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.eclipse.jetty.server.Server.doStart(Server.java:401)
at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:73)
at org.apache.zookeeper.server.admin.JettyAdminServer.start(JettyAdminServer.java:190)
... 5 common frames omitted
Caused by: java.net.BindException: 地址已在使用
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:438)
at sun.nio.ch.Net.bind(Net.java:430)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:225)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
at org.eclipse.jetty.server.ServerConnector.openAcceptChannel(ServerConnector.java:344)
... 12 common frames omitted
Unable to start AdminServer, exiting abnormally
从 zookeeper 3.5 开始,zookeeper 内嵌了一个 Jetty 服务器叫做 AdminServer,它默认监听 8080 端口,而我的服务器的 8080 端口已经被使用了,所以解决方案是修改 AdminServer 监听的端口号。在配置文件 zoo.cfg 中添加配置:
admin.serverPort=8888
AdminServer 相关配置项如下:
- admin.enableServer:启用或禁用 AdminServer,默认启用
- admin.serverAddress:Jetty 服务器监听的 IP 地址,默认是 0.0.0.0
- admin.serverPort:Jetty 服务器监听的端口号,默认是 8080
- admin.idleTimeout:连接在发送或接收数据之前可以等待的最大空闲时间
- admin.commandURL:上下文路径,默认值 "/commands"
集群模式
zookeeper 的集群模式最少要三台服务器,相比与单机模式,集群模式需要额外添加少量配置,如果你只有一台服务器,推荐使用 Docker 来部署(使用 Docker 甚至不用考虑 AdminServer 端口占用的问题)。
首先创建一个 Dockerfile 文件构建 zookeeper 镜像:
# 我自己构建的基于 ubuntu 的包含 jdk 环境的镜像
FROM swr.cn-south-1.myhuaweicloud.com/monkeybrain/jdk-ubuntu:v2
# 拷贝 zookeeper 压缩包到镜像
COPY apache-zookeeper-3.9.1-bin.tar.gz /usr/zookeeper/
# 指定工作目录
WORKDIR /usr/zookeeper
RUN tar -zxvf apache-zookeeper-3.9.1-bin.tar.gz
RUN mv apache-zookeeper-3.9.1-bin zookeeper-3.9
# 设置匿名卷
VOLUME [ "/usr/zookeeper/zookeeper-3.9/data", "/usr/zookeeper/zookeeper-3.9/conf", "/usr/zookeeper/zookeeper-3.9/logs" ]
WORKDIR /usr/zookeeper/zookeeper-3.9/bin
# 这里注意要前台运行 zookeeper,不然 docker 容器会自动退出
CMD ["bash", "zkServer.sh", "start-foreground"]
构建镜像
$ docker build -t swr.cn-south-1.myhuaweicloud.com/monkeybrain/myzookeeper:v3 .
先启动单机 zookeeper 试一试,zoo.cfg 文件如下:
tickTime=2000
initLimit=5
syncLimit=2
dataDir=/usr/zookeeper/zookeeper-3.9/data
clientPort=2181
启动容器
$ docker run --name zookeeper --rm -v /d/IT/docker/zookeeper/conf:/usr/zookeeper/zookeeper-3.9/conf -v /d/IT/docker/zookeeper/logs:/usr/zookeeper/zookeeper-3.9/logs -p 2181:2181 swr.cn-south-1.myhuaweicloud.com/monkeybrain/myzookeeper:v3
在本机运行 zkCli.cmd 连接 zookeeper 服务器
$ zkCli.cmd
连接成功
[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
然后开始部署 3 台服务器的 zookeeper 集群,使用 docker compose 来管理三个 zookeeper 容器,docker-compose.yml 文件如下:
version: '3'
services:
zoo1:
image: swr.cn-south-1.myhuaweicloud.com/monkeybrain/myzookeeper:v3
container_name: zoo1
ports:
- "2181:2181"
- "8081:8080"
volumes:
- /d/IT/docker/zookeeper/server1/conf:/usr/zookeeper/zookeeper-3.9/conf
- /d/IT/docker/zookeeper/server1/data:/usr/zookeeper/zookeeper-3.9/data
- /d/IT/docker/zookeeper/server1/logs:/usr/zookeeper/zookeeper-3.9/logs
networks:
- zookeeper
zoo2:
image: swr.cn-south-1.myhuaweicloud.com/monkeybrain/myzookeeper:v3
container_name: zoo2
ports:
- "2182:2181"
- "8082:8080"
volumes:
- /d/IT/docker/zookeeper/server2/conf:/usr/zookeeper/zookeeper-3.9/conf
- /d/IT/docker/zookeeper/server2/data:/usr/zookeeper/zookeeper-3.9/data
- /d/IT/docker/zookeeper/server2/logs:/usr/zookeeper/zookeeper-3.9/logs
depends_on:
- zoo1
networks:
- zookeeper
zoo3:
image: swr.cn-south-1.myhuaweicloud.com/monkeybrain/myzookeeper:v3
container_name: zoo3
ports:
- "2183:2181"
- "8083:8080"
volumes:
- /d/IT/docker/zookeeper/server3/conf:/usr/zookeeper/zookeeper-3.9/conf
- /d/IT/docker/zookeeper/server3/data:/usr/zookeeper/zookeeper-3.9/data
- /d/IT/docker/zookeeper/server3/logs:/usr/zookeeper/zookeeper-3.9/logs
depends_on:
- zoo2
networks:
- zookeeper
networks:
zookeeper:
external: true
这里我给每个 zookeeper 服务器都创建了一个文件夹,分别是 server1、server2、server3,里面的 conf 文件夹表示配置文件目录,data 文件夹表示存储快照目录,logs 文件夹表示日志文件目录。
在每个 conf 文件夹中创建 zoo.cfg 文件,其中 server1/conf/zoo.cfg 文件如下:
tickTime=2000
initLimit=5
syncLimit=2
dataDir=/usr/zookeeper/zookeeper-3.9/data
clientPort=2181
server.1=0.0.0.0:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
server2/conf/zoo.cfg 文件如下:
tickTime=2000
initLimit=5
syncLimit=2
dataDir=/usr/zookeeper/zookeeper-3.9/data
clientPort=2181
server.1=zoo1:2888:3888
server.2=0.0.0.0:2888:3888
server.3=zoo3:2888:3888
server3/conf/zoo.cfg 文件如下:
tickTime=2000
initLimit=5
syncLimit=2
dataDir=/usr/zookeeper/zookeeper-3.9/data
clientPort=2181
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=0.0.0.0:2888:3888
server.x 表示三个 zookeeper 服务器,它的值分三部分,其中第一部分表示 IP 地址,第二部分是一个端口号,表示 zookeeper 服务器之间交互的端口号,第三部分是一个端口号,表示 zookeeper 服务器之间选举时用的端口号。在 IP 地址部分,如果表示当前主机则用 0.0.0.0(别用 localhost),如果是其他的 zookeeper 服务器则用 docker-compose.yml 文件中 container_name 的值。
server.x 中的 x 用来标识服务器,server.x 中的 x 需要和 data/myid 文件内容一致,服务器启动时会到 data 目录下找到这个 myid 文件,然后就知道自己是 zoo.cfg 配置文件中的 server.x 列表中的哪台服务器了。
如在 server1/data/ 中创建 myid 文件,文件中的内容是 1;在 server2/data/ 中创建 myid 文件,文件中的内容是 2;在 server3/data/ 中创建 myid 文件,文件中的内容是 3。
一切准备就绪后,在 docker-compose.yml 所在目录下执行 docker compose up -d
启动整个项目。
因为我们映射了 AdminServer 的端口号到主机,所以可以用 AdminServer 来验证集群状态。在浏览器中访问 http://localhost:8081/commands
查看所有可用命令列表。
其中 voting_view 可以看到集群中参与投票的成员。
可以看到是三个,而且 IP 地址和 zoo.cfg 配置文件对的上。
参考:
zookeeper.apache.org/doc/r3.9.1