Можно ли сделать классный и удобный мониторинг не для галочки, а с пользой для всех участников разработки? Инженерный опыт подсказывает, что Observability как процесс идеально построить не получится. Но стремиться к этому стоит. Статья подготовлена на основе доклада Владимира Дроздецкого о вопросах, методах, практиках и в целом подходе к Observability для инфраструктурной платформы Magnit Online Services. Внутри — о том, как пришли к идее нового процесса, о настройке мониторинга от helm upgrade до gitops-подхода, опыте интеграций с Grafana в работе с визуализацией и метриками и политике алертов, которая не пропускает критичные угрозы. Статья будет полезна инженерам платформенных команд и эксплуатации, а также всем, кто внедряет у себя DevOps-практики. Около трёх лет назад руководство «Магнита» отказалось от разработки мобильного приложения  на аутсорсе. Всё перенесли внутрь компании. В результате к концу 2020 года в руках команды эксплуатации появился OpenShift одной из старых версий и работающие внутри него Prometheus и ELK Stack. Было много проблем. В частности, с метриками, потому что их Retention соответствовал базовой конфигурации Prometheus DB. А в случае с ELK Stack терялось большое количество логов, так что разработка не могла ими пользоваться. Чтобы избежать подобных проблем в будущем и обеспечить более эффективную работу, в Magnit Online Services решили строить свою платформу на базе Yandex Cloud и процесс мониторинга для неё. Сердцем инфраструктуры стал Kubernetes, а основным стеком — Prometheus, Grafana, Loki, Promtail. Observability как процесс — это танго для двоих. Командам эксплуатации и разработки предстоит плотно взаимодействовать и много договариваться о том, что и как строить. Без этого хорошее Observability вряд ли получится. У команды в процессе таких переговоров рождается целый ряд внутренних соглашений. Например: Использование twelve factor app: все приложения, запущенные в Kubernetes, должны соответствовать правилам для создания cloud native приложений. Метрики: формат пригодный для Prometheus, базовые метрики, возможность добавлять кастомные метрики. Логи: все логи должны храниться в JSON-формате с определёнными полями, такими как time stamp, месседжи, специфичные для сервиса поля. Благодаря такой стандартизации появляется универсальный апчарт для всех внутренних приложений, и новые сервисы запускаются быстрее.  Когда разработчик впервые деплоит своё приложение в dev-контур, он автоматически получает сервисы мониторинга и базовые алерты. Это означает, что приложение начинает мониториться и логироваться. Логи можно просматривать через Grafana, а информацию о сервисах, такую как потребление ресурсов процессора и памяти, можно увидеть, переключаясь между панелями на предварительно настроенных дашбордах. Для более узких кейсов и специфичных бизнес-метрик можно использовать отдельные дашборды. Prometheus — центр мониторинга и компонент платформы. Для развертывания применяется kube prometheus stack helm chart, включающий в себя все необходимые компоненты. Всё конфигурирование проходило по схеме: есть инженер, отдельный репозиторий в GitLab и Kube Prometheus Stack со всеми манифестами. Чтобы внести изменения, достаточно было использовать «helm upgrade –install …». Из-за такого подхода возникло несколько проблем, связанных с человеческим фактором: Ошибки в процессе конфигурирования. Иногда члены команды вносили изменения в кластер руками при помощи команды «kubectl» и забывали запушить это в репозиторий. Конфликты во время слияния разных веток, когда в них работали разные люди. Решение проблемы нашли в подходе GitOps и начали использовать Flux CD. Вначале всё хорошо настроили, и Flux применял изменения, которые были в репозитории. Его использовали только для настройки Prometheus, но потом решили управлять и другими ресурсами, такими как: Nginx Ingress Controller, Prometheus Rules, etc. Первоначальный бутстрап кластера тоже был интересен. С первой большой проблемой столкнулись далее. Первая большая проблема, с которой команда столкнулась при реализации, — зависимость ресурсов. Flux из коробки рекурсивно перебирал всю свою директорию и находил сущность Prometheus Service Monitor, которая не могла быть добавлена, потому что отсутствовал нужный CRD и весь деплой «залипал». Решение нашли в переходе на кастомизацию и использовали Kustomization Controller. Определили зависимости в спеке, установили таймауты и описали health check для компонентов, которые деплоим. После этого стало возможным управлять зависимостями ресурсов при бутстрапинге кластера с нуля. Не все ресурсы, которые нужны, находились внутри кластеров Kubernetes. Некоторые были за пределами и запущены на виртуальных машинах. Их нужно было как-то мониторить. Для этого на виртуалках установили экспортеры и Promtail для логов. Но возник вопрос как добавлять в мониторинг новые виртуалки с сервисами? И можно ли это автоматизировать? В качестве решения пришли к Service Discovery, с которым работает Prometheus. Источником ресурсов, которые нужно было мониторить, стал HashiCorp Consul. А для управления инфраструктурой, виртуальными машинами и кластерами использовали Terraform и его модули. Когда базовая виртуальная машина со всеми обновления разворачивается, на ней уже стоит Node Exporter. Для регистрации сервиса в Consul добавили отдельный ресурс в Terraform. Prometheus уже знал, на какой сервис ему смотреть, и благополучно добавлял нужный ресурс в мониторинг. Особенность таких виртуальных машин в том, что на них может быть установлено несколько экспортеров, и каждый предоставляет метрики для конкретной задачи. Например, на виртуалке с VMware Harbor могут быть установлены экспортеры для метрик Harbor и других задач. В модуле Terraform — дополнительные ресурсы, которые регистрируют новые сервисы в Consul. Так можно добавлять новые ресурсы, соответствующие этим экспортерам, и Prometheus будет успешно добавлять таргеты для сбора метрик. Теперь о том, как работали с метриками внутри Kubernetes-кластеров. По запросу о мониторинге в Kubernetes поисковой сервис выдаст что-то подобное: В кейсе Magnit Online Services рабочий набор включал dev, uat, prod-стенды. В качестве хранилища данных использовали Victoria Metrics и визуализировали при помощи Grafana. Remote_write был важен как механизм Prometheus. Он позволял использовать отдельное хранилище для записи метрик и так называемые external labels, когда по лейблу метрики можно понять, из какого она кластера. Всё это было нужно, чтобы формировать дашборды, которые дадут разработчику полную картину, возможность переключать окружение и просматривать метрики для dev, uat, prod и так далее. Victoria Metrics использовали в Single Node-инсталляции. Этот способ выбрали потому, что на тот момент было не так много метрик. Виртуальная машина с 8 ядрами, 16 гигабайтами оперативной памяти и диском была способна справиться с задачей. Тестирование кластерной инсталляции внутри Kubernetes тоже проводили. Всё работало отлично, но потребляло больше ресурсов. Поэтому решили отложить вопрос на будущее. Теперь об алертах. В  Magnit Online Services выделяют два типа: технические — алерты о дисках, процессорах, оперативке и прочем; бизнес — алерты, которые срабатывают в случае наступления какого-то бизнес-события, например, в мобильном приложении. Для создания технических и бизнес-алертов применяли правила Prometheus, которые разработчики хранили рядом с приложением в репозитории. Создать технический или бизнес-алерт мог любой сотрудник, не важно, это команда инфраструктуры или разработки. Например, разработчики могли создать alert о том, что диски базы данных заполнились на 50%. Бизнес-алерты, в отличие от технических, более критичны. Их нужно писать внятно и более внимательно из-за прямого импакта на пользователей. В нашем случае людей, которые используют приложение «Магнит акции и скидки». Основной канал оповещений о событиях работает через Microsoft Teams.  Если бизнес-алерт срабатывает, команде приходит оповещение, и алерту сразу присваивается статус Critical. Это значит, нужно срочно разобраться, что произошло.  Алерты с разными статусами приходят в отдельный канал нотификации, где уже есть люди, ответственные за эту часть задач в продукте. Ещё есть Notification channel — буквально мусорка для алертов, куда попадает вообще всё. Например, алерты, которые притянул за собой Prometheus Stack. Дальше каналы делятся по важности и времени реакции: на Critical алерт — в течение 15 минут; на Pre Critical — в ближайший час, так как этот алерт может в скором времени стать критичным; на Non-critical не нужно реагировать мгновенно, но обязательно завести задачу в Jira и начать что-то делать. Любое Non-critical событие также может перерасти в критичное. По Critical алерту в канале и по решению кейса, который произошёл, формируется постмортем. Для этого в Confluence заведен отдельный раздел. В нем есть полное описание того, что случилось: название и тип инцидента, на кого и в каком масштабе он влияет (начало, конец, что было, как починили). И последний пункт — набор тасок в Jira, которые нужно выполнить, чтобы ситуация больше не повторялась. Когда в команде поняли, что возможностей Microsoft Teams недостаточно для задач с оповещениями, то воспользовались дополнительной системой для интеграции алертов, сначала с помощью Pager Duty, затем с Aletrops.  Более подробная схема оповещений о событиях выглядит так: Основная задача Alertops — отправка сообщений на электронную почту, смс и телефонный звонок. При этом телефонный звонок — это не просто очередной способ донести информацию. Когда звонит Alertops, инженер слушает сообщение и должен нажать определённую цифру. Это будет означать, что алерт принят в работу, отклонен или эскалирован. Бывают ситуации, когда инженер, которому приходят алерты, недоступен. Для этого в Alertops настроена политика эскалации. С алертами в Magnit Online Services работают две команды: платформенная и саппорт. Последняя оказывает круглосуточную поддержку всей компании. Инженеры платформенной команды дежурят с 10:00 до 19:00, с понедельника по пятницу. Внутри есть ротация, чтобы распределить нагрузку между инженерами. Политика эскалации срабатывает, если дежурный инженер получил Critical alert и по какой-то причине на него не ответил. Если первый не ответил, звонок поступает второму. Если не ответил второй, звонок идёт лиду команды. Если никто из инженеров не ответил, звонок направляется руководителю разработки, и тот точно кого-нибудь найдёт.  Со второй командой инженеров схема такая же. Отличаются только часы дежурств: их расписание с 00:00 до 10:00 и с 19:00 до 00:00. Саппорт работает 365 дней в году и 24 часа в сутки без выходных. Если они не могут отработать оповещение, то обращаются к платформенной команде. Типичная ошибка визуализации — дашборд со множеством цифр и графиков, в котором разработчик не сможет быстро найти то, что нужно. Например, узнать, сколько ресурсов процессора и оперативной памяти потребляет приложение, которое только что задеплоили в Kubernetes. Разработчик, который ничего не может найти, начинает грустить. А польза от дашборда — минимальна. Поэтому лучше формировать плюс-минус универсальные дашборды. Ниже та же самая задача. В верхнем углу видим namespace и название приложения (APP). Их переключение позволяет указать разработчику нужный namespace и приложение. Соответственно, на данном графике он увидит свои поды и то, как они потребляют ресурсы. И верхняя отсечка — это лимиты, указанные при деплое приложения. Некоторые запросы, с которыми разработчики приходят к инженерам, на первый взгляд кажутся банальными. Но это не так. Если сделать дашборд удобным, вопросов будет меньше, и воспользоваться им смогут быстрее. Приложения не живут в вакууме, они взаимодействуют между собой. Случаются кейсы, когда разработчик вносит изменения, деплоит приложение, а оно не работает так, как задумано, или не работает вообще. Потому что сервис, с которым он взаимодействует, полностью не функционирует.  Понять, работает ли соседнее приложение, помог дашборд, который показывает жизнеспособность сервиса в том или ином окружении. Существует отдельный плагин для Grafana, который появился в версии 8.5: называется Status History, он поможет визуализировать доступность сервиса. В команде Magnit Online Services используется стандартизация, включающая известные порты для приложений и определенные эндпоинты для проверки состояния приложения. Например, эндпоинт health, который должен отдать ответ ready. Если в деплойменте есть хотя бы один запущенный и работающий под, то считается, что приложение работает. Чтобы получить данные о работе приложения, был добавлен Blackbox Exporter в кластер и настроен на проверку нужных эндпоинтов. В итоге все данные отображаются в Grafana. Итак, у нас есть метрики и графики, которые показывают, живо ли приложение и как себя чувствует пользователь. Фактически отображается количество пятисотых и других ошибок. Из CI/CD пайплайна отправляем аннотацию в Grafana при помощи того же cURL с определёнными параметрами. Получаем тот же график, но с отсечкой о том, что, например, был деплой. Такие отсечки полезно сделать для деплоя в прод. Предположим, мы заметили на графике резкий спад числа пользователей и рост числа пятисотых ошибок на бекенде. Чтобы понять причины, включаем аннотации и просматриваем, какие изменения происходили в системе в момент, когда возникла проблема. Например, видим, что 15 минут назад разработчик провёл деплой, и в параметрах видно, что именно он задеплоил и какой коммит использовал. Дальше можем принять решение: откатить изменения или быстро выпустить фикс. Ещё глубже погрузиться в анализ проблем, происходящих в системе, помогает Drill Down-подход. В работе с дашбордом он помогает переходить от общей картины к частным вещам.  На рисунке ниже пример, как формировали Drill Down дашборд для инфраструктуры и виртуальных машин Magnit Online Services.  Переключатель сверху — указание окружения (например, dev, uat и так далее) и список из виртуальных машин с какими-то параметрами (количество CPU, оперативная память, загрузка, диски). Чтобы понять, что происходит с диском, подсвеченным красным, нажимаем на имя инстанса и попадаем в более детализированный дашборд. Иметь такие данные разработчику очень полезно. Дальше о том, как это сделать. Открывая дашборд в Grafana, видим, что ссылка на него статическая. Она не меняется в зависимости от контекста, а значит, в неё можно передавать параметры. Например, параметр var node может быть передан из имени инстанса  предыдущего дашборда, а var site — переменной ENV. Это позволяет открыть нужный нам детализированный дашборд с нужным инстансом и окружением. Такой подход позволяет создавать дашборды с высокой детализацией на нижнем уровне, когда нужно добраться до целевого инстанса. Основная проблема при построении Observability заключается в слишком большом количестве инструментов мониторинга, трейсинга, логирования и других. Разработчик получает в своё распоряжение сразу несколько интерфейсов, может, и больше десяти. И когда случается какое-то событие, становится сложно переключиться из метрик в трейсы, логи и сопоставить с ними это событие. В качестве основного инструмента для логирования удобно использовать компоненты Gap Stack, потому что он отлично интегрируется с Grafana. Хранилищем для логов выступает Grafana Loki, а хранилищем трейсов — Grafana Tempo. В такой связке получаются удобные дашборды. Ниже пример дашборда с логами, которым пользуются разработчики в «Magnit Online Services».  Здесь можно переключить namespace, выбрать приложение и через строку поиска найти нужные логи.  Подводные камни Grafana Loki: Нуждается в тюнинге, если нужно работать с большим объёмом данных — 600–800 Гб и более. Придётся куда-то вынести индексы, например, в Apache Cassandra. Также желательно, чтобы диски были побыстрее. Не подходит как долгосрочное хранилище логов. Loki удобно использовать как краткосрочное хранилище — до двух недель, чтобы разработчик мог быстро посмотреть какую-то информацию. Дальше нужна автоматическая ротация. Если нужно работать с периодом в два-три месяца, Loki зависает, и лучше использовать другое решение.  В Magnit Online Services эту задачу решает ELK. В Elasticsearch хранятся логи только определенных сервисов. Доступ к Kibana имеет ограниченный круг разработчиков, которые сделали запрос на логирование своих сервисов и доступ. ELK используется ограниченно для построения более гибкой и сложной аналитики. Grafana Tempo используется для хранения трейсов. Согласно конвенции Magnit Online Services, трейсы должны быть сквозными, чтобы разработчик всегда мог видеть, как ушёл запрос, как он прошёл через gateway и с каким результатом. Инструмент легко устанавливается, но из-за большого объёма данных также нуждается в тюнинге. Для этого в Magnit Online Services добавили локальное кэширование, кластерную инсталляцию в Tempo и основное хранилище в S3 от Yandex.Cloud.  Один из важных моментов интеграции Grafana, Loki и Tempo — возможность формирования вычисляемых полей, называемых derived field.  В логе есть поле с уникальным идентификатором трейса — TraceID. Можно настроить источник данных в Grafana, чтобы парсить это поле, и, нажав на кнопку Tempo или Tempo Prod, разработчик может легко найти нужный ему трейс в интерфейсе Grafana. Это улучшает процесс отладки и позволяет быстро находить нужные данные. Ниже видим два варианта визуализаций. Справа — классическая, а слева — Node View, — бета-версия одной из новых фич. Она уже хорошо работает и рисует графы взаимодействия сервисов. Возвращаясь к Flux и вопросу настройки и управления кластерами в среде Kubernetes с его помощью. Команда, которая работала над процессом Observability, создала публичный репозиторий с описанием настроек, кастомизаций и минимальной документацией. Любой, кому это нужно, может зайти, посмотреть на готовую реализацию и начать использовать Flux для бутстрапинга своих кластеров. Пример структуры репозитория, используемый для базового бутстрапа кластеров k8s Удобный процесс Observability — это всегда результат совместной работы команд эксплуатации и разработки. Здесь хочется учесть все запросы, сделать работу мониторинга прозрачной, понятной, быстрой и с теми инструментами, которые подходят лучше всего. Возможно, идеальный процесс построить не получится, но к этому стоит стремиться.   В этой статье мы подробно разобрали опыт настройки процесса мониторинга командой Magnit Online Services. Ниже краткие выводы о том, из чего исходили авторы, какие проблемы решали и с помощью каких инструментов реализовали решения: Упростили и стандартизировали процесс мониторинга приложений — сформировали набор внутренних соглашений и понятных стандартов разработки. Так уменьшили число ошибок. Сделали мониторинг прозрачным — подключили сервисы мониторинга и базовых алертов при деплое приложения на dev-контур.  Систему алертов и политику их эскалации настроили исходя из приоритетов и бизнес-требований, специфики приложения и расписания работы команд. Связка из инструментов — Microsoft Teams, система для интеграции алертов Aletrops. Настроили работу с метриками с помощью Grafana, Victoria Metrics и настраиваемых дашбордов. Разработчикам стало удобно анализировать метрики и логи приложений.  Решили проблему зависимости ресурсов внутри кластера Kubernetes с помощью Kustomization Controllers и проблему мониторинга других ресурсов, запущенных на виртуальных машинах, через экспортеры, Prometheus и Service Discovery с использованием HashiCorp Consul.  Решили проблему визуализации ресурсов и памяти, состояния соседних сервисов и множественности неудобных графиков и цифр, в которых трудно что-то быстро найти. Реализовали с помощью плагинов и аннотаций Grafana, а также универсальных дашбордов.  Сделали мониторинг более гибким и настраиваемым, с высокой детализацией на нужных разработчику уровнях с помощью Drill Down подхода в разработке дашбордов и настройках в Grafana. Стало возможным погружаться в анализ проблем инфраструктуры глубже. Решили проблему мониторинга, логирования и трейсинга с помощью интеграции инструментов: Gap Stack для логирования в связке с Grafana, Grafana Loki для хранения логов и Grafana Tempo — для хранения трейсов.