Хранение метрик: как мы перешли с Graphite+Whisper на Graphite+ClickHouse

    Всем привет! В своей прошлой статье я писал об организации модульной системы мониторинга для микросервисной архитектуры. Ничего не стоит на месте, наш проект постоянно растёт, и количество хранимых метрик — тоже. Как мы организовали переход с Graphite+Whisper на Graphite+ClickHouse в условиях высоких нагрузок, об ожиданиях от него и результатах миграции читайте под катом.



    Прежде чем я расскажу, как мы организовали переход от хранения метрик в Graphite+Whisper на Graphite+ClickHouse, хотелось бы дать информацию о причинах принятия подобного решения и о тех минусах Whisper, с которыми мы жили в течение продолжительного времени.


    Проблемы Graphite+Whisper


    1. Высокая нагрузка на дисковую подсистему


    На момент перехода к нам прилетало примерно 1.5 млн метрик в минуту. С таким потоком дисковая утилизация на серверах была равна ~30%. В целом это было вполне приемлемо — все работало стабильно, быстро писалось, быстро читалось… До того момента, пока одна из команд разработки не выкатила новую фичу и не стала отправлять нам 10 млн метрик в минуту. Вот тогда-то дисковая подсистема поднапряглась, и мы увидели 100% утилизации. Проблему удалось быстро решить, но осадочек остался.


    2. Отсутствие репликации и консистентности


    Скорее всего как и все, кто использует/использовал Graphite+Whisper, мы лили одинаковый поток метрик сразу на несколько серверов Graphite с целью создания отказоустойчивости. И с этим особых проблем не было — до момента, когда один из серверов по какой-либо причине не падал. Иногда мы успевали поднять упавший сервер достаточно быстро, и carbon-c-relay успевал заливать в него метрики из своего кэша, а иногда нет. И тогда в метриках была дыра, которую мы затягивали rsync`ом. Процедура была достаточно долгой. Спасало только то, что происходило подобное очень редко. Также мы периодически брали рандомный набор метрик и сравнивали их другими такими же на соседних нодах кластера. Примерно в 5% случаев несколько значений различались, что нас не очень радовало.


    3. Большой объем занимаемого места


    Так как мы пишем в Graphite не только инфраструктурные, но и бизнес-метрики (а теперь ещё и метрики из Kubernetes), то довольно часто получаем ситуацию, при которой в метрике присутствуют только несколько значений, а .wsp-файл создается с учетом всего retention периода, и занимает предвыделенный объем места, который у нас был равен ~2Мб. Проблема усугубляется ещё и тем, что подобных файлов со временем появляется очень много, и при построении отчетов по ним на чтение пустых точек уходит много времени и ресурсов.


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


    Имея все вышеперечисленное (с учетом предыдущей статьи), а также постоянный рост количества получаемых метрик, желание перевести все метрики к интервалу хранения в 30 сек. (при необходимости — до 10 сек.), мы решили попробовать Graphite+ClickHouse в качестве перспективной альтернативы Whisper.


    Graphite+ClickHouse. Ожидания


    Посетив несколько митапов ребят из Яндекса, прочитав пару статей на Хабре, прошерстив документацию и найдя вменяемые компоненты для обвязки ClickHouse под Graphite, мы решили действовать!


    Хотелось получить следующее:


    • снизить утилизацию дисковой подсистемы с 30% до 5%;
    • снизить объем занимаемого места с 1Тб до 100Гб;
    • иметь возможность принимать по 100 млн метрик в минуту в сервер;
    • репликацию данных и отказоустойчивость из коробки;
    • не сидеть над этим проектом год и сделать переход за какой-то вменяемый срок;
    • переключиться без даунтайма.

    Достаточно амбициозно, правда?


    Graphite+ClickHouse. Компоненты


    Для получения данных по протоколу Graphite и последующей записи их в ClickHouse, был выбран carbon-clickhouse (golang).


    В качестве базы данных для хранения временных рядов был выбран последний на тот момент релиз ClickHouse стабильной версии 1.1.54253. При работе с ним были проблемы: в логи сыпало гору ошибок, и было не совсем понятно что с ними делать. В обсуждении с Романом Ломоносовым (автор carbon-clickhouse, graphite-clickhouse и еще много-много чего) был выбран более старый релиз 1.1.54236. Ошибки пропали — все стало работать на ура.


    Для чтения данных из ClickHouse был выбран graphite-сlickhouse (golang). В качестве API-интерфейса для Graphite — carbonapi (golang). Для организации репликации между таблицами ClickHouse был использован zookeeper. Для маршрутизации метрик мы оставили нами горячо любимый carbon-c-relay (С) (см. прошлую статью).


    Graphite+ClickHouse. Структура таблиц


    “graphite” — база данных, созданная нами для таблиц мониторинга.


    “graphite.metrics” — таблица с движком ReplicatedReplacingMergeTree (реплицируемый ReplacingMergeTree). В данной таблице хранятся имена метрик и пути до них.


    CREATE TABLE graphite.metrics ( Date Date, Level UInt32, Path String, Deleted UInt8, Version UInt32 ) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/replicator/graphite.metrics', ‘r1’, Date, (Level, Path), 8192, Version);

    “graphite.data” — таблица с движком ReplicatedGraphiteMergeTree (реплицируемый GraphiteMergeTree). В данной таблице хранятся значения метрик.


    CREATE TABLE graphite.data ( Path String, Value Float64, Time UInt32, Date Date, Timestamp UInt32 ) ENGINE = ReplicatedGraphiteMergeTree('/clickhouse/tables/replicator/graphite.data', 'r1', Date, (Path, Time), 8192, 'graphite_rollup')

    “graphite.date_metrics” — таблица, заполняемая по условию, с движком ReplicatedReplacingMergeTree. В эту таблицу записываются имена всех метрики, которые встретились за сутки. Причины создания описаны в разделе «Проблемы» в конце этой статьи.


    CREATE MATERIALIZED VIEW graphite.date_metrics ( Path String,  Level UInt32,  Date Date) ENGINE = ReplicatedReplacingMergeTree('/clickhouse/tables/replicator/graphite.date_metrics', 'r1', Date, (Level, Path, Date), 8192) AS SELECT toUInt32(length(splitByChar('.', Path))) AS Level, Date, Path FROM graphite.data

    “graphite.data_stat” — таблица, заполняемая по условию, с движком ReplicatedAggregatingMergeTree (реплицируемый AggregatingMergeTree). В эту таблицу записывается количество входящих метрик, с разбивкой до 4 уровня вложенности.


    CREATE MATERIALIZED VIEW graphite.data_stat ( Date Date,  Prefix String,  Timestamp UInt32,  Count AggregateFunction(count)) ENGINE = ReplicatedAggregatingMergeTree('/clickhouse/tables/replicator/graphite.data_stat', 'r1', Date, (Timestamp, Prefix), 8192) AS SELECT toStartOfMonth(now()) AS Date, replaceRegexpOne(Path, '^([^.]+\\.[^.]+\\.[^.]+).*$', '\\1') AS Prefix, toUInt32(toStartOfMinute(toDateTime(Timestamp))) AS Timestamp, countState() AS Count FROM graphite.data  GROUP BY Timestamp, Prefix

    Graphite+ClickHouse. Схема взаимодействия компонентов



    Graphite+ClickHouse. Миграция данных


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


    • В carbon-c-relay добавили правило отправлять дополнительный поток метрик в carbon-clickhouse одного из серверов, участвующих в репликации ClickHouse таблиц.


    • Написали небольшой скрипт на python, который с помощью библиотеки whisper-dump вычитывал все .wsp-файлы из нашего хранилища и отправлял эти данные в вышеописанный carbon-clickhouse в 24 потока. Количество принимаемых значений метрик в carbon-clickhouse, достигало в 125 млн/мин., и ClickHouse даже не вспотел.


    • Создали отдельный DataSource в Grafana с целью отладки функций, использующихся в существующих дашбордах. Выявили список функций, которые мы использовали, но они не были реализованы в carbonapi. Дописали эти функции, и отправили PR`ы авторам carbonapi (отдельное им спасибо).


    • Для переключения читающей нагрузки в настройках балансировщиков изменили эндпоинты с graphite-api (API интерфейс для Graphite+Whisper) на carbonapi.

    Graphite+ClickHouse. Результаты


    • снизили утилизацию дисковой подсистемы с 30% до 1%;


    • снизили объем занимаемого места с 1 Тб до 300 Гб;
    • имеем возможность принимать по 125 млн метрик в минуту в сервер (пики в момент миграции);
    • перевели все метрики на тридцатисекундный интервал хранения;
    • получили репликацию данных и отказоустойчивость;
    • переключились без даунтайма;
    • на все потратили примерно 7 недель.

    Graphite+ClickHouse. Проблемы


    В нашем случае не обошлось и без подводных камней. Вот с чем мы столкнулись после перехода.


    1. ClickHouse не всегда перечитывает конфиги на лету, иногда его надо перезагружать. К примеру, в случае с описанием кластера zookeeper в конфиге ClickHouse — он не применялся до перезагрузки clickhouse-server.
    2. Не проходили большие запросы ClickHouse, поэтому у нас в graphite-clickhouse строка подключения к ClickHouse выглядит вот так:

      url = "http://localhost:8123/?max_query_size=268435456&max_ast_elements=1000000"
    3. В ClickHouse довольно часто выходят новые версии стабильных релизов, в них могут встретиться сюрпризы: будьте внимательны.
    4. Динамически создаваемые контейнеры в kubernetes отправляют большое количество метрик с коротким и случайным периодом жизни. Точек по таким метрикам не много, и проблем с местом нет. Но вот при построении запросов ClickHouse поднимает огромное количество этих самых метрик из таблицы ‘metrics’. В 90% случаев данные по ним за окно (24 часа) отсутствуют. А вот время на поиск этих данных в таблице ‘data’ затрачивается, и в конечном счете упирается в таймаут. Для того, чтобы решить эту проблему, мы стали вести отдельную вьюшку с информацией по метрикам, которые встретились за сутки. Таким образом, при построении отчетов (графиков) по динамически создаваемым контейнерам, мы опрашиваем только те метрики, которые встречались в пределах заданного окна, а не за всё время, что в разы ускорило построение отчетов по ним. Для вышеописанного решения был собран graphite-clickhouse (fork), включающий реализацию работы с таблицей date_metrics.

    Graphite+ClickHouse. Теги


    С версии 1.1.0 Graphite стал официально поддерживать теги. И мы активно думаем над тем, что и как надо сделать чтобы поддержать эту инициативу в стеке graphite+clickhouse.


    Graphite+ClickHouse. Детектор аномалий


    На базе описанной выше инфраструктуры, мы реализовали прототип детектора аномалий, и он работает! Но о нем — в следующей статье.


    Подписывайтесь, жмите стрелку вверх и будьте счастливы!

    Авито

    202,00

    У нас живут ваши объявления

    Поделиться публикацией
    Комментарии 33
      +1
      Доработки в рамках graphite-clickhouse (fork) будете обратно в graphite-clickhouse отправлять?
      Спасибо за статью
        +2
        Да будем, в самое ближайшее время.
        +1
        Здорово!
        А по сравнению с Забиксом так вообще сказка :)
          +3

          Шикарная картинка.

            0
            На какой версии Clickhouse летите сейчас?
              0
              54236, хотели обновиться до 54310: подняли тестовую среду — проверили не поломается ли репликация при постепенном обновлении (каждой ноды отдельно), все ок, проблем не обнаружили, но нас остановило это:
              github.com/yandex/ClickHouse/issues/1510#issuecomment-345839291
              как только решат — сразу обновимся.
                0
                Обновились до 54318 — полет нормальный!
              +3
              Раз у вас есть кубернетос, не рассматривали habrahabr.ru/company/flant/blog/341386 от наших соотечественников?
                0
                Мы, если что, всячески «за» взаимодействие по таким вопросам :-)
                  +2
                  Наша «облачная» команда, знает о существовании этого проекта. Я им передал вашу заинтересованность во взаимодействии.
                  0
                  Подскажите, как осуществляется генерализация метрик (если вообще осуществляется)?
                    0
                    Я, к сожалению не уверен что корректно понимаю вопрос, если мы говорим о каком то верхоуровневом обобщении, то в своей прошлой статье я рассказывал как у нас утроена схема хранения метрик и каким образом мы её используем.
                    0
                    Спасибо, интересно.
                    Последняя версия InfluxDB сильно отстает от производительности Clickhouse?
                      0
                      Можете задать этот вопрос в «Telegram», в канале «Церковь метрик», вам обязательно ответят!
                        +1
                        (сразу оговорюсь, я к авито никакого отношения не имею и мы просто делали свои тесты какое-то время назад, но недостаточно далеко продвинулись чтобы оформлять в виде статьи)
                        Смотря о какой производительности говорить. По опыту Clickhouse на графит-подобных метриках может держать 2.1кк точек в секунду достаточно долго (в тесте на одной из старых версий оставляли на пару недель).
                        У InfluxDB графитный ресивер написан отвратительно и больше 150к в секунду переварить не может в принципе.
                        Нативный получше, но там начинаются спецэффекты от самой базы — если держать нагрузку на запись близко к пиковой, то в момент merge'а прием данных останавливается. Поэтому стабильный рейт который в тестах мы у себя видели — где-то 300-400к точек в секунду.
                        Но дальше начинаются особенности — у InfluxDB кластеризация платная, у Clickhouse'а шардинг и репликация из коробки (пусть и не самые простые в обращении). При старте InfluxDB имеет свойство потреблять ресурсы как ни в себя на время реиндексации данных (TSM1 до сих пор не стабилен, а на момент тестирования просто ломал данные), Clickhouse же перезапускается относительно безболезненно (простой в пару минут не в счет) и так далее.
                        То есть с точки зрения стабильности работы нам InfluxDB не понравился настолько, что мы решили не пытаться его тестировать в ближайшие 3-4 релиза от слова совсем и решили не использовать у себя ни под какие задачи.
                          0
                          отлично, исчерпывающий ответ.
                          спасибо.
                        0
                        vkolobaev А вы когда данные мигрировали с whisper в clickhouse вы их как пересылали? Используя pickle или plain text protocol?
                          0
                          plain text
                            0
                            А если не сложно, сможете поделиться скриптом для миграции? И еще вопрос, а почему выбрали связку carbon-clickhouse+graphite-clickhouse, а не graphouse(https://github.com/yandex/graphouse)?
                              0
                              1. Скрипт был в контейнерах с виспером, и их больше нет с нами =(. Там все очень просто — вычитываем с помощью whisper-dump файлы .wsp и полученный результат, через сокет отправляем на порт carbon-clickhouse одной из нод. На bash, думаю, подобное можно за 5 минут написать.
                              2. graphouse — это java — а мы любим Go
                                0
                                Я опять же напомню, что к авито не имею отношения, но мы в общем-то тоже смотрели на graphouse, но решили его не шибко тестировать по следующим причинам:
                                1. У нас на фронтэнде go-graphite/carbonapi, а graphouse завязан на graphite-web/graphite-api. В наших тестах carbonapi значительно быстрее чем graphite-web/graphite-api (даже если их запускать на pypy)
                                2. На прием метрик он примерно в 4 раза хуже работает чем lomik/carbon-clickhouse. То есть жрет больше CPU и быстрее заканчивается по скорости


                                Сравнение справедливо на момент где-то полугодичной давности.
                                  0
                                  Да, но если я правильно понимаю, если использовать graphite-web/graphite-api к graphouse — то можно получить функциональность тегов graphite.readthedocs.io/en/latest/tags.html или нет?
                                    +2
                                    graphouse(Java) — это аналог carbon-clickhouse(GoLang), carbon-clickhouse умеет теги(запись)
                                    graphite-web(Python) — это аналог carbonapi(GoLang), и он значительно медленнее обрабатывает запросы, но в нем реализованы теги. В carbonapi, пока, теги мы реализовали только в своем форке, в мастере они пока не поддерживаются.
                                      0
                                      Т.е. писать теги carbon-clickhouse уже может, а carbonapi читать их еще не может?

                                      Если не принимать скорость работы во внимание, graphite-web может быть заменой carbonapi, которая уже умеет читать теги? И совместим ли graphite-web с graphite-clickhouse?

                                      А что с Grafana? Она уже может работать с тегами?
                                        +2
                                        Писать теги carbon-clickhouse уже может :: Да
                                        carbonapi читать их еще не может :: официальный не может
                                        graphite-web — умеет читать теги, умеет работать с graphite-clickhouse, может быть заменой carbonapi
                                        Grafana — общается с графитом через API(graphite-web/carbonapi) — то есть она поддерживает теги, да.
                                          0
                                          Спасибо, конструктивно.
                          0

                          Небольшая часть стека все же на Java ;)
                          ZooKeeper — не тестировали замену в виде Zetcd (https://github.com/coreos/zetcd) ?

                            0
                            На сколько мне известно, КХ поддерживает работу только с Zookeeper — мы бы с радостью ушли на какой нить Консул, но увы.
                              0
                              К сожалению баги в Zookeeper клиенте Clickhouse до сих пор не починены на эту тему

                              github.com/yandex/ClickHouse/issues/777
                              github.com/Slach/clickhouse-zetcd

                              если бы кто нибудь грамотный мог мне помочь понять, почему именно валится Clickhouse при повторной вставке блоков и что не так делает zetcd
                              было бы здорово
                            0

                            рассматривали ли в качестве бекенда graphite (или фронтенда КХ, тут уж как посмотреть) https://github.com/yandex/graphouse и сравнивали ли? Если да, то каковы результаты сравнения?
                            Интересуюсь с практической точки зрения. С графаусом работал и в его производительности уверен. И скоро надо будет переводить местный графит… вот и думаю

                              0
                              Увидел выше ответ про «любим», и всё же интересует, были ли сравнительные тесты
                                0
                                У меня были очень давно, был хуже раза в 3 на запись. Меня впрочем по-прежнему волнует чтение, а graphite-web/graphite-api будет очень плохо рабоать под нашими нагрузками.
                                Так как в графаусе не появилось поддержки нашего протокола, то я его больше не тестировал.
                              +1
                              Очень очень очень было бы интересно увидеть детектор аномалий!
                              Пожалуйста, выложите его, просто banshee, skyline, morgoth хорошие попытки =(( но у меня не взлетело

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

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