Lonjakan status HTTP 503 Service Unavailable saat proses rolling update di Kubernetes umumnya bersumber dari dua kesalahan konfigurasi: traffic dialihkan ke pod sebelum pool koneksi database siap menerima query, atau Kubelet menghentikan pod sehat karena liveness probe mengalami timeout saat beban kerja tinggi.
Panduan ini membahas arsitektur health check yang tepat pada aplikasi Rust berbasis Actix Web, memisahkan logika liveness dan readiness secara ketat, serta sinkronisasi lifecycle pod dengan Kubernetes Endpoints controller.
Postmortem: Mengapa HTTP 503 Muncul Saat Deployment?
Saat Deployment menjalankan pembaruan pod via strategi RollingUpdate, dua masalah race condition sering terjadi secara bersamaan:
- Traffic mendahului inisialisasi: Container Actix Web aktif dan port HTTP terbuka, tetapi inisialisasi pool database (misalnya SQLx atau Deadpool) masih berlangsung atau menunggu alokasi koneksi awal. Kubelet menganggap pod siap dan Endpoint Controller langsung mendaftarkan IP pod ke Service iptables/IPVS. Request pertama dari klien gagal dengan status 500 atau 503.
- Anti-Pattern Liveness Probe dengan DB Ping: Endpoint liveness mengecek koneksi database via query
SELECT 1. Ketika database mengalami lonjakan beban sesaat, latency query meningkat melampauitimeoutSecondsliveness probe. Kubelet membunuh worker pod yang sebenarnya masih sehat dan sedang memproses request. Ini memicu kaskade pod restart berulang (crash loop semu). - SIGTERM mendahului deregistrasi endpoint: Kubelet mengirim sinyal
SIGTERMke pod lama pada saat yang hampir sama dengan pengiriman sinyal update IP ke kube-proxy di node-node lain. Traffic masih dikirim ke pod lama yang sudah mulai mematikan server socket-nya.
Pemisahan Tanggung Jawab: /livez vs /readyz
Dua probe Kubernetes memiliki tujuan operasional yang berbeda mendasar:
- Liveness Probe (
/livez): Bertujuan mendeteksi deadlocks atau event loop yang macet total. Sifat operasi ini harus zero-I/O. Jangan pernah menyertakan pengecekan database, cache, atau network hop lain di sini. Jika endpoint ini gagal, Kubelet akan me-restart container. - Readiness Probe (
/readyz): Bertujuan memvalidasi apakah aplikasi sanggup melayani traffic klien. Probe ini memeriksa status koneksi downstream (seperti database SQLx) dengan batas timeout yang ketat. Jika endpoint ini gagal, Kubelet hanya mencopot IP pod dari Service Endpoint. Container tetap hidup dan diberi kesempatan untuk memulihkan koneksi atau menyelesaikan inisialisasi.
Implementasi Handler pada Actix Web
Berikut implementasi endpoint /livez dan /readyz menggunakan Actix Web dan SQLx PostgreSQL. Cek koneksi di-wrap menggunakan tokio::time::timeout untuk mencegah probe worker menggantung jika pool kehabisan slot.
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
use sqlx::PgPool;
use std::time::Duration;
struct AppState {
db: PgPool,
}
// Zero I/O: hanya memvalidasi event loop Actix masih responsif
#[get("/livez")]
async fn liveness_handler() -> impl Responder {
HttpResponse::Ok().body("OK")
}
// Non-blocking timeout check terhadap database pool
#[get("/readyz")]
async fn readiness_handler(data: web::Data<AppState>) -> impl Responder {
let timeout_duration = Duration::from_millis(800);
let db_check = tokio::time::timeout(
timeout_duration,
sqlx::query("SELECT 1").execute(&data.db),
)
.await;
match db_check {
Ok(Ok(_)) => HttpResponse::Ok().body("READY"),
Ok(Err(e)) => {
eprintln!("Readiness DB query failed: {:?}", e);
HttpResponse::ServiceUnavailable().body("DB Unreachable")
}
Err(_) => {
eprintln!("Readiness DB check timed out");
HttpResponse::ServiceUnavailable().body("DB Check Timeout")
}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let database_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
let pool = sqlx::postgres::PgPoolOptions::new()
.max_connections(20)
.acquire_timeout(Duration::from_secs(3))
.connect(&database_url)
.await
.expect("Failed to create pool");
let app_state = web::Data::new(AppState { db: pool });
HttpServer::new(move || {
App::new()
.app_data(app_state.clone())
.service(liveness_handler)
.service(readiness_handler)
})
.bind(("0.0.0.0", 8080))?
.run()
.await
}Catatan: Parametertimeout_durationpadatokio::time::timeoutharus lebih rendah daripadatimeoutSecondsyang ditentukan di Kubernetes spec untuk menghindari socket hang pada Kubelet.
Konfigurasi Kubernetes Deployment Spec
Konfigurasikan pod probe beserta preStop hook. Hook preStop memberi jeda waktu agar kube-proxy di seluruh worker node sempat mencopot IP pod dari tabel routing sebelum server Actix Web menerima sinyal shutdown (SIGTERM).
apiVersion: apps/v1
kind: Deployment
metadata:
name: actix-api
labels:
app: actix-api
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: actix-api
template:
metadata:
labels:
app: actix-api
spec:
containers:
- name: api
image: my-registry/actix-api:v1.2.0
ports:
- containerPort: 8080
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 5"]
livenessProbe:
httpGet:
path: /livez
port: 8080
initialDelaySeconds: 2
periodSeconds: 10
timeoutSeconds: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 3
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 2Analisis Parameter Kritis
maxUnavailable: 0: Memaksa Kubernetes untuk meluncurkan pod baru dan memastikan statusnyaReadysebelum menghapus pod lama.preStop: sleep 5: Menghentikan eksekusi SIGTERM langsung ke container selama 5 detik. Dalam durasi ini, Pod berstatusTerminatingdan Endpoint controller menghapus pod dari daftar target upstream Service/Ingress secara konsisten.initialDelaySecondspada readiness: Memberi toleransi waktu bootstrapping worker threads Actix Web dan koneksi pool TCP awal sebelum evaluasi dimulai.
Verifikasi Rollout Tanpa Downtime
Untuk memastikan tidak ada 503 selama deployment, lakukan stress testing concurrently saat rolling update berlangsung menggunakan tool seperti hey atau k6.
Jalankan load generator pada endpoint bisnis yang memerlukan query DB:
hey -z 30s -q 50 -c 10 http://api.internal.local/v1/resourceDi terminal terpisah, trigger proses rolling restart:
kubectl rollout restart deployment/actix-api
kubectl rollout status deployment/actix-apiEvaluasi hasil output benchmark. Bila konfigurasi berhasil, seluruh request harus menghasilkan status [200] tanpa kemunculan respon [503] atau connection reset by peer.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!