Сервис Apache ZooKeeper применяется в качестве сервера метаданных и конфигураций сервисов и часто используется для реализации механизма поиска сервисов (Service Discovery). Например, в Apache Kafka, популярном инструменте для организации обмена сообщениями в распределенных системах, до версии 4.0 ZooKeeper может использоваться в качестве хранилища метаданных кластера. Как и любой другой компонент распределённой системы ZooKeeper желательно запускать в кластере для обеспечения отказоустойчивости системы, чему посвящена данная статья.
📖 Читать на сайте 📖 Читать в VK 📖 Читать в Telegraph
Я преднамеренно вынес тему запуска ZooKeeper в отдельную статью, так как из Apache Kafka поддержка ZooKeeper будет удалена с релизом версии 4.0, но ZooKeeper может использоваться и в других ваших проектах.
Установка и запуск ZooKeeper
Для начала работы с ZooKeeper нужно скачать дистрибутив с официального сайта проекта. Скачайте и распакуйте архив с ZooKeeper в любую удобную для вас директорию. Для запуска достаточно скопировать шаблон файла настроек conf/zoo_sample.cfg в conf/zoo.cfg и выполнить следующую команду:
$ bin/zkServer.sh start
ZooKeeper JMX enabled by default
Using config: /home/akosarev/opt/apache-zookeeper-3.8.4-bin/bin/../conf/zoo.cfg
Starting zookeeper ... STARTED
По умолчанию в ZooKeeper включён интерфейс администратора, и после запуска вы можете открыть в браузере страницу http://localhost:8080/commands для доступа к командам.
Если посмотреть на файл конфигурации, то он достаточно простой:
# Параметры синхронизации
tickTime=2000
initLimit=10
syncLimit=5
# Директория данных ZooKeeper
dataDir=/tmp/zookeeper
# Порт для подключения клиентов
clientPort=2181
Локальный кластер ZooKeeper
Теперь мы можем попробовать запустить локальный кластер из трёх экземпляров ZooKeeper. Для этого нужно выполнить следующие шаги:
- Создать директорию данных для каждого сервиса
- В директории данных каждого сервиса создать файл myid с уникальным идентификатором сервиса (от 0 до 255)
- Создать копию директории conf для каждого сервиса и добавить в zoo.cfg список сервисов, входящих в кластер
Описание сервиса выглядит следующим образом: server.{id}={host}:{quorum_port}:{leader_election_port}{;client_port}, ниже приведён для наглядности файл настроек первого сервиса:
conf/i1/zoo.cfg
# Параметры синхронизации
tickTime=2000
initLimit=10
syncLimit=5
# Директория данных ZooKeeper
dataDir=/tmp/zookeeper
# Порт для подключения клиентов
clientPort=2181
# Отключим страницу администратора, чтобы не занимать порт 8080
admin.enableServer=false
# Включение сервисных команды stat и srvr
4lw.commands.whitelist=stat,srvr
# Участники кворума
server.1=localhost:2888:3888
server.2=localhost:12888:13888
server.3=localhost:22888:23888
Порт для клиентских соединений можно указывать как в свойстве clientPort, так и в конце server, отделяя его при помощи ;. Сервис использует запись server со своим идентификатором для определения, какие порты он должен прослушивать для обмена данными кворума (2888) и выбора лидера (3888). Остальные записи server используются для взаимодействия с другими сервисами.
Конечно же, узлы кластера лучше разворачивать на отдельных физических или виртуальных серверах, а в последнем случае желательно, чтобы виртуальные серверы были расположены на разных физических.
Файлы настроек двух остальных сервисов будут аналогичны:
conf/i2/zoo.cfg
# Параметры синхронизации
tickTime=2000
initLimit=10
syncLimit=5
# Директория данных ZooKeeper
dataDir=/tmp/zookeeper-2
# Порт для подключения клиентов
clientPort=12181
# Отключим страницу администратора, чтобы не занимать порт 8080
admin.enableServer=false
# Включение сервисных команды stat и srvr
4lw.commands.whitelist=stat,srvr
# Участники кворума
server.1=localhost:2888:3888
server.2=localhost:12888:13888
server.3=localhost:22888:23888
conf/i3/zoo.cfg
# Параметры синхронизации
tickTime=2000
initLimit=10
syncLimit=5
# Директория данных ZooKeeper
dataDir=/tmp/zookeeper-3
# Порт для подключения клиентов
clientPort=22181
# Отключим страницу администратора, чтобы не занимать порт 8080
admin.enableServer=false
# Включение сервисных команды stat и srvr
4lw.commands.whitelist=stat,srvr
# Участники кворума
server.1=localhost:2888:3888
server.2=localhost:12888:13888
server.3=localhost:22888:23888
Теперь нужно запустить все три экземпляра командами:
$ bin/zkServer.sh --config conf/i1 start
$ bin/zkServer.sh --config conf/i2 start
$ bin/zkServer.sh --config conf/i3 start
Теперь можно проверить состояние участников кворума, отправив команды stat или srvr при помощи nc или telnet:
$ echo stat | nc localhost 22181
Zookeeper version: 3.8.4-9316c2a7a97e1666d8f4593f34dd6fc36ecc436c, built on 2024-02-12 22:16 UTC
Clients:
/127.0.0.1:54518[0](queued=0,recved=1,sent=0)
Latency min/avg/max: 0/0.0/0
Received: 4
Sent: 3
Connections: 1
Outstanding: 0
Zxid: 0x20000000a
Mode: follower
Node count: 39
Сервис, являющийся лидером кворума, в Mode будет иметь значение leader, остальные — follower.
Кластер ZooKeeper в Docker
Для запуска ZooKeeper в контейнерах доступен официальный образ zookeeper. Настроить сервис можно как файлом свойств, который должен быть смонтирован в /conf/zoo.cfg, так и при помощи переменных окружения.
Переменные окружения начинаются с префикса ZOO_ и повторяют свойства из файла свойств в верхнем регистре и знаком _ для разделения слов, например 4lw.commands.whitelist соответствует переменная окружения ZOO_4LW_COMMANDS_WHITELIST. Идентификатор сервиса указывается при помощи переменной ZOO_MY_ID, а список сервисов — при помощи ZOO_SERVERS.
Пример запуска кворума ZooKeeper из трёх экземпляров в Docker:
$ docker run -d -p 2181:2181 -p 2888:2888 -p 3888:3888 \
-e ZOO_MY_ID=1 \
-e ZOO_SERVERS='server.1=0.0.0.0:2888:3888;2181 server.2=172.17.0.1:12888:13888 server.3=172.17.0.1:22888:23888' \
-e ZOO_4LW_COMMANDS_WHITELIST=srvr,stat \
zookeeper
$ docker run -d -p 12181:2181 -p 12888:2888 -p 13888:3888 \
-e ZOO_MY_ID=2 \
-e ZOO_SERVERS='server.1=172.17.0.1:2888:3888 server.2=0.0.0.0:2888:3888;2181 server.3=172.17.0.1:22888:23888' \
-e ZOO_4LW_COMMANDS_WHITELIST=srvr,stat \
zookeeper
$ docker run -d -p 22181:2181 -p 22888:2888 -p 23888:3888 \
-e ZOO_MY_ID=3 \
-e ZOO_SERVERS='server.1=172.17.0.1:2888:3888 server.2=172.17.0.1:12888:13888 server.3=0.0.0.0:2888:3888;2181' \
-e ZOO_4LW_COMMANDS_WHITELIST=srvr,stat \
zookeeper
Для межсервисного взаимодействия я использовал в этом примере ip-адрес хост-системы 172.17.0.1, если у вас он отличается, то замените адрес на свой.
В Docker Compose кворум ZooKeeper будет выглядеть следующим образом:
services:
zk1:
image: zookeeper
hostname: zk1
ports:
- "2181:2181"
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zk1:2888:3888;2181 server.2=zk2:2888:3888 server.3=zk3:2888:3888
ZOO_4LW_COMMANDS_WHITELIST: srvr,stat
zk2:
image: zookeeper
hostname: zk2
ports:
- "12181:2181"
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zk1:2888:3888 server.2=zk2:2888:3888;2181 server.3=zk3:2888:3888
ZOO_4LW_COMMANDS_WHITELIST: srvr,stat
zk3:
image: zookeeper
hostname: zk3
ports:
- "22181:2181"
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zk1:2888:3888 server.2=zk2:2888:3888 server.3=zk3:2888:3888;2181
ZOO_4LW_COMMANDS_WHITELIST: srvr,stat
Поддержать: 💵 В VK | 💵 В Boosty | 💵 Через T | 💵 ЮМани 5599 0050 8286 9475