zookeeper -- 搭建单机和集群环境

187 阅读4分钟

这篇文章需要你有一定的 zookeeper 知识

zookeeper 部署有两种模式:单机模式和集群模式。

单机模式

在 linux 服务器上解压 zookeeper 的压缩包,然后在安装目录创建一个 data 目录,用来配置 dataDir 配置项。

image.png

把 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=2181server.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=2181server.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=2181server.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 查看所有可用命令列表。

image.png

其中 voting_view 可以看到集群中参与投票的成员。

image.png

可以看到是三个,而且 IP 地址和 zoo.cfg 配置文件对的上。

参考: zookeeper.apache.org/doc/r3.9.1