Мы два года развивали свою систему мониторинга. Кликай, чтобы…

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

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

wyzdu6fyb-3ehjdbzwi8jgxhvec.png

Но давайте начнем по порядку.

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

4e9d52d3e19df15d928d29d2394095fa.jpg

С того момента произошло следующее.


  • Количество серверов в кластере Graphite выросло с 3 до 6.
    (56 CPU 2.60GHz, 384GB RAM, 10 SSD SAS 745GB, Raid 6, 10GBit/s Net).


  • Мы заменили brubeck на bioyino — нашу собственную имплементацию StatsD на Rust, и даже целую статью про это написали. Правда, после выхода статьи мы подвезли в него поддержку тегов (Graphite) и Raft для выбора лидера.


  • Мы проработали возможность использовать bioyino в качестве StatsD-агента и разместили такие агенты рядом с инстансами монолита, а также там, где это было необходимо в k8s.


  • Мы наконец-то избавились от старой системы мониторинга Munin (формально она у нас ещё есть, но её данные уже нигде не используются).


  • Сбор данных из кластеров Kubernetes был организован через Prometheus/Federations, так как Heapster в новых версиях Kubernetes не поддержали.


За два прошедших года количество принимаемых и обрабатываемых метрик выросло примерно в 9 раз.

r2yuzm1rt5ok64zh2utwrpdtzbk.png

Процент занимаемого места на серверах также неумолимо ползёт вверх, и мы предпринимаем различные шаги по его понижению. Это хорошо видно на графике.

s2ujmlqnrsoo0yb0kgtxkdcf4ek.png

Что именно мы делаем?

10 10 10 * * clickhouse-client -q "select distinct partition from system.parts where active=1 and database='graphite' and table='data' and max_date between today()-55 AND today()-35;" | while read PART; do clickhouse-client -u systemXXX --password XXXXXXX -q "OPTIMIZE TABLE graphite.data PARTITION ('"$PART"') FINAL";done


  • Мы расшардировали таблицы с данными. Теперь у нас три шарда по две реплики в каждом с ключом шардирования по хэшу от имени метрики. Такой подход дает нам возможность производить rollup процедуры, так как все значения по конкретной метрике находятся в пределах одного шарда, а дисковое пространство на всех шардах утилизируется равномерно.

Схема Distributed таблицы выглядит следующим образом.

CREATE TABLE graphite.data_all (
`Path` String, 
`Value` Float64, 
`Time` UInt32, 
`Date` Date, 
`Timestamp` UInt32
) 
ENGINE = Distributed (
'graphite_cluster',
'graphite',
'data', 
jumpConsistentHash(cityHash64(Path), 3)
) 

Мы также назначили пользователю «default» readonly-права и перекинули выполнение процедур записи в таблицы на отдельного пользователя systemXXX.

Конфигурация кластера Graphite в ClickHouse выглядит следующим образом.

    
        
            
                true
                
                    graphite-clickhouse01
                    9000
                    systemXXX
                    XXXXXX
                
                
                    graphite-clickhouse04
                    9000
                    systemXXX
                    XXXXXX
                
            
            
                true
                
                    graphite-clickhouse02
                    9000
                    systemXXX
                    XXXXXX
                
                
                    graphite-clickhouse05
                    9000
                    systemXXX
                    XXXXXX
                
            
            
                true
                
                    graphite-clickhouse03
                    9000
                    systemXXX
                    XXXXXX
                
                
                    graphite-clickhouse06
                    9000
                    systemXXX
                    XXXXXX
                
            
        
    

Помимо нагрузки на запись, выросло количество запросов на чтение данных из Graphite. Эти данные используются для:


  • обработки триггеров и формирования алертов;
  • отображения графиков на мониторах в офисе и экранах ноутбуков и ПК растущего числа сотрудников компании.

chrbwhdiyl3x3cxjqmwgtohph6y.png

Чтобы мониторинг не утонул под этой нагрузкой, мы использовали ещё один хак: данные за последние два дня мы храним в отдельной «маленькой» табличке, и все читающие запросы за последние два дня мы отправляем туда, снижая нагрузку на основную шардированную таблицу. Так же для этой «маленькой» таблички мы применили реверсивную схему хранения метрик, что значительно ускорило поиск находящихся в ней данных и организовали для неё дневное партиционирование. Cхема этой таблички выглядит следующим образом.

CREATE TABLE graphite.data_reverse (
`Path` String, 
`Value` Float64, 
`Time` UInt32 CODEC(Delta(4), ZSTD(1)),
 `Date` Date, 
`Timestamp` UInt32
) 
ENGINE = ReplicatedGraphiteMergeTree (
'/clickhouse/tables/{cluster}/data_reverse', 
'{replica}', 
'graphite_rollup'
) PARTITION BY Date 
ORDER BY (Path, Time) 
SETTINGS index_granularity = 4096

Чтобы направлять в неё данные, мы добавили в конфигурационный файл приложения carbon-clickhouse новую секцию.

[upload.graphite_reverse]
type = "points-reverse"
table = "graphite.data_reverse"
threads = 2
url = "http://systemXXX:XXXXXXX@localhost:8123/"
timeout = "60s"
cache-ttl = "6h0m0s"
zero-timestamp = true

Чтобы удалять партиции старше двух дней, мы написали задание cron. Оно выглядит примерно так.

1 12 * * * clickhouse-client -q "select distinct partition from system.parts where active=1 and database='graphite' and table='data_reverse' and max_date

Чтобы читать из таблицы данные, в конфигурационном файле graphite-clickhouse добавили секцию:

[[data-table]]
table = "graphite.data_reverse"
max-age = "48h"
reverse = true

В результате мы имеем таблицу с 100% данных, реплицированных на все шесть серверов, которые обрабатывают всю читающую нагрузку от запросов с окном менее двух суток (а таких у нас 95%). А также мы имеем шардированную таблицу с ⅓ данных на каждом шарде, обеспечивающую чтение всех исторических данных. И пусть таких запросов кратно меньше, нагрузка от них значительно выше.

Что же происходит с CPU?! В результате роста объемов записываемых и читаемых данных в кластере Graphite, выросла и суммарная CPU-нагрузка на серверы. Выглядит она примерно так.

_umgm3dd9lgetkopu45jjrqqodw.png

Хочется обратить внимание на следующий нюанс: половина CPU уходит на парсинг и первичную обработку метрик в carbon-c-realy (v3.2 от 2018–09–05, отвечает за транспорт метрик), который размещен на трёх серверах из шести. Как видно по графику, именно эти три сервера и находятся в ТОПе.

В качестве системы алертинга у нас по-прежнему Moira и написанный под неё moira-client. Для гибкого управления триггерами, нотификациями и эскалациями, мы используем декларативное описание, которое назвали alert.yaml. Оно генерируется автоматически при создании сервиса через PaaS (подробнее об этом можно почитать в статье Вадима Мадисона «Что мы знаем о микросервисах») и размещается в его репозитории. Для работы с alert.yaml мы сделали обвязку над moira-client и назвали её alert-autoconf (планируем заопенсорсить). В сборке сервиса в TeamCity есть шаг с экспортом триггеров и нотификаций в Moira через alert-autoconf. При коммите изменений в alert.yaml запускаются автотесты, которые проверяют валидность yaml-файла, а также делают запросы в Graphite по каждому шаблону метрик с целью проверить их корректность.

Для инфраструктурных команд, не использующих PaaS, мы организовали отдельный репозиторий под названием Alerting. В нем сделали структуру вида: Команда/Проект/alert.yaml. К каждому alert.yaml мы генерируем отдельную сборку в TeamCity, которая прогоняет тесты и пушит содержимое alert.yaml в Moira.

Таким образом все наши сотрудники могут управлять своими триггерами, нотификациями и эскалациями, используя единый подход.

Так как раньше у нас уже были триггеры, заведённые через GUI, мы реализовали возможность выгрузить их в формате yaml. Содержимое полученного yaml-документа можно вставить в alert.yaml практически без дополнительных преобразований, после чего запушить изменения в мастер. В ходе сборки alert-autoconf поймет, что такой триггер уже существует и зарегистрирует его в нашем реестре в Redis.

А ещё не так давно мы обзавелись дежурной сменой инженеров 24×7. Для того, чтобы передать триггеры им на обслуживание, достаточно в своем alert.yaml корректно заполнить описание «что делать, если ты это видишь», поставить тег [24×7] и запушить изменения в мастер. После раскатки alert.yaml’а все описанные в нем триггеры автоматически попадут под круглосуточное наблюдение смены 24×7. У — Упрощай! Красота!

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


  1. Вместо выбора лидера через Consul используется встроенный Raft.
  2. Корректно обрабатываются теги в формате Graphite.
  3. Появилась возможность использовать bioyino (StatsD-server) в качестве агента.
  4. Для подсчета уникальных значений поддерживается формат «set».
  5. Финальную агрегацию метрик можно делать в несколько потоков.
  6. Данные можно отправлять в Graphite чанками в несколько параллельных соединений.
  7. Исправлены все найденные баги.

Сейчас это работает так.

Мы стали активно внедрять StatsD-агенты рядом со всеми крупными крупными генераторами метрик: в контейнерах с инстансами монолита, в подах k8s рядом с сервисами, на хостах с инфраструктурными компонентами и т. д.
Statsd-агент размещается рядом с приложением. Он принимает метрики от этого приложения всё так же по UDP, но уже не используя сетевую подсистему (за счёт оптимизаций в ядре Linux). Все события предагрегируются, и собранные данные ежесекундно (интервал можно настроить) отправляются в основной кластер серверов StatsD (bioyino0[1–3]) в формате Cap«n Proto.

Дальнейшая обработка и агрегация метрик, выбор лидера в StatsD-кластере и отправка лидером метрик в Graphite практически не изменились. Про это вы можете подробно прочитать в нашей прошлой статье.

Что же касается цифр, то они следующие.

График полученных StatsD-событий
m2td56sycgexhmsupyp7nfmp2hw.png
np5mhi_4z6pfwpdpbvzfamn7fws.png

График метрик, отправленных из StatsD в Graphite
m2td56sycgexhmsupyp7nfmp2hw.png

Общая схема взаимодействия компонентов мониторинга на данный момент выглядит так.

n6ym-bcuyt3a3ctoda93lassduo.png

Суммарное количество значений метрик: 2 189 484 898 474.
Общая глубина хранения метрик: 3 года.
Количество уникальных имен метрик: 6 585 413 171.
Количество триггеров: 1053, они обслуживают от 1 до 15k метрик.

Планы на ближайшее будущее:


  • начать переводить продуктовые сервисы на тегированную схему хранения метрик;
  • добавить в кластер Graphite еще три сервера;
  • подружить Moira с персистентной тканью;
  • найти ещё одного разработчика в команду мониторинга.

Буду рад комментариям и вопросам здесь — пишите. А ещё я буду выступать 7 ноября на Highload++, если будете там, можем пообщаться.

© Habrahabr.ru