На прошлой неделе компания 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 создан для того, чтобы получать запросы приложений, отправленные в 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, автоматизированные обновления, бэкапы и другие возможности.
Автор: Флант