zetcd от CoreOS: Заменяя ZooKeeper на… хранилище etcd +20


На прошлой неделе компания CoreOS порадовала очередным Open Source-проектом — zetcd. На самом деле о нём было известно ещё с прошлого года, но теперь состоялся первый релиз, который перевёл продукт в статус бета-тестирования — заявил о готовности продукта к серьёзным испытаниям перед выпуском в мир production. Авторы позиционируют zetcd как готовую замену для ZooKeeper внутри таких распределённых/кластерных решений, как Mesos, Apache Kafka и Apache Drill. Их настрою не препятствует даже тот факт, что etcd предлагает «плоское» хранение ключей-значений против иерархического подхода своего конкурента. Как они к этому пришли?


Предыстория: Apache ZooKeeper


Вряд ли Apache ZooKeeper нуждается в особом представлении, но тем не менее: это написанное на Java хранилище ключей-значений (KV). Оно отличается иерархическим пространством имён и ориентированностью на применение в распределённых приложениях, где выступает в качестве централизованной службы для хранения конфигураций и другой служебной информации.

Как утверждают авторы ZooKeeper, этот проект появился (изначально, кстати, внутри Hadoop) как ответ на многократные попытки разработчиков распределённых систем создать свои подобные хранилища, что неизменно приводило к новым вызовам и сложностям в поддержке. И надо признать, что за десять лет своего существования ZooKeeper удалось хорошо закрепиться на рынке, став важным компонентом для таких Open Source-проектов, как Apache Drill, Apache Hadoop YARN, Apache HBase, Apache Kafka, Apache Solr, Mesos, Norbert. (Кстати, есть и примеры тех, кто в итоге отказался от ZooKeeper: Juju и Neo4j.) Разумеется, ZooKeeper выбрали и некоторые разработчики закрытых систем и приложений, которым требовалось распределённое KV-хранилище. Наконец, это решение стало популярным в мире микросервисов, где используется ещё и как служба обнаружения сервисов, хоть тут не обходится без проблем.

Причём здесь etcd?


Конечно, Apache ZooKeeper не является единственным решением в своей нише. Есть ещё, например, Consul, предлагающий KV-хранилище и делающий особый акцент на упомянутый Service Discovery, а также Doozer, ориентированный на высокую доступность (в ущерб масштабируемости и производительности), и главный «персонаж» этой статьи — etcd. (Для интересующихся в разнице этих решений могут быть полезны статьи «Consul vs. ZooKeeper, doozerd, etcd» по версии HashiCorp, «Exploring Performance of etcd, Zookeeper and Consul Consistent Key-value Datastores» по версии CoreOS.)

Хранилище etcd было создано в CoreOS для критически важных данных в распределённых системах, написано на языке Go и стремится обеспечить: а) простоту (простой API, доступный через gRPC), б) безопасность (автоматический TLS), в) производительность (авторы обещают 10000 операций записи в секунду), г) надёжность (благодаря алгоритму конcенсуса Raft). Всё это, приправленное интересами и энтузиазмом CoreOS, позволило новому решению набрать определённый вес: достаточно упомянуть, что etcd стал постоянным хранилищем объектов REST API в Kubernetes.

Дальнейший ход CoreOS не вызывает удивления: если многие в мире пользуются ZooKeeper, а у компании есть интерес продвигать свой конкурирующий продукт, то почему бы не сделать миграцию настолько простой, насколько это вообще возможно… А раз модель данных и клиентский протокол etcd отличаются от используемого в ZooKeeper и не будут изменяться ради этой совместимости, то решение очевидно — выпустить прослойку, которая перенаправит все запросы приложений, обращённые к ZooKeeper, в кластер etcd. Благо, «достаточно выразительный API в etcd v3 позволил эмулировать модель данных ZooKeeper на клиентской стороне с помощью обычного прокси». Удобство для разработчиков заключается в том, что модифицировать приложения вообще не требуется: достаточно заменить инсталляцию ZooKeeper на etcd и добавить перед ней новый прокси-сервер — тот самый zetcd.

Основы zetcd


Разобраться в его устройстве поможет анонс проекта и небольшие практические эксперименты. Итак, zetcd (написанный на языке Go, как и сам etcd) создан для того, чтобы получать запросы приложений, отправленные в ZooKeeper, и преобразовывать их в операции, соответствующие модели данных etcd и исполняемые в этом хранилище.



Для запуска zetcd требуется лишь наличие компилятора Go. Установка etcd и zetcd:

$ go get github.com/coreos/etcd/cmd/etcd
$ go get github.com/coreos/zetcd/cmd/zetcd

Примечание: по опыту инсталляций на разных версиях Ubuntu — лучше всего (для установки последних версий всех приложений из Git-репозиториев CoreOS) иметь установленным Go 1.8, который для 16.04 можно взять из ppa:longsleep/golang-backports.

Запуск etcd (по умолчанию биндится к localhost:2379) и zetcd:

$ etcd &
$ zetcd -zkaddr localhost:2181 -endpoints localhost:2379 &

Установка zkctl (CLI-утилита для выполнения простых операций в ZooKeeper) и пробные команды:

$ go install github.com/coreos/zetcd/cmd/zkctl
$ zkctl watch / &
$ zkctl create /abc "test"
$ zkctl get /abc
2017/05/23 21:57:44 Connected to 127.0.0.1:2181
2017/05/23 21:57:44 Authenticated: id=7587822333160445481, timeout=1000
[116 101 115 116]
Stat:
&{Czxid:3 Mzxid:14 Ctime:1495550621626193 Mtime:1495551461984432 Version:1 Cversion:1 Aversion:0 EphemeralOwner:0 DataLength:4 NumChildren:1 Pzxid:7}

Очевидно, был создан новый узел в корне дерева ZooKeeper (abc), с которым ассоциировали строку test (116 101 115 116 — номера юникодных символов, подтверждающие случившееся). Но это терминология ZooKeeper, а что произошло в etcd?

Внутреннее устройство zetcd


Для ответа на этот вопрос надо лучше разобраться в том, как же zetcd преобразует иерархическую модель данных ZooKeeper в формат, понятный для etcd. Древовидная структура ZooKeeper укладывается в плоское KV-пространство таким образом, что на каждую запись создаются KV-пары с метаданными, ключами которых является полный путь, содержащий также название параметра и уровень вложенности: /zk/[param]/[depth]/[full path]. Таким образом, просмотру ключей по директориям в ZooKeeper (getChildren) соответствует просмотр списка ключей по интервалу в etcd (Range), а внутри самой etcd хранятся дополнительные сведения, формирующие полное представление ZNode:



То есть для каждого ключа из ZooKeeper хранятся метаданные о его ревизии, версии и правах доступа. Они упрощены по сравнению с оригинальным ZNode из-за специфики etcd (отсутствие директорий в дереве как таковых, ролевая аутентификация вместо ACL и т.п.), однако полностью описывают узел. Например:

  • /zk/key/001/abc — хранит значение ключа /abc (первый уровень вложенности в дереве ZooKeeper);
  • /zk/mtime/002/abc/xyz — время модификации ключа /abc/xyz (второй уровень вложенности).

Проверить физическую организацию данных в действии можно с помощью консольного клиента etcdctl:

$ go get github.com/coreos/etcd/etcdctl
$ export ETCDCTL_API=3
$ etcdctl get --prefix /zk
/zk/acl/001/abc
??????-??ACL??PermsScheme
                         ID
                           ??>worldanyone
/zk/count/001/abc

/zk/ctime/001/abc
??????
/zk/cver/001/

/zk/cver/001/abc

/zk/err-node
1
/zk/key/001/abc
test
/zk/mtime/001/abc
??????
/zk/ver/001/abc

Примечание: если вы хотите повторить последнюю операцию, чтобы своими глазами увидеть, как данные ZNode хранятся в etcd, то собирать zetcd необходимо с -tags path_debug (т.е. go get -u -v -tags path_debug github.com/coreos/zetcd/cmd/zetcd), а также вам потребуется версия из Git-репозитория, вобравшая в себя свежий Pull Request #54, созданный в результате моего любопытства в Issue #52).

Производительность zetcd


Считая производительность не первой необходимостью, но важной составляющей успеха для zetcd, разработчики позаботились о создании простой benchmark-утилиты — zkboom. С ней в CoreOS сравнили показатели ZooKeeper 3.4.10 и dev-ветви etcd при записи и чтении 128-байтных KV-пар, ограничивая число запросов до 2500 в секунду и наращивая количество одновременных клиентов. Всё это производилось на «двух современных Linux-машинах, соединённых гигабитным свитчем; на одной были запущены прокси и серверный софт, а другая генерировала клиентские запросы».



Как видно, в создании ключей задержка zetcd достигает в ~2—3 раза худших значений, чем у ZooKeeper, а в чтении — в ~1,5 раза. Нельзя сказать, что это грандиозные результаты (скорее — вполне объяснимые, учитывая необходимость работать со множеством дополнительных ключей для эмуляции метаданных ZNode), но и совсем плохими, учитывая специфику назначения прокси-сервера, их не назовёшь.

К слову, со связкой etcd и zetcd уже должны работать и другие утилиты, созданные для тестирования производительности самого ZooKeeper, что открывает большое поле для самостоятельных экспериментов и выводов.

Заключение


Первый публичный релиз zetcd — v0.0.1 — состоялся на прошлой неделе. По мнению авторов, даже при том, что остаётся место для улучшений в производительности zetcd, в скором времени продукт сможет стать готовой заменой для ZooKeeper в различных применениях, к которым помимо известных проектов можно отнести и разработки категории in-house, авторы которых желают по каким-либо причинам уйти с ZooKeeper и сделать это «малой кровью». Наконец, интересная особенность такой замены появляется в контексте применения etcd Operator для Kubernetes, добавляющего этому хранилищу, совместимому с ZooKeeper, автоматизированные обновления, бэкапы и другие возможности.
-->


К сожалению, не доступен сервер mySQL