Введение: Почему мониторинг Rust-приложений — это вызов и возможность
Rust славится своей производительностью и безопасностью памяти. Однако, когда ваше приложение работает на грани возможностей железа (например, высоконагруженный WebSocket-сервер или парсер данных), стандартные метрики CPU и RAM часто оказываются недостаточными. На продвинутом уровне мониторинг Rust-приложений требует не просто сбора данных, а создания точной системы алертов, которая предскажет сбой до того, как он произойдет.
В этой статье мы рассмотрим, как построить production-ready систему мониторинга для Rust: от кастомных метрик с использованием prometheus crate до настройки алертов через Alertmanager и Grafana.
1. Продвинутые метрики: не только HTTP-запросы
Базовый мониторинг (количество запросов, задержки) — это минимум. На продвинутом уровне мы собираем метрики, которые отражают внутреннее состояние приложения: размер очередей, количество активных задач в Tokio, состояние пула соединений.
1.1. Кастомные метрики с типом Histogram и Gauge
Используем крейт prometheus. Рассмотрим пример сбора метрик для пула соединений к базе данных:
use prometheus::{HistogramOpts, HistogramVec, Gauge, register_histogram_vec, register_gauge};
use lazy_static::lazy_static;
lazy_static! {
static ref DB_CONNECTION_POOL: Gauge = register_gauge!(
"db_connection_pool_size",
"Текущее количество активных соединений в пуле"
).unwrap();
static ref DB_QUERY_DURATION: HistogramVec = register_histogram_vec!(
HistogramOpts::new("db_query_duration_seconds", "Время выполнения запроса к БД"),
&["query_type", "status"]
).unwrap();
}
// Использование в коде
fn execute_query(query_type: &str) -> Result<(), String> {
DB_CONNECTION_POOL.inc();
let start = std::time::Instant::now();
// ... выполнение запроса ...
let duration = start.elapsed().as_secs_f64();
DB_QUERY_DURATION.with_label_values(&[query_type, "success"]).observe(duration);
DB_CONNECTION_POOL.dec();
Ok(())
}
Важно: Используйте HistogramVec для категоризации запросов по типу (SELECT, INSERT) и статусу (success, error). Это позволит строить тепловые карты в Grafana.
1.2. Метрики изнутри Tokio runtime
Для асинхронных приложений критично знать загруженность планировщика. Используем tokio-metrics:
use tokio_metrics::RuntimeMetrics;
use std::sync::Arc;
async fn monitor_runtime(metrics: Arc<RuntimeMetrics>) {
loop {
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
let num_alive_tasks = metrics.num_alive_tasks();
let blocking_threads = metrics.num_blocking_threads();
// Отправляем эти значения в Prometheus через Gauge
println!("Активные задачи: {}, Блокирующие потоки: {}", num_alive_tasks, blocking_threads);
}
}
Эти метрики помогают выявить утечки задач (когда num_alive_tasks постоянно растет) или избыточное использование block_in_place.
2. Архитектура сбора метрик: Push vs Pull
В Rust-экосистеме чаще используется Pull-модель (Prometheus scraper), но для короткоживущих процессов (CLI-утилиты, парсеры) удобен Push-подход через Pushgateway.
2.1. Pull-модель: HTTP-эндпоинт
Стандартный способ — запустить отдельный HTTP-сервер на порту 9090 (или другом) внутри вашего приложения:
use warp::Filter;
use prometheus::{Encoder, TextEncoder};
async fn metrics_handler() -> Result<impl warp::Reply, warp::Rejection> {
let metric_families = prometheus::gather();
let mut buffer = Vec::new();
let encoder = TextEncoder::new();
encoder.encode(&metric_families, &mut buffer).unwrap();
Ok(warp::reply::with_header(buffer, "Content-Type", "text/plain; charset=utf-8"))
}
#[tokio::main]
async fn main() {
let metrics_route = warp::path!("metrics").and_then(metrics_handler);
warp::serve(metrics_route).run(([0, 0, 0, 0], 9090)).await;
}
2.2. Push-модель для batch-задач
Если ваше приложение запускается раз в час и завершается, используйте prometheus-pushgateway:
use prometheus_pushgateway::PushGateway;
fn push_metrics(job_name: &str) {
let gateway = PushGateway::new("http://pushgateway:9091");
let metric_families = prometheus::gather();
gateway.push_add(metric_families, job_name, &std::collections::HashMap::new()).unwrap();
}
3. Настройка алертов: от порогов до аномалий
Алерты в Prometheus (Alertmanager) — это язык PromQL. Для Rust-приложений типичны следующие правила:
3.1. Алерт на высокую задержку запросов (p99)
Создайте файл alerts.yml:
groups:
- name: rust_app_alerts
rules:
- alert: HighQueryLatency
expr: histogram_quantile(0.99, rate(db_query_duration_seconds_bucket[5m])) > 2.0
for: 5m
labels:
severity: critical
annotations:
summary: "P99 задержка запросов к БД превышает 2 секунды"
description: "Текущее значение: {{ $value }} с"
3.2. Алерт на утечку задач Tokio
Если количество активных задач не уменьшается в течение 10 минут:
- alert: TokioTaskLeak
expr: avg_over_time(runtime_tasks_alive[10m]) > 1000
for: 10m
labels:
severity: warning
annotations:
summary: "Подозрение на утечку задач Tokio"
description: "Среднее количество задач за 10 мин: {{ $value }}"
3.3. Интеграция с Alertmanager
Настройте маршруты для отправки уведомлений в Telegram/Slack:
route:
receiver: 'telegram'
routes:
- match:
severity: critical
receiver: 'telegram-critical'
receivers: