company_banner

В чем польза ZooKeeper для админов и разработчиков. Семинар в Яндексе

    Привет! Меня зовут Андрей Степачев. В конце прошлого года я выступил перед коллегами с небольшим рассказом о том, что такое ZooKeeper, и как его можно использовать. Доклад изначально был рассчитан на широкий круг аудитории и может быть полезен и разработчикам, и админам, желающим разобраться, как все это примерно работает.





    Начнем, пожалуй, с истории появления ZooKeeper. Сначала, как известно, в Google написали сервис Chubby для управления своими серверами и их конфигурацией. Заодно решили задачу с распределенными блокировками. Но у Chubby была одна особенность: для захвата локов необходимо открывать объект, потом закрывать. От этого страдала производительность. В Yahoo посчитали, что им нужен инструмент, при помощи которого они могли бы строить различные системы для конфигураций своих кластеров. Именно в этом основная цель ZooKeeper — хранение и управление конфигурациями определенных систем, а локи получились как побочный продукт. В итоге вся эта система была создана для построения различных примитивных синхронизаций клиентским кодом. В самом ZooKeeper явных понятий подобных очередям нет, все это реализуется на стороне клиентских библиотек.


    Стоит отметить, что протокол, используемый Zookeeper называется ZAB, ссылки на описания протокола приведены в конце статьи.




    Основа ZooKeeper — виртуальная файловая система, которая состоит из взаимосвязанных узлов, которые представляют собой совмещенное понятие файла и директории. Каждый узел этого дерева может одновременно хранить данные и иметь подчиненные узлы. Помимо этого в системе существует два типа нод: есть так называемые persistent-ноды, которые сохраняются на диск и никогда не пропадают, и есть эфемерные ноды, которые принадлежат какой-то конкретной сессии и существуют, пока существует она.





    На картинке буквами Р —обозначены клиенты. Они устанавливают сессии — активные соединения с ZooKeeper-сервером, в рамках которого происходят обмен heartbeat-пакетами. Если в течение одной трети от тайм-аута мы не услышали хартбита, по истечении двух третей тайм-аута, клиентская библиотека присоединится к другому ZooKeeper-серверу, пока сессия на сервере не успела пропасть. Если сессия пропадает, эфемерные ноды (на схеме обозначены синим) пропадают. У них обычно есть атрибут, который указывает, какая из сессий ими владеет. Такие узлы не могут иметь детей, это строго объект, в который можно сохранить какие-то данные, но нельзя сделать зависимые.



    Разработчики ZooKeeper посчитали, что очень удобно было бы иметь это все в виде файловой системы.



    Вторая базисная для ZooKeeper вещь — это так называемая синхронная реализация записи и FIFO-обработка сообщений. Идея заключается в том, что вся последовательность команд в ZooKeeper проходит строго упорядоченно т.е. данная система поддерживает total ordering.





    Все операции в ZooKeeper превращается в эту идемпотентную операцию. Если мы хотим изменить какую-то ноду, то мы создаем запись о том, что мы ее изменили, при этом запоминаем ту версию ноды, которая была и которая будет. За счет этого мы можем много раз получать одно и то же сообщение, при этом мы будем точно знать, в какой момент можно его применить. Соответственно, любые операции на запись осуществляются строго последовательно в одном потоке, в одном сервере (мастере). Есть лидер, который выбирается между несколькими машинками, и только он выполняет все операции на запись. Чтения могут происходить с реплики. При этом у клиента выполняется строгая последовательность его операций. Т.е. если он послал операцию на запись и на чтение, то сначала выполнится запись. Даже несмотря на то, что операцию чтения можно было бы выполнить не блокируясь, операция на чтение будет выполнена только после того как выполнена предыдущая операция на запись. За счет этого можно реализовывать предсказуемые системы асинхронной работы с ZooKeeper. Сама система в основном ориентирована на асинхронную работу. То что клиентские библиотеки реализуют синхронный интерфейс — это для удобства программиста. На самом деле, высокую производительность ZooKeeper обеспечивает именно при асинхронной работе, как обычно и бывает.



    Каким образом это все работает? Есть один лидер плюс несколько фолловеров. Изменения применяются с использованием двухфазного коммита. Оновное, на что ориентируется ZooKeeper — это то, что он работает по TСP плюс total ordering. В целом целом протокол ZAB (ZooKeeper Atomic Broadcast) — это упрощенная версия Паксоса, которая, как известно, переживает переупорядочивание сообщений, умеет с этим бороться. ZAB с этим бороться не умеет, он изначально ориентирован на полностью упорядоченный поток событий. Параллельной обработки нет, но часто она и не требуется, потому что система ориентирована больше на чтение, чем на запись.





    Например, у нас есть такой кластер, есть клиенты. Если сейчас клиенты сделают чтение они увидят то значение, которое сейчас сейчас видят фолловеры, считают, что сейчас значение у некого поля сейчас 1. Если мы в каком-то клиенте запишем 2, то фолловер выполнит операцию через лидера и получит в результате новое состояние.





    Лидеру для того чтобы запись считалась успешной, нужно, чтобы как минимум 2 из 3 машин подтвердили то, что они эти данные надежно сохранили. Представьте, что у нас вот тот фолловер, который с 1, сейчас, например в каком-нибудь Амстердаме или другом удаленном ДЦ. Он отстает от остальных фолловеров. Следовательно, локальные машины уже будут видеть 2, а тот удаленный фолловер, если клиент произведет с него чтение до того, как фолловер успеет догнать мастера, увидит отстающее значение. Для того чтобы ему прочитать правильное значение, нужно послать специальную команду, чтобы ZooKeeper принудительно синхронизировался с мастером. Т.е. как минимум на момент выполнения команды sync будет точно известно, что мы получили состояние достаточно свежее по отношению к мастеру. Это называется slow read — медленное чтение. Обычно мы читаем очень быстро, но если все будут пользоваться медленным чтением, то понятно, что весь кластер будут читать всегда с мастера. Соответственно, масштабирования не будет. Если мы читаем быстро, позволяем себе отставать, то мы можем масштабироваться на чтение довольно хорошо.



    Рецепты применения


    Управление конфигурацией


    Первое и самое основное применение — это управление конфигурацией. Записываем в Zookeper какую-то настройку, например, URL коннекта с базой или просто флажок, который запрещает или разрешает работу какому-то сервису внутри нашего кластера. Соответственно, участники кластера подписываются на раздел с конфигурацией и отслеживают ее модификации.





    Если они зафиксировали изменение, они могут его прочитать и как-то отреагировать на это. За счет т.н. эфемерных нод можно отслеживать, например, жива ли еще машинка. Или получить список активных машин. Если у нас есть набор воркеров, то мы можем зарегистрировать каждого из них. И в зукипере мы будем видеть все машины, которые реально на связи. За счет наличия таймстемпа последней модификации или списка сессий, мы можем даже предсказывать, насколько машинки у нас отстают. Если мы видим, что машина начинает приближаться к тайм-ауту, можно предпринимать какие-то действия.



    Рандеву


    Еще один вариант — так называемое рандеву. Идея заключается в том, что есть некий путь воркеров и дилер, который раздает им задачи.





    Мы не знаем, заранее, сколько у нас воркеров, они могут подключаться и отключаться, мы этот процесс не контролируем. Для этого можно создать каталог workers. И когда у нас появляется новый воркер, он регистрирует эфемерную ноду, которая будет сообщать о том, что воркер все еще жив, и, например, свой каталог, куда будут попадать задания для него. Лидер будет отслеживать каталог workers. Он заметит, если один из воркеров отвалится в какой-то момент. Обнаружив это он может, например, перенести задачи из queue к другому воркеру. Таким образом, мы можем построить на ZooKeeper несложную систему обработки заданий.



    Блокировки


    Допустим, пришел один клиент. Он создает эфемерную ноду. Тут нужно оговориться, что в ZooKeeper помимо обычных есть sequential-ноды. К ним в конце может приписываться некий атомарно растущий сиквенс.





    Второй клиент, приходя создает еще одну эфемерную ноду, получает список детей и отсортировав их, смотрит, является ли он первым. Он видит, что он не первый, вешает обработчик событий на первый и ждет, пока он исчезнет.





    Почему не нужно вешать на сам каталог my-lock и ждать когда там все исчезнет? Потому что, если у вас много машин, которые пытаются что-то заблокировать, то когда пропадет lock–0001, у вас будет шторм уведомлений. Мастер будет просто занят рассылкой уведомлений об одном конкретном локе. Поэтому лучше их сцеплять именно таким образом — друг за другом, чтобы они следили только за предыдущей нодой. Они выстраиваются в цепочку.





    Когда первый клиент отпускает лок, удаляет эту запись, второй клиент видит это и считает, что теперь он является владельцем лока, т.к. впереди никого нет. За счет сиквенса никто вперед него гарантированно не залезет. Соответственно, если первый клиент придет снова, он создаст ноду уже в конце цепочки.



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


    На графике ниже по оси x отображено отношение записи к чтению. Заметно, что с ростом количества операций записи в процентном отношении, производительность сильно проседает.





    Это результаты работы в асинхронном режиме. Т.е. операции шлются по 100. Если бы они слались по одной, числа по оси y нужно поделить на 100. На чтение можно расти лучше. В ZooKeeper помимо возможности построить кворум из, скажем, пяти машин, которые будут гарантировать сохранение данных, можно еще делать неголосующие машины, которые работают как репитеры: просто читают события, но сами в записи не участвуют. За счет этого и получается преимущество в записи.



    Servers 100% reads 0% reads
    13 460k 8k
    9 296k 12k
    7 257k 14k
    5 165k 18k
    3 87k 21k


    Таблица выше демонстрирует, как добавление серверов влияет на чтение и запись. Видно, что скорость чтения растет вместе с количеством, а если мы увеличиваем кворум на запись, производительность падает.



    На картинке ниже можно посмотреть, как ZooKeeper реагирует на различные сбои. Первый случай — выпадение реплики. Второй — выпадение реплики, к которой мы не присоединены.





    Падение лидера для зукипера условно критично. При хорошей сети ZooKeeper может восстановить его примерно за 200 мс. Иногда лидер может выбираться и несколько минут. И в этот момент если мы пытаемся захватить lock, любые попытки записи без активного лидера ни к чему не приведут, мы будем вынуждены ждать его появления.



    Задержки

    severs / workers 3 5 7 9
    1 776 748 758 711
    10 1831 1831 1572 1540
    20 2470 2336 1934 1890


    Время в таблице выше представлено в наносекундах. Соответственно по скорости чтения ZooKeeper приближается к in-memory-базам. Фактически это такой кэш, который всегда читает из памяти. Вообще у ZooKeeper база данных всегда находится в памяти. Запись происходит примерно так: ZooKeeper пишет лог событий, он в соответствии с настройками тика сбрасывается на диск и периодически система делает снэпшот всей базы. Соответственно, при слишком большой базе или слишком загруженных дисках задержки могут быть сравнимы с тайм-аутом. Этот тест моделирует создание некой конфигурации: у нас есть 1килобайт данных, сначала идет один синхронный create, потом асинхронный delete, все это повторяется 50 000 раз.



    Ссылки:


    ZAB протокол — оригинальный документ
    Анализ ZAB и его реализации в ZK, чуть более человеческим языком чем оригинал
    Яндекс
    473,05
    Как мы делаем Яндекс
    Поделиться публикацией

    Комментарии 22

      0
      Какое будет поведение в случае split brain? два лидера? или есть какие-то механизмы для обработки такой ситтуации?
        +3
        Split brain ситуации тут не может быть, т.к. Все операции выполняется только при наличии кворума — минимум половина машин плюс одна должны согласовать решение. Таким образом два мастера существовать не смогут, у одного будет недостаточно последователей для формирования кворума и выполнения операций.
          0
          я просто пытаюсь разобраться. То есть при четном количестве машин network split ровно пополам — приведет к отказу всего зоопарка?
            +1
            Кворум будет повышен до ближайшего нечетного вверх (N/2 + 1, например для 4 будет 4/2 + 1 = 3, т.е. столько же сколько и для 5, 5/2 + 1 = 3 )
              0
              это понятно, я о другом спрашивал, например 10 машин, network split на дву сети в каждой из которых по 5 машин. Ни в одной половине не будет N/2+1 то есть кворума (который как я понял — 6). вопросы --1) что будет с существующим лидером? 2) Возможна ли будет вообще запись в zookeeper? 3) чтение тоже требует кворума? если да — означает ли это что и чтение будет недоступно? 4) как это будет выглядеть с клиентской стороны — ошибки? разрыв соединений?
                +1
                Да, будет потеря кворума:
                1. ничего, он превратиться в кандидата и начнутся выборы
                2. нет, запись будет невозможна
                3. чтение никогда не требует кворума и чтение всегда с той машины, куда ты прицепился. для работы с таким развлившимся кворумом можно подключаться в readonly режиме.
                4. ошибки да (на память не помню что именно прилетит). Разрыв соединения zk не страшен, при подключении создается сессия, которая реплицирутся на все сервера кворума, по этому при переподключении клиент указывает свою сессию и с точки зрения клиентского кода происходит просто небольшая задержка.
        0
        Кипарис из YT не так пригоден для внедрения внутри Яндекса, как Zookeeper?
          +1
          Кипарис — отличная технология для хранения метаданных. И она тоже используется. Хотя и имела существенные отличия.
            0
            Не будет ли сильным нарушением NDA рассказать ключевые отличия Кипариса от Zookeeper'а и Etcd? Или лучше надеяться, что на YaC'е в этом году расскажут немного больше?
        +2
        Ну etcd мало что могу сказать, я его ни у кого в проде не видел. а по возможностям и по свойствам raft-а должно быть очень похоже на zk.
        Про YT думаю расскажут разработчики, правда я не знаю когда они это собираются делать.
          +1
          Да, похоже ровной в той же мере, как яблоко похоже на яблоню :)

          RAFT имеет ряд преимуществ:
          1) Академически доказан и реализован по теоретическим выкладкам (можно ли реализовать PAXOS прочтя алгоритм? Конечно же нет)
          2) Решительно проще, чем PAXOS и не требует костылей, чтобы он вел себя адекватно во всех случаях
          3) Etcd не содержит богоморезкой явы (зачем она в таком сугубо системном инструменте?)

          Иными словами, как раз в случае RAFT не нужно быть семь пядей во лбу, чтобы сказать, что произойдет с кластером в той или иной ситуации, а достаточно изучить и понять короткую пдфку.
            +1
            У raft изменения размеров кластера предусмотрено протоколом, у zk, кажется, такого не было.

            Также радует, что за счёт простоты raft'а есть воз и маленькая тележка его имплементаций и систем поверх него (всякие etcd, consul, hydrabase, influxdb).

            Те же etcd и consul довольно просты с т. з. клиента http (etcd и consul), dns и rpc (consul) против написание zk-enabled оберток для сервисов (особенно, когда они не на яве), хотя там тоже есть strong ordering, cas операции, сессии (для leader election), watch'и (через http long-polling).

            Правда, пока сложно сказать насколько оно production ready, всё молодые проекты.
              0
              Да, а еще референсная реализация на няшном Go вообще возносит raft на олимп :)

              Я искреннее уверен, что в скором будущем многие сервисы (тот же http, dns) станут намного более распределенными, но при этом очень управляемыми.

              DNS хоть и распределенный сервис by design, но, например, такая задача как замена мастера, когдау тебя сотни тысяч доменных зон — становится адом и кошмаром. Ну, конечно, есть те, кто используют SQL движки под DNS и думают, что решили проблему даже не достойны упоминания — это треш =)
              0
              А при чем тут paxos? Zab не есть paxos и точно так же довольно прост. Есть даже pdf, предлагаю сначала изучить предмет разговора поподробнее.
                0
                А я предлагаю сначала статью дополнить указанием на используемый ZK протокол.
                  +1
                  добавил, спасибо за дополнение.

                  PS: кстати, название протокола в статье есть.
            0
            В проде ZK используется достаточно давно.
              0
              Другая большая лекция по ЗуКиперу от сотрудников Яндекса: www.lektorium.tv/lecture/14880
              Насколько я помню, там поподробнее, для первого знакомства самое то!
                0
                Видно, что человек знает свою тему, но доклады ему вести не стоит. Посмотрел видео, советую лучше читать статью.
                  0
                  Я правильно понимаю, что Zookeeper решает примерно такие же задачи, как и микросовтовский Active Directory?
                    0
                    Думаю что нет. У них почти нет ничего общего.

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое