Deployment modern sering kali gagal mendeteksi regresi sistem jika hanya mengandalkan health check standar HTTP /healthz. Pod Kubernetes dapat melaporkan status Running karena endpoint liveness tetap mengembalikan respons 200 OK, sementara seluruh traffic bisnis mengalami lonjakan error 5xx atau degradasi p99 latency secara tajam. Masalah ini membutuhkan sinyal observabilitas tingkat aplikasi yang presisi.

Artikel ini membahas konfigurasi middleware observabilitas pada Actix Web menggunakan tracing-actix-web dan actix-web-prom, pengelolaan metrik Service Level Indicator (SLI), isolasi endpoint metrik dari traffic publik, serta penggunaannya sebagai pemicu rollback otomatis di lingkungan container.

1. Problem: Blind Spot Health Check dan Sinyal Rollback

Health check dasar bersifat biner: proses hidup atau mati. Metrik ini gagal menangkap dua masalah utama pasca-release:

  • Silent 5xx Errors: Bug logika atau kesalahan konfigurasi runtime yang hanya terpicu saat menerima beban spesifik pengguna.
  • Latensi Degradatif (p99 spike): Query database tanpa index atau resource leak yang memperlambat sebagian request secara eksponensial tanpa langsung memutus koneksi.

Untuk menerapkan automated rollback yang andal, pipeline deployment memerlukan dua Service Level Indicator (SLI) primer: Error Rate SLI (rasio HTTP status 5xx terhadap total request) dan Latency SLI (persentil p99 durasi pemrosesan request).

2. Implementasi Middleware Instrumentasi

Instrumentasi pada Actix Web membutuhkan pelacakan terdistribusi (tracing) untuk triage dan metrik agregat (Prometheus) untuk alarm. Gunakan crate tracing-actix-web dan actix-web-prom pada Cargo.toml:

[dependencies]
actix-web = "4.9"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
tracing-actix-web = "0.7"
actix-web-prom = "0.9"

Inisialisasi subscriber tracing dan daftarkan middleware ke dalam aplikasi. Pisahkan registry metrik agar dapat diakses oleh server metrik secara terisolasi.

use actix_web::{web, App, HttpResponse, HttpServer, Responder};
use actix_web_prom::PrometheusMetricsBuilder;
use tracing_actix_web::TracingLogger;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};

async fn get_user(path: web::Path<u32>) -> impl Responder {
    let user_id = path.into_inner();
    // Simulasi respons aplikasi
    HttpResponse::Ok().json(serde_json::json!({ "id": user_id, "name": "Rustacean" }))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Inisialisasi tracing JSON output untuk log agregator
    tracing_subscriber::registry()
        .with(tracing_subscriber::EnvFilter::new("info"))
        .with(tracing_subscriber::fmt::layer().json())
        .init();

    // Konfigurasi Prometheus Middleware
    let prometheus = PrometheusMetricsBuilder::new("api")
        .endpoint("/metrics")
        .build()
        .expect("Gagal inisialisasi Prometheus metrics");

    // ponytail: port tunggal dipilih untuk kesederhanaan, pisahkan port jika ingress terekspos langsung ke publik
    HttpServer::new(move || {
        App::new()
            .wrap(prometheus.clone())
            .wrap(TracingLogger::default())
            .route("/users/{id}", web::get().to(get_user))
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

TracingLogger menghasilkan trace ID unik per request (menggunakan header x-request-id jika tersedia), sedangkan PrometheusMetrics secara otomatis mencatat counter request, status code, dan histogram durasi request.

3. Isolasi Rute Metrik dan Pencegahan Ingress Leak

Mengekspos /metrics pada port yang sama dengan traffic bisnis meningkatkan risiko keamanan (eksposur data internal sistem) dan beban DDoS mikro yang mengganggu request pengguna. Pola yang lebih baik adalah menjalankan server internal kedua pada port terpisah untuk scraping Prometheus:

use actix_web::{App, HttpServer};
use actix_web_prom::PrometheusMetricsBuilder;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let prometheus = PrometheusMetricsBuilder::new("api")
        .endpoint("/metrics")
        .build()
        .unwrap();

    let metrics_middleware = prometheus.clone();

    // Server aplikasi bisnis (Port 8080)
    let app_server = HttpServer::new(move || {
        App::new()
            .wrap(metrics_middleware.clone())
            .service(/* rute bisnis */)
    })
    .bind(("0.0.0.0", 8080))?
    .run();

    // Server internal monitoring (Port 9090) - Hanya diakses scraper K8s
    let metric_server = HttpServer::new(move || {
        App::new()
            .wrap(prometheus.clone())
    })
    .bind(("0.0.0.0", 9090))?
    .run();

    // Jalankan kedua event loop konkuren
    futures::try_join!(app_server, metric_server)?;
    Ok(())
}

4. Pencegahan Memory Bloat: Masalah High-Cardinality

Salah satu penyebab kegagalan umum aplikasi Rust berbasis Prometheus adalah lonjakan alokasi memori (OOM kill) akibat high-cardinality labels. Jika endpoint Anda mencatat URI riil tanpa normalisasi pola route, setiap request ke ID unik akan membuat metrik time-series baru di RAM:

# CONTOH BURUK (High Cardinality - Memory Leaks):
http_requests_total{endpoint="/users/12345"} 1
http_requests_total{endpoint="/users/12346"} 1

# CONTOH BENAR (Route Template Matched):
http_requests_total{endpoint="/users/{id}"} 2

Pastikan middleware dikonfigurasi untuk mengekstrak path pattern terdaftar (seperti /users/{id}) bukan raw path (/users/12345). actix-web-prom secara bawaan menggunakan matched pattern Actix Web. Namun, hindari penggunaan query parameter atau header acak sebagai custom labels pada metrik.

5. Integrasi Metrik SLI sebagai Threshold Rollback Kubernetes

Dengan metrik yang diekspor secara akurat, definisikan SLI menggunakan PromQL untuk mendeteksi degradasi deployment canary di Kubernetes (misalnya menggunakan Argo Rollouts atau Flagger).

PromQL: Error Rate SLI

Rasio request 5xx terhadap total request selama interval 2 menit:

sum(rate(api_http_requests_total{status=~"5.."}[2m]))
/
sum(rate(api_http_requests_total[2m])) > 0.02

Threshold: Rollback dipicu jika error rate melebihi 2% (0.02) selama fase canary.

PromQL: Latency p99 SLI

Persentil ke-99 durasi eksekusi request:

histogram_quantile(0.99, sum(rate(api_http_requests_duration_seconds_bucket[2m])) by (le)) > 0.75

Threshold: Rollback dipicu jika 99% request memerlukan waktu lebih dari 750ms.

Contoh Argo Rollouts AnalysisTemplate

apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: actix-web-sli-check
spec:
  metrics:
  - name: error-rate-sli
    interval: 30s
    failureLimit: 2
    provider:
      prometheus:
        address: http://prometheus-k8s.monitoring:9090
        query: |
          sum(rate(api_http_requests_total{status=~"5..", app="order-service"}[1m]))
          /
          sum(rate(api_http_requests_total{app="order-service"}[1m]))
    successCondition: result[0] < 0.02
  - name: latency-p99-sli
    interval: 30s
    failureLimit: 2
    provider:
      prometheus:
        address: http://prometheus-k8s.monitoring:9090
        query: |
          histogram_quantile(0.99, sum(rate(api_http_requests_duration_seconds_bucket{app="order-service"}[1m])) by (le))
    successCondition: result[0] < 0.75

Jika kondisi gagal melampaui failureLimit: 2 berturut-turut, controller secara otomatis membatalkan canary deployment dan mengembalikan routing ke replicaset versi stabil tanpa intervensi manual.

6. Triage Postmortem: Bug Aplikasi vs Upstream Failure

Ketika automated rollback terpicu, gunakan korelasi trace ID dan metrik untuk mempercepat postmortem:

  • Koneksi Upstream Gagal: Status code adalah 502/504 atau internal 500 dengan pesan error koneksi pool (misal: PoolTimedOut pada database r2d2/deadpool). Histogram durasi menunjukkan request tertahan tepat pada batas timeout (misal: tepat 5000ms). Tracing span upstream client (HTTP/DB) menunjukkan span berakhir tanpa response.
  • Bug Aplikasi Internal: Status code 500 langsung dikembalikan dalam hitungan sub-milidetik (panik Rust, unwrap pada None, atau validasi invariant gagal). Tracing log langsung mencetak target backtrace tanpa span upstream eksternal.

Menerapkan instrumentasi terpadu antara tracing terdistribusi dan metrik SLI presisi mengubah proses release dari spekulatif menjadi berbasis data observabilitas kuantitatif.