Фото ETA+, Unsplash.com
Первые шаги в сторону observability — собираем метрики с Prometheus
Представьте себе человека, который бьется над решением загадки, а вокруг собралось с десяток советчиков, которые помогают ему «найти убийцу». Знакомая ситуация? В таких случаях возникает больше вопросов, чем ответов, и если отвечать на все эти вопросы, ни на что другое времени уже не останется.
Сложно найти то, чего не знаешь и не понимаешь. Вот тут-то на помощь и приходит observability (наблюдаемость). Она помогает понять, что сломалось или плохо работает, и даже позволяет найти причину, чтобы вы могли постоянно вносить улучшения.
Что такое observability
Под observability мы подразумеваем показатель того, насколько эффективно можно определить внутреннее состояние системы по ее выходным данным (телеметрии). Ничего нового в этом нет — мы пытаемся разобраться в работе приложений по логам с начала времен, но сейчас все осложняется облаками, контейнерами, микросервисами, многоязычными средами и т. д.
Три кита observability
Observability основана на трех главных компонентах. Это данные телеметрии, которые можно агрегировать и анализировать, чтобы понять состояние системы:
- Метрики.
- Трассировки.
- Логи.
В этой серии статей мы рассмотрим все три компонента с примерами. Начнем с метрик и опенсорс-проекта Prometheus.
Метрики
Метрика — это числовое значение за определенный период времени, например, счетчик, таймер или измеритель. Их сбор не требует много ресурсов, потому что числовые значения можно объединять, чтобы сэкономить на трафике. Обычно с помощью метрик мы определяем работоспособность системы, потому что они хорошо описывают статус ресурса. Кроме того, можно инструментировать код с помощью таких библиотек, как prometheus_client или prometheus_exporter, чтобы собирать кастомные метрики, например, время отклика, число активных запросов, количество ошибок и т. д.
Prometheus
Prometheus — это опенсорс-система мониторинга и оповещения на основе метрик. Изначально он был разработан SoundCloud, а теперь это проект Cloud Native Computing Foundation. Он собирает метрики об инфраструктуре и приложениях.
Prometheus состоит из нескольких компонентов:
- Cервер Prometheus собирает метрики и хранит данные временных рядов.
- Клиентские библиотеки генерируют метрики для приложения.
- Push Gateway — промежуточный компонент, который позволяет пушить метрики из джобов, если скрейпинг невозможен.
- Alert Manager обрабатывает алерты от Prometheus. Отвечает за группирование, дедупликацию и направление алертов нужному получателю.
- Экспортеры собирают статистику из другой системы и преобразуют ее в метрики Prometheus.
Архитектура Prometheus
Деплоймент
Prometheus можно развернуть по-разному: установить в инфраструктуре вручную в контейнерах или Kubernetes или использовать helm — с оператором или без. Для простоты мы задеплоим Prometheus с помощью helm-чарта с оператором Prometheus.
kubectl create namespace observability
helm repo add prometheus-community https://prometheus-
community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack --
namespace observability
На момент написания последняя версия Prometheus — 2.26, с удобными новыми фичами. Подробнее: https://github.com/prometheus/prometheus/releases/tag/v2.26.0 Примечание переводчика: к моменту публикации перевода последняя версия — 2.28.1.
После деплоймента вы увидите следующие поды:
NAME READY STATUS RESTARTS AGE
alertmanager-prometheus-kube-prometheus-alertmanager-0 2/2 Running 0 69m
prometheus-grafana-5c469b9cbf-kn2zk 2/2 Running 0 69m
prometheus-kube-prometheus-operator-68d59d8d59-gvkzk 1/1 Running 0 69m
prometheus-kube-state-metrics-685b975bb7-rhstl 1/1 Running 0 69m
prometheus-prometheus-kube-prometheus-prometheus-0 2/2 Running 0 69m
prometheus-prometheus-node-exporter-mn68b 1/1 Running 0 69m
Для доступа к Prometheus мы пробросим локальный порт на порт сервиса Prometheus.
kubectl port-forward service/prometheus-operated -n observability 9090
Forwarding from 127.0.0.1:9090 -> 9090
Forwarding from [::1]:9090 -> 9090
Prometheus на localhost:9090
Как видите, у нас уже есть несколько метрик. Все благодаря поду kube-state-metrics — сервису, который обращается к Kubernetes API и генерирует метрики о состоянии объектов Kubernetes.
Чтобы взаимодействовать с этими метриками и агрегировать их, используйте язык запросов PromQL (Prometheus Query Language). Результаты запроса можно вывести в виде таблицы или графика.
Метрики бывают четырех типов:
- Счетчик: значение можно только увеличивать или сбросить до нуля.
- Измеритель: значение можно увеличивать или уменьшать.
- Гистограмма: несколько временных рядов — совокупные счетчики для бакетов, сумма наблюдаемых значений и общий счетчик событий.
- Сводка: похожа на гистограмму (показывает сумму наблюдаемых значений и счетчик для событий), но также предоставляет расчет для φ-квантилей.
Гистограмма тоже может давать φ-квантили с помощью функции histogram_quantile
, но расчет выполняется на стороне сервера, а не клиента, как в сводке.
Предоставление метрик
Это была теория. Давайте теперь на практике посмотрим, как собирать метрики приложений и сервисов через Prometheus.
Для начала нужно предоставить некоторые метрики. Для этого используем клиент Prometheus для Node.js. У клиента уже есть дефолтные метрики, так что результат мы получим быстро.
Создаем реестр и велим клиенту собирать дефолтные метрики.
const prom = require('prom-client')
const registry = new prom.Registry()
prom.collectDefaultMetrics({
register: registry
})
const registerPromMetrics = async (req, res) => {
console.log('Returning Metrics')
try {
res.set('Content-Type', registry.contentType);
res.end(await registry.metrics());
} catch (err) {
res.status(500).end(err);
}
}
module.exports = {registerPromMetrics}
Предоставляем эти метрики через endpoint /metrics.
const express = require('express');
const { registerPromMetrics } = require('./telemetry/metrics')
const app = express();
app.use(express.json());
app.get('/metrics',registerPromMetrics)
app.listen(process.env.SERVER_PORT, () => {
logger.info(`Server Ready and Listening to port ${process.env.SERVER_PORT} `)
})
Вот и все. Метрики готовы.
endpoint /metrics на localhost:8080
Чтобы создать кастомные метрики, нужно выбрать тип метрик, а затем сконфигурировать и зарегистрировать его. Для всех типов метрик можно настроить метки, чтобы получить больше контекста и использовать фильтры. На гистограммах можно настроить бакеты, которые объединяют и подсчитывают значения в своем интервале.
Когда вы настроите метрики, вам понадобится метод наблюдения значений. Для этого можно использовать функции промежуточной обработки.
const request_duration = new prom.Histogram({
name: "request_duration_ms",
help: "Request duration histogram",
labelNames: ["method","path","statuscode"],
buckets: [0.1, 5, 10, 15, 50, 100, 200, 500]
})
const active_request = new prom.Gauge({
name: "active_requests_count",
help: "Count active requests",
labelNames: ["path"]
})
registry.registerMetric(request_duration)
registry.registerMetric(active_request)
const getRequestDuration = (req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
request_duration.labels(req.url, req.method, res.statusCode).observe(duration)
});
next();
};
const countActiveRequest = (req, res, next) => {
active_request.inc()
res.on('finish', () => {
active_request.dec()
})
next();
}
module.exports = {getRequestDuration, countActiveRequest, registerPromMetrics}
Затем функции промежуточной обработки загружаются express.
const { countActiveRequest,getRequestDuration,registerPromMetrics } = require('./telemetry/metrics')
const app = express();
app.use(countActiveRequest)
app.use(getRequestDuration)
app.use('/',getRouter)
app.get('/metrics',registerPromMetrics)
app.listen(process.env.SERVER_PORT, () => {
logger.info(`Server Ready and Listening to port ${process.env.SERVER_PORT} `)
})
Пример приложения
Все ресурсы, которые мы используем в этой серии статей, доступны в репозитории на GitHub: https://github.com/jonathanbc92/observability-quickstart/tree/master/part1
Пример приложения для этой серии статей будет развернут на Minikube. Оно состоит из трех контейнеров Node.js:
- People Service — сервис с простым запросом на получение данных из таблицы в MySQL.
- Format Service — сервис, который форматирует сообщение, используя значения из People Service.
- Hello Service — сервис, который вызывает предыдущие сервисы и отправляет итоговое сообщение пользователю.
У всех микросервисов уже есть дефолтные и кастомные метрики в файле metrics.js.
Чтобы задеплоить весь набор (включая Prometheus), выполните команду make
.
Чтобы протестировать приложение, пробросьте порт на порт hello service 8080, а затем сделайте запрос на localhost:8080/sayHello/{name}
curl http://localhost:8081/sayHello/iroh
Hey! this is iroh, here is my message to you: It is important to draw wisdom from many different places
Мониторинг сервисов
Важно понимать, что от отсутствия или наличия оператора при деплойменте Prometheus зависит конфигурация, необходимая для определения target services (целевых сервисов).
Если Prometheus задеплоен без оператора, мы настраиваем targets (цели) в файле конфигурации.
scrape_configs:
- job_name: 'node'
# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s
static_configs:
- targets: ['localhost:8080', 'localhost:8081']
labels:
group: 'production'
- targets: ['localhost:8082']
labels:
group: 'canary'
Если мы используем оператор, нужно создать кастомные ресурсы — ServiceMonitor. ServiceMonitor указывает, как мониторить сервисы Kubernetes, и автоматически создает конфигурацию скрейпа (примечание переводчика: Конфигурацию автоматического сбора метрик).
Схема ServiceMonitor
Кастомный ресурс ServiceMonitor сопоставляет сервисы с метками, указанными в параметре matchLabels в неймспейсе, заданном в namespaceSelector. Сервис может предоставлять много портов, поэтому нужно указать порт и путь, из которого будут собираться метрики.
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: all-service-sm
namespace: observability
labels:
release: prometheus
spec:
selector:
matchLabels:
monitor: prometheus
namespaceSelector:
matchNames:
- applications
endpoints:
- port: http
path: /metrics
Проверьте метки, включенные в ServiceMonitor, — Prometheus не будет выбирать ServiceMonitor, у которых нет нужных меток. Что это за метки? В спецификации кастомного ресурса Prometheus видим следующее:
kubectl get prometheuses prometheus-kube-prometheus-prometheus -n observability -o yaml
alerting:
alertmanagers:
- apiVersion: v2
name: prometheus-kube-prometheus-alertmanager
namespace: observability
pathPrefix: /
port: web
enableAdminAPI: false
externalUrl: http://prometheus-kube-prometheus-prometheus.observability:9090
image: quay.io/prometheus/prometheus:v2.26.0
listenLocal: false
logFormat: logfmt
logLevel: info
paused: false
podMonitorNamespaceSelector: {}
podMonitorSelector:
matchLabels:
release: prometheus
portName: web
probeNamespaceSelector: {}
probeSelector:
matchLabels:
release: prometheus
replicas: 1
retention: 10d
routePrefix: /
ruleNamespaceSelector: {}
ruleSelector:
matchLabels:
app: kube-prometheus-stack
release: prometheus
securityContext:
fsGroup: 2000
runAsGroup: 2000
runAsNonRoot: true
runAsUser: 1000
serviceAccountName: prometheus-kube-prometheus-prometheus
serviceMonitorNamespaceSelector: {}
serviceMonitorSelector:
matchLabels:
release: prometheus
shards: 1
version: v2.26.0
Параметры serviceMonitorSelector показывают, какие метки должны быть у ServiceMonitor, чтобы Prometheus мог его выбрать.
Теперь нужно просто создать ServiceMonitor:
kubectl apply -f k8s/config/prometheus/servicemonitoring.yml
Если все получилось, перейдем в раздел Targets (Целевые объекты) в меню Status (Статус) в Prometheus. Мы увидим все поды, связанные с отслеживаемыми сервисами.
Эндпоинты, с которых Prometheus собирает метрики
Когда новые метрики будут доступны, вы сможете запрашивать их в браузере выражений (expression browser).
Размер кучи Node.js в Prometheus
Экспортеры
О сервисах Node.js мы уже поговорили. Как насчет MySQL? Метрики базы данных нам тоже нужны. Чтобы экспортировать имеющиеся метрики из сторонних систем в виде метрик Prometheus, можно использовать экспортеры.
Выбор экспортеров богатый. Вот некоторые из них: https://prometheus.io/docs/instrumenting/exporters/
Мы используем официальный образ экспортера MySQL и запустим его с контейнером MySQL. Для образа требуется переменная среды DATA_SOURCE_NAME
, значение которой будет получено из секрета. Поскольку все контейнеры в поде используют одну сеть, можно использовать localhost, чтобы подключиться к MySQL.
kubectl create secret generic mysql-credentials --from-literal=username=root --from-literal=password=admin --from-literal=datasource="exporter:exportpass@(localhost:3306)/tutorial" -n applications
После создания ресурсов в Kubernetes на вкладке Prometheus Targets мы увидим следующее:
В браузере выражений Prometheus мы увидим, что метрики MySQL доступны.
Метрики MySQL в Prometheus
Теперь можно использовать функцию rate
, чтобы посмотреть средний рост для команд select за определенный период. Это показатели только для команд select, выполненных экспортером для сбора метрик.
Если сделать то же самое с командами delete, мы увидим 0, потому что команды delete не выполнялись.
С помощью Apache Benchmark можно провести нагрузочное тестирование, чтобы создать пик.
20к запросов с AB
Пик для команд select в Prometheus
Заключение
Как видите, запустить Prometheus с helm-чартом очень просто — вам понадобится пара минут и Minikube. Конечно, в первый раз посмотреть на метрики интересно, но сами по себе они мало что дают. В следующий раз мы поговорим об алертах.
Комментариев нет:
Отправить комментарий