Привет, Хабр! Меня зовут Юра, я DevOps-инженер команды разработчиков платформы контейнеризации dBrain.cloud. Наши статьи о системе мониторинга и логирования в составе платформы dBrain вызвали большой отклик. Пришло время сделать обзор на обновленную инфраструктуру мониторинга dBrain: рассказать про VictoriaMetrics версии 2.5 и выше, какие инструменты планируем внедрить и как их применять.

Метрики
Для начала разберем, что входит в стек мониторинга нашей платформы. Основным компонентом для метрик является VictoriaMetrics. Составляющие для метрик - развернутая через оператор кластерная версия VictoriaMetrics. Сам по себе оператор дает только возможность управления ресурсами через CRD и иногда может немного ограничивать свободу действий. С другой стороны автоматические настройки при масштабировании и обслуживании все-таки очень полезны. Вывод из нашего опыта работы с оператором: он все же несет гораздо больше плюсов, чем минусов, при этом потребляет совсем немного ресурсов (отдельное спасибо за это разработчикам). Поэтому если перед вами стоит вопрос, использовать ли оператор, ответ - однозначно да.
Что касается экспортеров, которые используются в dBrain, то все перечислять не имеет смысла, так как задачи у каждого свои. В нашем случае, кроме стандартной связки kube-state + node-exporter, есть и боле специфичные dcgm-exporter или ebpf-exporter. Со стороны клиентской части, конечно же, не обойтись без всякого рода адаптеров и агрегаторов для короткоживущих метрик. Мы выбрали otel-collector и pushgateway, что позволяет нам не быть злыми дядями, которые указывают в каком формате слать метрики в систему мониторинга и дают полную свободу действий.
С точки зрения кода и поддержки все достаточно просто и не занимает много сил. Если на кластере уже есть оператор, то нужно сделать совсем немного, чтобы развернуть минимальный стек.
Создание кластера vmcluster (vminsert + vmselect + vmstorage) и объекта CRD.
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMCluster
metadata:
name: dbrain
namespace: mon
spec:
replicationFactor: 2 # фактор репликации: во сколько минимум реплик будет попадать
каждая порция данных
retentionPeriod: "3" # время хранения метрик в месяцах(можно использовать другие
параметры, например, 7d (дней)
vminsert:
image:
repository: <репозиторий, откуда тянется образ VictoriaMetrics>
tag: <версия компонента>
logLevel: INFO
replicaCount: 2 # количество реплик
Resources: # ну и само собой ресурсы
limits:
cpu: "0.5"
memory: 512Mi
requests:
cpu: "0.2"
memory: 256Mi
vmselect:
image:
repository: <репозиторий, откуда тянется образ VictoriaMetrics>
tag: <версия компонента>
logLevel: INFO
replicaCount: 2
resources:
limits:
cpu: "0.2"
memory: 256Mi
requests:
cpu: "0.05"
memory: 64Mi
vmstorage:
image:
repository: <репозиторий, откуда тянется образ VictoriaMetrics>
tag: <версия компонента>
logLevel: INFO
replicaCount: 3
resources:
limits:
cpu: "1"
memory: 5Gi
requests:
cpu: "0.1"
memory: 3Gi
storage:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 128Gi
storageClassName: <storageclass, который есть на кластере>
Таким образом на нашем сетапе появится кластерная версия VictoriaMetrics. Развернуть только хранилище недостаточно. Для полноценной работы нужен как минимум сборщик метрик, учитывая что у нас пулл модель (можно обойтись “костылями” и сразу пушить в vminsert, но лучше не стоит).
Добавление сборщика vmagent. Догадываетесь, как будем это делать? Правильно, через объект CRD, ведь мы красавчики и у нас операторная версия.
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAgent
metadata:
name: <имя вашего сборщика>
namespace: mon
spec:
image:
repository: <репозиторий, откуда тянется образ VictoriaMetrics>
tag: <версия компонента>
insertPorts:
graphitePort: "2003"
influxPort: "8089"
openTSDBHTTPPort: "4243"
openTSDBPort: "4242"
logLevel: INFO
minScrapeInterval: 60s
nodeScrapeNamespaceSelector: # важные настройки: если их провалить,
то VictoriaMetrics либо не найдет нужные джобы, либо произойдет дублирование.
Если у вас несколько сборщиков или другие их типы, то подробнее можно посмотреть
здесь https://docs.victoriametrics.com/operator/api/index.html#vmagentspec
matchExpressions:
- key: namespace
operator: In
Values:
- перечисляем неймспейсы, в которых находятся джобы сбора
- testnamespace
- testnamespace2
relabelConfig: # общий релейблинг, который применяется ко всем джобам
key: relabel.yaml
name: vm-agent-core-global-relabel-config
remoteWrite:
- url: <место отправки метрик>
replicaCount: 1
resources:
limits:
cpu: "0.5"
memory: 1Gi
requests:
cpu: "0.15"
memory: 256Mi
shardCount: 1 # количество шардов - фича классная, но в дебаге будете страдать.
Попадая не на ту реплику, не будете понимать почему нет всех целей.
Теперь у нас есть место хранения метрик и инструмент для их сбора. Осталось добавить цель для сбора, т.е. место.
Добавление джобы сбора, допустим, это будет kube-state. Пример ниже:
---
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape # тип ресурса (в нашем примере - это аналог сервис монитора)
metadata:
name: kube-state
namespace: mon
spec:
namespaceSelector: # в каком неймспейсе
matchNames: [ mon ]
Selector: # селектор сервиса
matchLabels:
app.kubernetes.io/name: kube-state-metrics
Endpoints: # и какие порты опрашивать в сервисе
- port: http-metrics
scheme: http
honorLabels: true
- port: telemetry
scheme: http
Получается, что с помощью оператора мы в три шага добавили сам кластер, сборщик метрик и цель. В этом и заключается главная ценность этого оператора: упростить работу и сократить издержки на поддержку. Это не вся функциональность, но, чтобы “потрогать” оператор, достаточно.
В мониторинге платформы dBrain есть и другие фичи:
В первую очередь это, конечно, мультитенантность, которая позволяет изолировать клиентов друг от друга.
Клиент самостоятельно может управлять своими ресурсами: дашборды, алерты, место для отправки оповещений.
Пользователь может сам указать сторонние интеграции: куда ходить, что собирать и т.д., буквально создав один объект на кластере.
Как добавить алертменеджер, сами алерты и настроить алертинг в конкретные точки, подробнее расскажем в следующих статьях. Пока продолжим, на метриках свет клином не сошелся.
Трассировка
Помимо метрик в dBrain реализована трассировка приложений через Tempo. Трассировка приложений простыми словами - это измерение и оценка производительности приложений с помощью определенных средств и инструментов. Звучит не очень понятно. Поясним на примере, где профилировка помогла найти истинную проблему. Нередкая ситуация: команда разработки полагает, что проблема связана с инфраструктурой, в то время как команда поддержки видит ее в приложении. Без сторонних инструментов в данном случае не обойтись.
Случай из практики: входящий RPS на клиентское приложение около 3 тысяч, что совсем немного по меркам PostgreSQL, который использовался как DB. В то же время на графиках мы видели, как растет время ожидания запросов и забиваются тредпулы в приложении. С первого взгляда казалось, что база по каким-то причинам не справляется и от этого увеличивается время ожидания запросов к ней, поэтому все приложение начинает тротлить. После долгих поисков проблем в инфраструктуре под базой не мы не обнаружили. Ресурсы были в норме, диски не указывали, что есть проблема, но тем не менее запросы к базе выполнялись крайне медленно (да-да, в метрики самой PostgreSQL смотрели, ничего криминального не нашли).
Так как же помогла трассировка в этом случае? Все достаточно просто: посмотрев на спаны трассировок, мы поняли, что некоторые части приложений на один входящий запрос порождали в несколько раз больше запросов к базе, из-за этого заканчивались пулы и увеличивалось время ожидания внутри самого аппликейшена, т.к. были долгие ожидания локов. После небольшого рефакторинга на той же инфраструктуре все снова отлично заработало. Это только один из кейсов. Для понимания, как это выглядит в Grafana, приведу скрин взаимодействия синтетики.

На этом примере мы можем увидеть, в каком порядке и сколько по времени выполнялись блоки взаимодействия, помеченные как спаны. Это могут быть как разные сервисы, так и внутри одного приложения. Просто посмотрев на этот скрин, можно найти проблемное место в приложении: что и где идет не так.
Еще одна классная фича - Service graph. Он показывает, как идет взаимодействие в приложении, что упрощает дебаг и позволяет сразу вычленять нагруженные части. Скрин ниже.

Чтобы подключить трассировку в рамках dBrain, необходимо сконфигурировать SDK для трассировки внутри приложения и отправить трейсы на готовый endpoint со стороны платформы. Остальное произойдет автоматически (доступно через Grafana, интеграции trace to metrics, trace to logs).
Профилировка
Изначально мы использовали сервис Phlare, теперь - Pyroscope. Если вы не знаете, что такое профилировка, flamegraph и прочие слова, которые в приличном обществе не стоит употреблять, я искренне рад за вас. Остальным же стоит только посочувствовать и рассказать, как это устроено у нас. Все достаточно просто Pyroscope + Grafana Alloy + Grafana. Конфигурация для сбора всех типов профилировок основана на автодискаваренге целей внутри Kubernetes. Для примера приведу часть конфига Alloy.
discovery.relabel "kubernetes_pods" {
targets = concat(discovery.kubernetes.pyroscope_kubernetes.targets)
rule {
action = "drop"
source_labels = ["__meta_kubernetes_pod_phase"]
regex = "Pending|Succeeded|Failed|Completed"
}
rule {
action = "labelmap"
regex = "__meta_kubernetes_pod_label_(.+)"
}
rule {
action = "replace"
source_labels = ["__meta_kubernetes_namespace"]
target_label = "namespace"
}
rule {
action = "replace"
source_labels = ["__meta_kubernetes_pod_name"]
target_label = "pod"
}
rule {
action = "replace"
source_labels = ["__meta_kubernetes_pod_container_name"]
target_label = "container"
}
}
Этот конфиг выбирает поды, делает релейблинг и передает дальше для работы в правила для отсеивания и переименования. Что нам это дает? Когда приложение пишется под хайлоуд или очень критичны скорости выполнения, настает этап отслеживания времени выполнения кусков кода. Профилировка позволяет понимать, какие кусочки кода сколько и как выполнялись относительно всех блоков. В Grafana это выглядит следующим образом.

На первый взгляд ничего непонятно, и если быть честным, на второй тоже. Но стоит поработать с этим подольше, можно будет детально рассказать, где и что нужно оптимизировать или почему снизилась производительность после очередного релиза. Если выбрать определенный блок, можно его подробно рассмотреть и получить более полное понимание, как работает приложение. Допустим, нас не устроило, что блок Serve на примере выше выполняется слишком долго. Выбрав его, можем увидеть следующее.

Видим детальное распределение: что, как и за какое время выполнялось. Находим проблему и фиксим (в данном случае проблемы нет, т.к. ситуация вымышленная).
Чтобы подключить профилировку, нужно на приложение повесить соответствующие лейблы, остальное произойдет автоматически.
Отличие трассировки и профилировки
Профилировка показывает отдельные функции, куски кода, которые выполнялись в определенное время, а трассировка - взаимодействие между сервисами и путь данных. Хоть они и похожи, выполняют разные функции и дополняют друг друга, что в сумме с метриками дает мощный инструмент не только траблшутинга, но и разработки.
RUM (Real user monitoring) осветим в отдельной статье, потому что тема объемная, особенно со стороны инфраструктуры. Но он у нас есть:)
Планы по развитию
Наша команда постоянно работает над улучшением и расширением возможностей платформы dBrain, чтобы она соответствовала потребностям пользователей. В ближайших планах несколько ключевых направлений:
Мультиретеншен для VictoriaMetrics.
Нативная поддержка корпоративного мессенджера Frisbee для алертменеджера.
Мультитенант на основе CRD.
UI-билдер для алертов через консоль dBrain (как работает консоль, мы рассказывали тут).
Уже традиционно оставляю полезные ссылки на официальную документацию и сообщество VictoriaMetrics, а вы делитесь в комментариях под нашей статьей своим опытом организации мониторинга.
Читайте также: