Метрики /metrics для центрального Prometheus + генерация scrape-таргетов

--metrics-port (0.0.0.0, в отличие от --health-port — тот 127.0.0.1-only)
отдаёт netrunner_connections_active/total, netrunner_auth_validate_duration_seconds,
netrunner_auth_failures_total, netrunner_circuit_breaker_open. Тот же
hand-rolled HTTP-паттерн, что и health.rs — без HTTP-фреймворка ради
одного эндпоинта.

scripts/generate-prometheus-targets.mjs генерит список scrape-таргетов
из nodes.json — job update-observability-targets в deploy.yml гоняет
его при каждом деплое нод и заливает на VPS с данными
(netrunner-data/observability/targets/nodes.json), чтобы центральный
Prometheus не терял из виду ноды без ручной правки конфига.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 23:21:26 +07:00
parent fafd573a28
commit cfcb1afcec
11 changed files with 367 additions and 13 deletions
+26 -1
View File
@@ -13,6 +13,7 @@
mod backend_client;
mod diagnostics;
mod health;
mod metrics_server;
mod network;
use clap::Parser;
use netrunner_core::net::AuthValidator;
@@ -88,6 +89,13 @@ struct Args {
/// этой же машине). Не задан — health-эндпоинт выключен.
#[arg(long)]
health_port: Option<u16>,
/// Порт для `/metrics` (Prometheus text exposition) — в отличие от
/// `--health-port`, биндится на 0.0.0.0 (нужен для скрейпа удалённым
/// центральным Prometheus), ОБЯЗАТЕЛЬНО зафайрволить на IP
/// observability-VPS. Не задан — метрики выключены.
#[arg(long)]
metrics_port: Option<u16>,
}
fn main() {
@@ -106,8 +114,15 @@ fn main() {
None
};
// Регистрируется один раз, до первого metrics::counter!/gauge!/histogram! —
// если --metrics-port не задан, вызовы макросов молча уходят в
// no-op recorder по умолчанию (штатное поведение крейта metrics).
let metrics_handle = args
.metrics_port
.map(|_| metrics_server::install_recorder());
let net = Network::new(
args.host,
args.host.clone(),
args.port,
args.decoy_host,
auth,
@@ -128,6 +143,16 @@ fn main() {
net.run(run_token).await;
});
if let (Some(port), Some(handle)) = (args.metrics_port, metrics_handle) {
let metrics_token = token.clone();
tokio::spawn(metrics_server::run(
"0.0.0.0".to_string(),
port,
handle,
metrics_token,
));
}
shutdown_signal(token).await;
if let Err(e) = run_handle.await {
error!(error = ?e, "Задача сервера завершилась с паникой при остановке");