- Graceful shutdown adalah prosedur saat aplikasi menerima sinyal penghentian lalu memblokir permintaan baru, menyelesaikan permintaan yang sedang berjalan, dan membersihkan resource
- Di Go, kita bisa menggunakan paket
os/signal untuk menangani langsung sinyal penghentian seperti SIGINT dan SIGTERM, dan juga dapat melakukan kontrol penghentian berbasis context dengan signal.NotifyContext
- Saat mematikan HTTP server, lebih stabil jika memutus trafik terlebih dulu melalui kegagalan readiness probe sebelum memanggil
Server.Shutdown(), lalu menunggu beberapa detik sebelum menjalankan shutdown
- Semua handler harus bisa mendeteksi sinyal penghentian dari context dan berhenti dengan aman, dan ini bisa ditangani secara terpadu melalui
BaseContext atau middleware
- Setelah menerima sinyal penghentian, resource eksternal seperti database, message broker, dan cache harus dibersihkan secara sengaja, dan jika didaftarkan dengan
defer, urutan shutdown akan lebih mudah dikelola
Apa itu Graceful Shutdown?
- Graceful shutdown adalah proses saat aplikasi dimatikan yang mencakup memblokir permintaan baru, menunggu permintaan yang sedang berlangsung selesai, dan membersihkan resource
- Artikel ini terutama membahas HTTP server dan lingkungan container, tetapi ini adalah konsep yang dapat diterapkan pada semua aplikasi
1. Menangani sinyal penghentian
- Pada sistem keluarga Unix, SIGTERM, SIGINT, dan SIGHUP digunakan sebagai sinyal penghentian
- Go runtime secara default menghentikan aplikasi saat menerima
SIGTERM atau SIGINT, tetapi kita bisa menanganinya sendiri dengan os/signal.Notify
- Dengan menggunakan channel yang dibuffer (kapasitas 1), kita bisa mencegah sinyal hilang selama inisialisasi
- Sejak Go 1.16, kontrol sinyal berbasis context menjadi lebih mudah dengan
signal.NotifyContext
2. Memahami batas waktu penghentian
- Di Kubernetes, secara default diberikan masa tenggang penghentian 30 detik (
terminationGracePeriodSeconds)
- Agar shutdown aman, sebaiknya sisakan margin 20% dan selesaikan pekerjaan shutdown dalam 25 detik
3. Berhenti menerima permintaan baru
http.Server.Shutdown() akan memblokir koneksi baru dan menunggu hingga permintaan yang ada selesai
- Di lingkungan Kubernetes, buat readiness probe gagal terlebih dulu untuk memutus masuknya trafik, lalu tunggu sebentar sebelum melakukan shutdown
- Pada handler readiness, status penghentian bisa diperiksa lewat variabel global untuk kemudian mengembalikan HTTP 503
4. Menyelesaikan pemrosesan permintaan
- Diperlukan pengaturan timeout yang tepat untuk context shutdown (
context.WithTimeout)
- Jika context shutdown kedaluwarsa, koneksi yang tersisa akan ditutup paksa
- Semua handler harus dirancang agar bisa mendeteksi sinyal penghentian dan berhenti dengan memanfaatkan
context.Context
- Untuk itu, kita bisa menyuntikkan context shutdown ke semua permintaan melalui middleware atau
BaseContext
5. Pembersihan resource
- Jika resource langsung ditutup begitu menerima sinyal penghentian, handler yang masih berjalan bisa mengalami masalah
- Setelah shutdown selesai, barulah koneksi database, message broker, cache, dan lain-lain dibersihkan
- Dengan memanfaatkan
defer di Go, rutin shutdown bisa dijalankan dalam urutan kebalikan dari inisialisasi, sehingga dependensi lebih mudah dikelola
- Selain resource seperti memori dan file descriptor yang dibersihkan otomatis oleh OS, ada juga resource yang memerlukan penghentian eksplisit seperti data flush dan transaction rollback
Ringkasan contoh lengkap
- Menerima sinyal penghentian dengan
signal.NotifyContext
- Mengimplementasikan endpoint readiness
/healthz
- Menyuntikkan context shutdown ke semua permintaan dengan
BaseContext
- Menjalankan shutdown setelah menunggu 5 detik untuk readiness
- Menyertakan fallback penghentian paksa jika pemanggilan
server.Shutdown gagal
Referensi dan resource terkait
1 komentar
Komentar Hacker News
Di Kubernetes, pembaruan IP target load balancer kadang memakan waktu lama. 90% masalahnya adalah memastikan trafik benar-benar ter-drain
preStopglobal sangat meningkatkan rasio HTTP 503Saat menggunakan
log.Fatal, isi di dalamdefertidak akan dijalankanlog.Fatalmemanggilos.Exitdan langsung menghentikan prosespanic, isideferakan tetap dijalankanSaat endpoint Prometheus
/metricsdi-scrape secara berkala, metrik yang dicatat di antara scrape terakhir dan penghentian proses mungkin tidak sempat terpropagasiJika sistem terdistribusi bergantung pada shutdown normal dari klien, sistem bisa mengalami kegagalan serius
Penjelasan tentang cara me-restart aplikasi tanpa memutus koneksi, saat instance layanan baru menerima socket dari instance sebelumnya, masih kurang
Pembahasan tentang liveness masih kurang
Jika program tidak bisa menangani perintah seperti ctrl c dengan bersih, berarti program itu ditulis dengan buruk
Elixir merancang proses sebagai proses VM kecil, sehingga tidak perlu sengaja membuat rutin graceful shutdown
Ada yang membuat library kecil di proyeknya untuk menangani graceful shutdown
Setelah memperbarui readiness probe, tunggu beberapa detik agar sistem tidak lagi mengirim permintaan baru