3 poin oleh GN⁺ 2025-05-06 | 1 komentar | Bagikan ke WhatsApp
  • 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

 
GN⁺ 2025-05-06
Komentar Hacker News
  • Di Kubernetes, pembaruan IP target load balancer kadang memakan waktu lama. 90% masalahnya adalah memastikan trafik benar-benar ter-drain

    • Menambahkan waktu tunggu 15 detik ke hook preStop global sangat meningkatkan rasio HTTP 503
    • Ini memberi jeda antara pembatalan registrasi load balancer dan pengiriman SIGTERM, sehingga penanganan aplikasi jadi lebih sederhana
  • Saat menggunakan log.Fatal, isi di dalam defer tidak akan dijalankan

    • log.Fatal memanggil os.Exit dan langsung menghentikan proses
    • Jika memakai panic, isi defer akan tetap dijalankan
  • Saat endpoint Prometheus /metrics di-scrape secara berkala, metrik yang dicatat di antara scrape terakhir dan penghentian proses mungkin tidak sempat terpropagasi

    • Beberapa detik terakhir log bisa hilang saat layanan dimatikan
    • Bisa terjadi race condition ketika file log dipantau oleh proses sidecar
  • Jika 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

    • Di systemd, implementasinya relatif sederhana
    • nginx sudah mendukung ini selama lebih dari 20 tahun
    • Kubernetes dan Docker tidak mendukungnya
  • Pembahasan tentang liveness masih kurang

    • Sudah beberapa kali terlihat aplikasi yang memakai endpoint yang sama untuk liveness/readiness
  • 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

    • Library itu menyediakan API untuk mengintegrasikan layanan dengan berbagai mekanisme start dan stop
  • Setelah memperbarui readiness probe, tunggu beberapa detik agar sistem tidak lagi mengirim permintaan baru

    • Pod yang sedang dimatikan tidak berada dalam status ready
    • Layanan akan menandai endpoint sebagai sedang dimatikan
    • Setelah SIGTERM masih bisa ada jendela kecil, tetapi ini bukan masalah besar
    • Yang penting adalah tidak menerima koneksi baru dan menutup koneksi yang sudah ada dengan baik