Baru aja kalian selesai nge-deploy versi terbaru backend ke server. Eh, beberapa detik kemudian channel monitoring malah banjir alert 502 Bad Gateway dan Connection Reset by Peer dari pengguna. 😅
Setelah dicek, ternyata ada transaksi checkout yang gagal di tengah jalan. Saldo udah kepotong di payment gateway, tapi status order-nya belum sempat ter-update di database!
Biang keroknya sering banget sepele: proses server lama dimatikan mendadak alias un-graceful shutdown. Kita asal tebas proses aja, tanpa nawarin pamit dulu.
Ibaratnya kayak kalian lagi makan malam di restoran. Kalau tepat jam 22.00 manajer langsung matiin lampu, ngunci pintu, dan ngusir pengunjung yang makanannya baru setengah tersaji, pasti kalian kesel banget, kan? Nah, itu versi paksa.
Versi beradabnya beda. Jam 21.30 staf udah mengumumkan "last order" dan nggak nerima tamu baru. Tapi yang udah duduk di dalam tetap dipersilakan menikmati hidangan sampai tuntas. Baru setelah semua meja kosong dan kasir nutup pembukuan, pintunya dikunci rapi.
Di dunia software, graceful shutdown adalah etika server buat menyelesaikan semua pekerjaan yang sedang berjalan (in-flight requests) sebelum benar-benar mematikan prosesnya. Kalau langkah ini dilewatin, semua koneksi HTTP yang lagi berlangsung langsung putus di tengah jalan, transaksi database menggantung, dan connection pool pun bocor (leak).
Agar backend kalian bisa diperbarui kapan aja tanpa bikin pengguna kena error, mekanisme ini wajib dipasang. Yuk, kita kupas tuntas cara kerjanya!
Poin Penting
- Graceful shutdown adalah etika server buat menyelesaikan semua in-flight request sebelum prosesnya benar-benar dimatikan.
- Tanpa graceful shutdown, transaksi database menggantung, connection pool bocor, dan pengguna disambut error 502 Bad Gateway tepat saat deploy.
- Sinyal SIGTERM minta aplikasi berhenti dengan sopan, sedangkan SIGKILL memutus proses tanpa kompromi dan nggak bisa ditolak.
- Kombinasi lifespan handler di FastAPI, exec form di Dockerfile,
terminationGracePeriodSeconds, danpreStophook bikin deployment berjalan tanpa downtime.
Mengenal Sinyal Unix: SIGINT, SIGTERM, dan SIGKILL
Sistem operasi—termasuk Docker dan Kubernetes—ngobrol sama aplikasi backend lewat sinyal standar POSIX.
SIGINT dikirim pas kalian nekan Ctrl + C di terminal. Aplikasi biasanya merespons dengan menghentikan eksekusi secara ramah.
SIGTERM ini yang paling sering muncul di produksi. Docker (docker stop) atau Kubernetes mengirimnya saat rolling update atau scale down pod. Pesannya kira-kira: "Tolong siap-siap berhenti, selesaikan tugas yang sedang aktif!"
SIGKILL (sinyal 9) adalah pamungkas yang nggak bisa ditolak atau ditangkap (uninterceptable) oleh aplikasi. Kernel langsung memutus proses secara instan. Kalau aplikasi nggak kunjung kelar dalam batas grace period, sistem bakal nembak SIGKILL deh.
Gimana Siklus Graceful Shutdown yang Ideal?
Ketika orchestrator kayak Kubernetes atau Docker mau mematikan container backend, ada lima tahap yang idealnya terjadi berurutan.
Pertama, aplikasi nerima sinyal SIGTERM dari OS.
Kedua, aplikasi nolak traffic baru. Status health check atau readiness probe-nya ditandai unhealthy, biar load balancer berhenti ngarahin request ke server ini.
Ketiga, request yang lagi in-flight diselesaikan. Kasih jeda (grace period, misalnya 10–30 detik) supaya request HTTP, transaksi database, atau proses upload file yang sedang berjalan bisa tuntas.
Keempat, koneksi eksternal ditutup teratur. Putus database pool (PostgreSQL, MySQL), simpan state cache (Redis), dan lakukan acknowledge/nack pada antrean pesan (RabbitMQ).
Terakhir, proses keluar bersih dengan exit code 0. Server pamit dengan baik, tanpa drama. 😎
Contoh Lifespan di FastAPI dan Uvicorn
Framework modern kayak FastAPI bareng server ASGI Uvicorn udah nyediain lifespan context manager yang elegan banget buat nangani startup dan graceful shutdown:
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI
import asyncpg
# Variabel global untuk connection pool
db_pool = None
@asynccontextmanager
async def lifespan(app: FastAPI):
global db_pool
# [TAHAP STARTUP]
print("🚀 [STARTUP] Menginisialisasi Database Connection Pool...")
db_pool = await asyncpg.create_pool(
dsn="postgresql://user:***@localhost:5432/mydb",
min_size=5,
max_size=20
)
print("✅ [STARTUP] Koneksi Database siap menerima traffic!")
yield # Aplikasi berjalan dan melayani request di sini
# [TAHAP GRACEFUL SHUTDOWN - Saat menerima SIGTERM]
print("🛑 [SHUTDOWN] Sinyal terminasi diterima! Memulai Graceful Shutdown...")
# Beri toleransi waktu untuk request in-flight selesai
await asyncio.sleep(2)
# Tutup koneksi database secara teratur
if db_pool:
print("🔒 [SHUTDOWN] Menutup Database Connection Pool...")
await db_pool.close()
print("✅ [SHUTDOWN] Seluruh koneksi DB berhasil dilepas.")
print("👋 [SHUTDOWN] Aplikasi backend berhenti dengan aman.")
app = FastAPI(lifespan=lifespan)
@app.get("/api/data")
async def get_data():
async with db_pool.acquire() as connection:
return await connection.fetch("SELECT * FROM users LIMIT 10")
Saat perintah docker stop atau sinyal SIGTERM masuk, Uvicorn otomatis berhenti nerima request baru, nunggu request aktif selesai, lalu menjalankan blok kode setelah yield di dalam fungsi lifespan.
Konfigurasi Penting di Docker dan Kubernetes
Biar mekanismenya jalan optimal di lingkungan cloud atau container, ada beberapa hal yang perlu kalian cek.
Pakai exec form di Dockerfile. Pastikan perintah CMD-nya berbentuk JSON array, misalnya CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]. Kalau kalian pakai shell form (CMD uvicorn ...), sinyal SIGTERM bakal nyangkut di shell /bin/sh dan nggak diteruskan ke aplikasi Python. Jebakan ini halus banget, lho.
Atur juga terminationGracePeriodSeconds di Kubernetes. Default-nya 30 detik sebelum SIGKILL dikirim. Kalau aplikasi kalian butuh waktu lebih lama buat ngerjain background job, naikin nilainya—misal jadi 60 detik—di manifest Deployment.
Terakhir, manfaatkan preStop hook. Tambahkan sleep (misal 5 detik) di pod container Kubernetes, supaya load balancer punya cukup waktu nyoret IP pod dari daftar endpoint aktif sebelum pod mulai mematikan prosesnya.
Jadi, Kenapa Ini Penting Banget?
Nulis kode fitur yang jalan barulah setengah perjalanan. Memastikan aplikasi bisa nyala dan mati dengan anggun (graceful) adalah kunci membangun sistem yang tangguh (resilient), konsisten secara data, dan bebas downtime saat tim kalian rilis tiap hari.
Selamat ber-devops ria, dan semoga server kalian selalu pamit dulu sebelum tidur! 👋