Blue-green deploy untuk API Rust adalah pola rilis yang meminimalkan risiko saat versi baru dipublikasikan. Intinya, Anda menjalankan dua environment identik—blue dan green—lalu mengalihkan trafik hanya setelah versi baru lolos verifikasi. Jika ada masalah, rollback cukup dengan memindahkan trafik kembali ke environment lama, tanpa perlu membangun ulang aplikasi di saat insiden berlangsung.

Pada artikel ini, fokusnya bukan teori deployment, tetapi implementasi praktis: layanan HTTP Rust berbasis Axum, endpoint health dan readiness, tracing, log terstruktur, metrik latensi dan error rate, alur cutover, indikator rollback, serta checklist pasca-insiden. Pendekatan yang sama juga bisa diterapkan pada Actix Web, tetapi contoh kode di sini memakai Axum agar sederhana dan langsung ke inti.

Mengapa Blue-Green Cocok untuk API Rust

Untuk API backend, risiko deploy biasanya bukan hanya proses build, tetapi perubahan perilaku runtime: koneksi database, timeout upstream, perubahan skema respons, lonjakan latensi, atau bug yang hanya muncul saat trafik nyata masuk. Blue-green membantu karena:

  • Versi lama tetap aktif selama verifikasi versi baru.
  • Rollback cepat cukup dengan mengubah arah trafik di load balancer, reverse proxy, atau service mesh.
  • Verifikasi lebih aman karena environment baru bisa diuji sebelum menerima trafik publik.
  • Observability lebih jelas karena metrik dan log bisa dipisahkan antara blue dan green.

Trade-off-nya, biaya infrastruktur sedikit lebih tinggi karena dua environment berjalan bersamaan. Selain itu, pola ini menuntut disiplin pada migrasi database, manajemen konfigurasi, dan pengecekan kompatibilitas antarversi.

Arsitektur Sederhana untuk Blue-Green Deploy API Rust

Struktur minimumnya seperti berikut:

  • Load balancer / reverse proxy mengarah ke environment aktif.
  • Blue: versi API saat ini yang melayani trafik produksi.
  • Green: versi API baru yang sudah dideploy tetapi belum menerima trafik publik.
  • Database bersama dengan migrasi yang kompatibel maju-mundur sejauh mungkin.
  • Observability stack: tracing, log terstruktur, dan metrik.

Alur dasarnya:

  1. Deploy versi baru ke green.
  2. Jalankan health check, readiness check, dan smoke test.
  3. Verifikasi metrik awal: startup sukses, koneksi database stabil, error rate normal.
  4. Cutover trafik dari blue ke green.
  5. Pantau latensi, error rate, dan log selama beberapa menit pertama.
  6. Jika indikator memburuk, rollback ke blue.

Catatan: Blue-green bukan pengganti pengujian. Ia mengurangi dampak saat rilis, tetapi tidak menghapus kebutuhan untuk test otomatis, staging, dan review migrasi data.

Membangun API Rust dengan Health Check, Readiness, dan Tracing

Contoh service Axum

Contoh berikut menunjukkan API sederhana dengan endpoint /health, /ready, dan satu endpoint bisnis. Tracing digunakan untuk menghasilkan log terstruktur dan memudahkan korelasi request saat investigasi insiden.

use axum::{
    extract::State,
    http::StatusCode,
    response::IntoResponse,
    routing::get,
    Json, Router,
};
use serde::Serialize;
use std::{net::SocketAddr, sync::Arc, time::Duration};
use tokio::net::TcpListener;
use tower_http::trace::TraceLayer;
use tracing::{error, info, instrument};

#[derive(Clone)]
struct AppState {
    app_name: String,
    environment: String,
    db: Arc<FakeDb>,
}

struct FakeDb;
impl FakeDb {
    async fn ping(&self) -> bool {
        true
    }
}

#[derive(Serialize)]
struct HealthResponse {
    status: String,
    service: String,
    environment: String,
}

#[derive(Serialize)]
struct ReadyResponse {
    status: String,
}

async fn health(State(state): State<AppState>) -> impl IntoResponse {
    Json(HealthResponse {
        status: "ok".into(),
        service: state.app_name,
        environment: state.environment,
    })
}

#[instrument(skip(state))]
async fn ready(State(state): State<AppState>) -> impl IntoResponse {
    if state.db.ping().await {
        (StatusCode::OK, Json(ReadyResponse { status: "ready".into() }))
    } else {
        error!("database ping failed");
        (
            StatusCode::SERVICE_UNAVAILABLE,
            Json(ReadyResponse {
                status: "not_ready".into(),
            }),
        )
    }
}

#[instrument]
async fn users() -> impl IntoResponse {
    (StatusCode::OK, Json(vec!["alice", "bob"]))
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .json()
        .with_target(false)
        .init();

    let state = AppState {
        app_name: "user-api".into(),
        environment: std::env::var("APP_ENV").unwrap_or_else(|_| "green".into()),
        db: Arc::new(FakeDb),
    };

    let app = Router::new()
        .route("/health", get(health))
        .route("/ready", get(ready))
        .route("/users", get(users))
        .with_state(state)
        .layer(TraceLayer::new_for_http());

    let addr: SocketAddr = "0.0.0.0:3000".parse().unwrap();
    let listener = TcpListener::bind(addr).await.unwrap();

    info!(%addr, "server started");
    axum::serve(listener, app).await.unwrap();
}

Perbedaan health check dan readiness

  • /health menjawab pertanyaan: proses hidup atau tidak.
  • /ready menjawab pertanyaan: proses siap menerima trafik atau belum.

Kesalahan yang sering terjadi adalah menyamakan keduanya. Jika /health ikut memeriksa semua dependensi berat, instance bisa dianggap mati padahal hanya ada gangguan sementara pada upstream. Praktik yang lebih aman adalah membuat /health ringan, sementara /ready memeriksa dependency penting seperti database atau konektivitas internal yang memang dibutuhkan untuk melayani request.

Log terstruktur dan tracing

Untuk rollback cepat, observability harus membantu menjawab pertanyaan ini dalam hitungan menit:

  • Endpoint mana yang mulai gagal?
  • Error terjadi di semua request atau hanya rute tertentu?
  • Latensi naik di aplikasi, database, atau upstream?
  • Apakah hanya environment green yang bermasalah?

Karena itu, log sebaiknya berbentuk JSON dan membawa konteks seperti request_id, environment, method, path, status_code, serta error yang relevan. Jika Anda memakai reverse proxy di depan aplikasi, teruskan header korelasi seperti x-request-id agar pencarian lintas sistem lebih mudah.

Metrik Minimum yang Wajib Dipantau Saat Cutover

Untuk rollout aman, jangan menunggu laporan manual dari pengguna. Pantau metrik dasar berikut sejak environment green mulai menerima trafik:

  • Latency: idealnya p50/p95 atau setidaknya rata-rata dan puncak.
  • Error rate: proporsi respons 5xx dan peningkatan 4xx yang tidak biasa.
  • Request throughput: memastikan trafik benar-benar berpindah sesuai rencana.
  • Readiness failure: apakah instance sering keluar dari status siap.
  • Dependency health: misalnya error koneksi DB, timeout HTTP upstream, atau pool exhaustion.

Jika stack Anda sudah memakai Prometheus, buat metrik HTTP request duration dan request total per status code. Bila belum, mulai dari yang sederhana: log terstruktur plus dashboard dari reverse proxy juga cukup membantu selama metrik inti terlihat per environment.

# Contoh target verifikasi operasional, bukan angka baku universal
- /ready harus stabil mengembalikan 200
- error 5xx tidak boleh meningkat tajam setelah cutover
- latensi endpoint utama tidak boleh melonjak signifikan
- koneksi database dan upstream tidak menunjukkan timeout berulang

Hindari membuat keputusan rollback hanya dari satu request gagal. Lihat pola dalam jendela waktu singkat, misalnya beberapa menit pertama setelah cutover.

Konfigurasi Sederhana Blue-Green di Reverse Proxy

Contoh paling sederhana adalah memakai reverse proxy yang mengarah ke upstream blue atau green. Ide utamanya bukan pada sintaks spesifik, melainkan fakta bahwa perpindahan trafik harus bisa dilakukan cepat dan dapat dibalik dengan aman.

upstream api_blue {
    server 10.0.1.10:3000;
}

upstream api_green {
    server 10.0.1.20:3000;
}

server {
    listen 80;

    location /healthz-proxy {
        return 200 "ok";
    }

    location / {
        proxy_pass http://api_green;
        proxy_set_header Host $host;
        proxy_set_header X-Request-Id $request_id;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Saat rollback, perubahan cukup diarahkan kembali ke api_blue. Pada sistem yang lebih matang, cutover biasanya dilakukan melalui load balancer, ingress controller, atau service mesh. Prinsipnya tetap sama: ubah routing, bukan bangun ulang sistem saat insiden.

Alur Deploy Praktis: Dari Green hingga Cutover

1. Deploy ke green tanpa menerima trafik publik

Setelah image atau binary baru tersedia, jalankan di environment green dengan konfigurasi produksi yang setara. Jangan ganti terlalu banyak variabel sekaligus; perubahan kode dan perubahan konfigurasi besar dalam satu deploy akan menyulitkan diagnosis bila terjadi masalah.

2. Verifikasi startup dan readiness

Minimal cek:

  • Proses berhasil start.
  • /health mengembalikan 200.
  • /ready mengembalikan 200 secara konsisten.
  • Koneksi database berhasil.
  • Log startup tidak menunjukkan panic, migration error, atau timeout berulang.

3. Jalankan smoke test

Smoke test sebaiknya menyentuh jalur kritis, bukan sekadar endpoint ping. Misalnya:

  • GET endpoint publik utama.
  • POST endpoint yang memerlukan validasi.
  • Satu skenario yang membaca database.
  • Jika ada autentikasi, satu request dengan token valid.
curl -fsS http://green.internal:3000/health
curl -fsS http://green.internal:3000/ready
curl -fsS http://green.internal:3000/users

Jika API Anda punya kontrak respons yang sensitif, tambahkan pengecekan field penting agar perubahan tak sengaja segera terdeteksi.

4. Pastikan migrasi database aman

Ini bagian yang paling sering menggagalkan rollback. Untuk blue-green, usahakan migrasi database mengikuti prinsip expand and contract:

  • Tambahkan kolom atau tabel baru dulu tanpa menghapus yang lama.
  • Buat kode baru kompatibel dengan skema lama selama masa transisi.
  • Setelah seluruh trafik stabil di versi baru, baru lakukan pembersihan skema lama.

Hindari migrasi yang langsung menghapus kolom yang masih dipakai versi lama. Kalau itu dilakukan, rollback aplikasi bisa berhasil secara routing tetapi gagal secara fungsi karena versi lama tidak lagi cocok dengan skema database.

5. Cutover trafik bertahap atau langsung

Dalam blue-green murni, cutover sering dilakukan langsung dari 0% ke 100%. Jika sistem Anda mendukung pengalihan bertahap, itu lebih aman untuk endpoint berisiko tinggi. Namun jika tooling Anda hanya mendukung switch penuh, pastikan observability dan rollback benar-benar siap sebelum tombol dipindahkan.

Kapan Harus Rollback

Rollback harus didasarkan pada indikator yang jelas, bukan intuisi sesaat. Beberapa pemicu yang layak dipakai:

  • Error rate naik konsisten, terutama 5xx pada endpoint utama.
  • Latency meningkat signifikan dibanding baseline versi sebelumnya.
  • Readiness flapping: instance bergantian siap dan tidak siap.
  • Timeout dependency meningkat setelah cutover.
  • Bug fungsi kritis terdeteksi dari smoke test produksi atau laporan internal.
  • Konsumsi resource tidak wajar seperti memory melonjak atau CPU tinggi terus-menerus.

Aturan praktis: jika masalahnya jelas muncul setelah cutover dan berdampak pada request nyata, lebih aman rollback dulu lalu investigasi. Jangan menjadikan produksi sebagai tempat debugging panjang jika rollback bisa dilakukan cepat.

Langkah Rollback Minim Downtime

  1. Bekukan perubahan lain. Jangan deploy hotfix sambil trafik masih tidak stabil kecuali prosedurnya sangat matang.
  2. Alihkan trafik kembali ke blue di load balancer atau reverse proxy.
  3. Verifikasi blue siap dengan health, readiness, dan metrik dasar.
  4. Pantau error rate dan latency beberapa menit setelah rollback.
  5. Isolasi green untuk investigasi, tetapi biarkan cukup hidup agar log dan metrik tetap bisa diperiksa.

Jika ada request in-flight, pertimbangkan graceful shutdown pada environment yang ditinggalkan agar koneksi aktif sempat selesai, terutama untuk endpoint yang memproses transaksi lebih lama.

Feature Flag dan Pencegahan Risiko Sebelum Deploy

Blue-green kuat, tetapi akan lebih efektif bila digabungkan dengan pencegahan berikut:

Feature flag

Jika perilaku baru berisiko, bungkus dengan feature flag. Dengan begitu, Anda bisa deploy kode lebih dulu, lalu mengaktifkan fitur secara terpisah setelah verifikasi dasar selesai. Ini mengurangi tekanan saat rollback karena tidak semua perubahan harus dibatalkan lewat cutover penuh.

Smoke test otomatis

Jangan mengandalkan pengecekan manual saja. Jadikan smoke test bagian dari pipeline deploy atau runbook. Minimal harus mencakup endpoint penting dan satu jalur error yang diketahui.

Migration aman

Seperti dibahas sebelumnya, kompatibilitas skema adalah syarat utama rollback cepat. Banyak deploy gagal bukan karena bug aplikasi, tetapi karena versi lama tidak bisa hidup lagi setelah database berubah terlalu agresif.

Alert dasar

Minimal pasang alert untuk:

  • Lonjakan 5xx.
  • Latensi tinggi pada endpoint utama.
  • Readiness gagal berulang.
  • Timeout database atau upstream.

Alert tidak harus kompleks. Yang penting cukup cepat memberi sinyal saat cutover bermasalah.

Runbook Operasional Singkat

Sebelum deploy

  • Pastikan versi baru sudah lulus test dasar.
  • Review perubahan konfigurasi dan environment variable.
  • Pastikan migrasi database aman untuk rollback.
  • Siapkan dashboard metrik per environment.
  • Siapkan perintah cutover dan rollback yang sudah diuji.

Saat deploy ke green

  • Deploy binary/image ke green.
  • Cek /health dan /ready.
  • Jalankan smoke test internal.
  • Cek log startup dan dependency error.

Saat cutover

  • Alihkan trafik ke green.
  • Pantau 5xx, latensi, throughput, dan readiness.
  • Catat waktu cutover untuk memudahkan korelasi log dan metrik.

Jika rollback diperlukan

  • Alihkan trafik kembali ke blue.
  • Verifikasi metrik kembali normal.
  • Simpan bukti: log, trace, dashboard, waktu kejadian, endpoint terdampak.
  • Tunda deploy ulang sampai akar masalah cukup jelas.

Checklist Postmortem Ringan Setelah Insiden

Postmortem tidak harus panjang. Yang penting membantu tim mengurangi peluang kejadian serupa.

  • Apa gejalanya? Misalnya 5xx naik di endpoint tertentu setelah cutover.
  • Kapan mulai terjadi? Catat waktu deploy, waktu cutover, dan waktu rollback.
  • Dampaknya apa? Endpoint terdampak, durasi, dan jenis kegagalan.
  • Deteksi berasal dari mana? Alert, dashboard, smoke test, atau laporan pengguna.
  • Akar masalah sementara? Misalnya query lambat, skema tidak kompatibel, timeout upstream.
  • Apa yang berhasil? Contoh: rollback cepat via switch trafik.
  • Apa yang perlu diperbaiki? Tambah smoke test, alert baru, logging field tertentu, atau strategi migrasi yang lebih aman.

Format ringkas seperti ini cukup untuk banyak tim kecil-menengah, asalkan tindak lanjutnya benar-benar dikerjakan.

Kesalahan Umum dalam Blue-Green Deploy API Rust

  • Readiness terlalu dangkal sehingga instance dianggap siap padahal koneksi database belum stabil.
  • Tidak memisahkan metrik blue dan green, membuat diagnosis saat cutover menjadi kabur.
  • Rollback tidak diuji; prosedurnya ada di dokumen, tetapi belum pernah dicoba.
  • Migrasi database tidak kompatibel, sehingga rollback aplikasi menjadi percuma.
  • Kurang konteks pada log, misalnya tidak ada request id atau environment.
  • Cutover tanpa smoke test, sehingga bug dasar baru terlihat setelah pengguna terkena dampak.

Penutup

Rust: Blue-Green Deploy API dengan Rollback Cepat dan Tracing pada praktiknya bukan soal memilih framework paling canggih, melainkan membangun proses rilis yang bisa diamati, diverifikasi, dan dibatalkan dengan cepat. Dengan endpoint health dan readiness yang benar, tracing dan log terstruktur, metrik latency/error rate, serta runbook rollback yang sederhana, tim bisa menurunkan risiko deploy tanpa memperumit operasi secara berlebihan.

Jika Anda baru mulai, lakukan versi minimum dulu: dua environment, readiness yang memeriksa dependency penting, smoke test, serta satu cara rollback yang benar-benar bisa dijalankan dalam hitungan menit. Setelah itu, barulah tingkatkan dengan feature flag, alert yang lebih presisi, dan postmortem ringan yang konsisten.