infra: expose monitoring service via monitor.fxhnt.ai Tailscale proxy

Add DNS record, nginx gRPC proxy block, network policy for Tailscale
ingress, and auto-detect fxhnt.ai in fxt monitor URL derivation.
Show GPU telemetry even when no training sessions are active.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-03-02 22:56:40 +01:00
parent d9b266e1d3
commit 6b67f6193a
5 changed files with 51 additions and 15 deletions

View File

@@ -33,9 +33,16 @@ pub struct MonitorCommand {
impl MonitorCommand {
/// Execute the monitor command -- connect to monitoring service and display metrics
pub async fn run(&self, api_gateway_url: &str, _jwt_token: &str) -> Result<()> {
// monitoring_service runs on a different port -- derive URL from env or default
let monitoring_url = std::env::var("MONITORING_SERVICE_URL")
.unwrap_or_else(|_| api_gateway_url.replace(":50050", ":50057"));
// monitoring_service runs on a different port -- derive URL from env or default.
// When connecting via Tailscale (*.fxhnt.ai), use monitor.fxhnt.ai:443 (nginx proxy).
// Otherwise, replace the api-gateway port with the monitoring port.
let monitoring_url = std::env::var("MONITORING_SERVICE_URL").unwrap_or_else(|_| {
if api_gateway_url.contains("fxhnt.ai") {
"https://monitor.fxhnt.ai".to_owned()
} else {
api_gateway_url.replace(":50050", ":50057")
}
});
let mut client = MonitoringServiceClient::connect(monitoring_url)
.await
@@ -72,11 +79,6 @@ impl MonitorCommand {
}
fn render_snapshot(resp: &GetLiveTrainingMetricsResponse) {
if resp.sessions.is_empty() {
println!("No active training sessions found.");
return;
}
if let Some(gpu) = &resp.gpu {
println!(
"GPU: {:.0}% util | {:.1}/{:.1} GB VRAM | {:.0}C | {:.0}W",
@@ -90,6 +92,11 @@ fn render_snapshot(resp: &GetLiveTrainingMetricsResponse) {
println!("Active K8s training jobs: {}", resp.active_k8s_jobs);
println!();
if resp.sessions.is_empty() {
println!("No active training sessions with metrics.");
return;
}
print_session_table(&resp.sessions);
print_hyperopt_summary(&resp.sessions);
print_health_summary(&resp.sessions);

View File

@@ -253,3 +253,21 @@ data:
grpc_connect_timeout 30s;
}
}
# Monitoring Service (gRPC) — monitor.fxhnt.ai
server {
listen 443 ssl;
http2 on;
server_name monitor.fxhnt.ai;
ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
grpc_pass grpc://monitoring-service.foxhunt.svc.cluster.local:50057;
grpc_read_timeout 3600s;
grpc_send_timeout 3600s;
grpc_connect_timeout 30s;
}
}

View File

@@ -11,16 +11,19 @@ spec:
- Ingress
- Egress
ingress:
# gRPC from web-gateway
# gRPC from web-gateway and Tailscale proxy (monitor.fxhnt.ai)
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: web-gateway
- podSelector:
matchLabels:
app.kubernetes.io/name: tailscale-gitlab-proxy
ports:
- protocol: TCP
port: 50057
egress:
# Prometheus HTTP API (port 80)
# Prometheus HTTP API
- to:
- podSelector:
matchLabels:
@@ -28,10 +31,5 @@ spec:
ports:
- protocol: TCP
port: 80
- to:
- podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- protocol: TCP
port: 9090

View File

@@ -39,3 +39,11 @@ resource "scaleway_domain_record" "api" {
data = var.git_ip
ttl = 300
}
resource "scaleway_domain_record" "monitor" {
dns_zone = var.dns_zone
name = "monitor"
type = "A"
data = var.git_ip
ttl = 300
}

View File

@@ -17,3 +17,8 @@ output "api_fqdn" {
description = "FQDN for the API Gateway (gRPC)"
value = "${scaleway_domain_record.api.name}.${scaleway_domain_record.api.dns_zone}"
}
output "monitor_fqdn" {
description = "FQDN for the Monitoring Service (gRPC)"
value = "${scaleway_domain_record.monitor.name}.${scaleway_domain_record.monitor.dns_zone}"
}