Jika deploy baru membuat error rate naik, latency melonjak, atau sebagian request gagal ke database dan layanan lain, jangan langsung menebak akar masalah di jalur request utama. Langkah paling aman adalah memastikan instance baru tidak menerima traffic dulu sampai benar-benar siap. Di sinilah readiness probe berperan: ia memberi sinyal ke load balancer atau orchestrator bahwa instance belum layak menerima request produksi.

Pada aplikasi Go Fiber, pola yang efektif adalah memisahkan /health dan /ready. /health menjawab apakah proses masih hidup, sedangkan /ready menjawab apakah aplikasi siap melayani traffic. Dengan pemisahan ini, rollback usai deploy gagal bisa dilakukan lebih aman: instance yang bermasalah dikeluarkan dari rotasi, traffic didrain, lalu rilis lama diaktifkan kembali sambil tim memverifikasi dampak dan mengumpulkan bukti untuk postmortem.

Mengapa readiness probe penting saat deploy dan rollback

Banyak deploy gagal bukan karena proses aplikasi mati total, melainkan karena aplikasi hidup tetapi belum siap. Contohnya:

  • Koneksi database belum stabil.
  • Migrasi skema belum kompatibel dengan kode baru.
  • Koneksi ke cache, message broker, atau layanan internal lain masih gagal.
  • Warm-up belum selesai, misalnya pemuatan konfigurasi, model, atau data referensi.
  • Shutdown sedang berlangsung, tetapi instance masih menerima request baru.

Jika hanya mengandalkan endpoint kesehatan sederhana yang selalu mengembalikan 200, load balancer dapat menganggap instance sehat padahal request nyata gagal. Readiness probe mencegah hal ini dengan memastikan instance baru masuk rotasi setelah dependency minimum siap, dan keluar dari rotasi saat sedang shutdown atau saat dependency kritis gagal.

Desain endpoint /health dan /ready di Go Fiber

Perbedaan tanggung jawab

Desain yang aman dan mudah dioperasikan:

  • /health: ringan, cepat, dan tidak bergantung pada layanan eksternal. Tujuannya untuk mengecek proses masih berjalan.
  • /ready: boleh memeriksa dependency penting, status inisialisasi, dan mode drain/shutdown. Tujuannya untuk menentukan apakah instance boleh menerima traffic.

Kesalahan umum adalah membuat /health terlalu berat, misalnya selalu memanggil database. Akibatnya, gangguan sesaat pada dependency bisa membuat orchestrator menganggap proses mati dan me-restart pod/container secara tidak perlu. Simpan logika dependency di /ready, bukan di /health.

Contoh implementasi ringkas di Go Fiber

Berikut contoh sederhana yang memisahkan status liveness dan readiness, sekaligus mendukung mode drain saat rollback atau shutdown.

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/gofiber/fiber/v2"
)

type AppState struct {
	ready    atomic.Bool
	draining atomic.Bool
}

type DB interface {
	PingContext(ctx context.Context) error
}

func healthHandler(c *fiber.Ctx) error {
	return c.Status(fiber.StatusOK).JSON(fiber.Map{
		"status": "ok",
	})
}

func readyHandler(state *AppState, db DB) fiber.Handler {
	return func(c *fiber.Ctx) error {
		if state.draining.Load() {
			return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
				"status": "draining",
			})
		}

		if !state.ready.Load() {
			return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
				"status": "starting",
			})
		}

		ctx, cancel := context.WithTimeout(c.Context(), 500*time.Millisecond)
		defer cancel()

		if err := db.PingContext(ctx); err != nil {
			return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{
				"status": "dependency_unready",
			})
		}

		return c.Status(fiber.StatusOK).JSON(fiber.Map{
			"status": "ready",
		})
	}
}

func main() {
	app := fiber.New()
	state := &AppState{}

	var db DB = newRealDB() // ganti dengan inisialisasi nyata

	app.Get("/health", healthHandler)
	app.Get("/ready", readyHandler(state, db))

	app.Get("/api/orders", func(c *fiber.Ctx) error {
		return c.JSON(fiber.Map{"ok": true})
	})

	go func() {
		if err := warmup(db); err != nil {
			log.Printf("warmup failed: %v", err)
			return
		}
		state.ready.Store(true)
		log.Println("application is ready")
	}()

	go func() {
		sigCh := make(chan os.Signal, 1)
		signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
		<-sigCh

		log.Println("shutdown signal received, entering draining mode")
		state.draining.Store(true)

		// beri waktu orchestrator/load balancer menghentikan traffic baru
		time.Sleep(10 * time.Second)

		if err := app.Shutdown(); err != nil {
			log.Printf("shutdown error: %v", err)
		}
	}()

	if err := app.Listen(":3000"); err != nil {
		log.Fatal(err)
	}
}

func warmup(db DB) error {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	return db.PingContext(ctx)
}

func newRealDB() DB {
	panic("implement DB here")
}

Poin penting dari contoh di atas:

  • ready=false saat startup, jadi instance tidak menerima traffic terlalu cepat.
  • draining=true saat shutdown, sehingga /ready mengembalikan 503 dan traffic baru bisa diarahkan ke instance lain.
  • Pemeriksaan dependency dilakukan dengan timeout pendek agar probe tidak menggantung.
  • /health tetap ringan dan tidak melakukan pengecekan berat.

Kapan dependency perlu dicek di /ready

Tidak semua dependency harus menentukan readiness. Gunakan aturan praktis berikut:

  • Masukkan ke /ready jika tanpa dependency itu request utama hampir pasti gagal, misalnya database utama untuk API transaksi.
  • Jangan masukkan ke /ready jika dependency bersifat opsional atau bisa didegradasi, misalnya layanan analitik atau notifikasi asynchronous.

Trade-off-nya jelas: semakin banyak dependency yang dipasang di /ready, semakin aman dari sisi kualitas traffic, tetapi semakin besar risiko instance dianggap tidak siap karena gangguan minor di layanan pendukung.

Alur incident handling ringan saat deploy gagal

1. Kenali gejala awal

Gejala deploy gagal biasanya terlihat dalam beberapa menit pertama setelah rilis:

  • Error rate 5xx meningkat.
  • Latency p95/p99 naik tajam.
  • Timeout ke database atau service downstream.
  • Readiness probe sering gagal pada rilis baru.
  • Jumlah restart container meningkat, walau proses sebenarnya tidak crash total.

Pada tahap ini, tujuan utama bukan langsung mencari semua akar masalah, melainkan menghentikan penyebaran dampak.

2. Identifikasi dampak dengan cepat

Pertanyaan operasional minimum yang perlu dijawab:

  • Apakah semua user terdampak atau hanya sebagian?
  • Apakah kegagalan hanya pada instance baru?
  • Apakah endpoint tertentu yang gagal, atau seluruh aplikasi?
  • Apakah dependency eksternal ikut bermasalah, atau masalah hanya muncul setelah deploy?

Jika hanya instance baru yang menunjukkan readiness gagal, sementara rilis lama stabil, sinyalnya kuat bahwa rollback adalah pilihan aman dan cepat.

3. Putuskan rollback, bukan perbaikan langsung di produksi

Rollback sebaiknya diprioritaskan bila:

  • Dampak ke user nyata dan meningkat.
  • Akar masalah belum jelas.
  • Perbaikan membutuhkan perubahan kode, konfigurasi, atau migrasi tambahan.
  • Rilis sebelumnya masih diketahui stabil.

Jangan memaksa hot fix di tengah insiden jika rollback bisa mengembalikan layanan lebih cepat. Prinsipnya: pulihkan layanan dulu, investigasi setelah stabil.

Strategi rollback aman dengan readiness probe dan drain traffic

Urutan rollback yang disarankan

  1. Hentikan promosi rilis baru jika deployment masih berjalan.
  2. Tandai instance baru sebagai not ready agar keluar dari rotasi traffic.
  3. Lakukan drain traffic agar request baru tidak masuk ke instance bermasalah.
  4. Biarkan request aktif selesai dalam waktu tunggu yang wajar.
  5. Aktifkan kembali versi stabil sebelumnya.
  6. Verifikasi metrik utama setelah rollback.

Drain traffic penting untuk menghindari pemutusan request di tengah jalan. Jika instance langsung dimatikan tanpa transisi, user bisa menerima error tambahan yang sebenarnya tidak perlu.

Implementasi drain traffic secara generik

Tanpa bergantung pada vendor tertentu, pola umumnya seperti ini:

  • Saat menerima sinyal shutdown, ubah status internal ke draining.
  • Endpoint /ready mulai mengembalikan 503.
  • Load balancer/orchestrator berhenti mengirim request baru ke instance tersebut.
  • Aplikasi menunggu beberapa detik agar koneksi aktif selesai.
  • Baru setelah itu server dihentikan.

Waktu tunggu drain harus disesuaikan dengan karakter request Anda. Jika API memiliki request yang biasanya selesai di bawah 1 detik, tunggu 5-15 detik sering kali cukup. Jika ada proses request yang lebih panjang, waktunya perlu lebih konservatif. Hindari angka sembarang tanpa melihat pola latency nyata.

Contoh integrasi sederhana pada deployment pipeline

Pipeline generik berikut menunjukkan logika yang umum dipakai:

# pseudo pipeline
build
run tests
publish artifact

deploy new instances
wait until /ready returns success on required instances
run smoke test to critical endpoints

if smoke test fails or error rate increases:
  mark new instances out of rotation
  deploy previous stable release
  verify recovery
else:
  continue rollout

Yang penting bukan nama tool-nya, tetapi gerbang verifikasi setelah deploy:

  • Jangan anggap deploy sukses hanya karena proses start berhasil.
  • Tunggu sampai readiness sukses.
  • Jalankan smoke test ke endpoint penting.
  • Pantau error rate dan latency singkat setelah rilis.
  • Siapkan rollback otomatis atau semi-otomatis bila ambang batas terlampaui.

Verifikasi pasca-rilis dan observability minimum

Readiness probe membantu mencegah traffic masuk terlalu cepat, tetapi ia bukan pengganti observability. Setelah rilis atau rollback, Anda tetap perlu verifikasi nyata di level aplikasi.

Checklist observability minimum

  • Log: request error, dependency error, dan log startup/shutdown yang jelas.
  • Metrik: jumlah request, error rate, latency, dan status code.
  • Error rate: terutama 5xx dan timeout.
  • Latency: minimal p95 atau indikator tail latency yang setara.
  • Versi rilis: cantumkan release identifier pada log atau endpoint info agar korelasi lebih mudah.

Kesalahan yang sering terjadi adalah hanya melihat CPU dan memory. Pada deploy gagal, sinyal paling berguna justru sering datang dari error rate, timeout dependency, dan perubahan latency.

Smoke test pasca-rilis

Setelah instance baru dinyatakan ready, jalankan verifikasi sederhana ke jalur kritis, misalnya:

  • Request GET ke endpoint baca yang paling sering dipakai.
  • Request yang menyentuh database utama.
  • Request autentikasi bila aplikasi bergantung pada session atau token.

Pastikan smoke test merepresentasikan traffic nyata, bukan hanya memanggil /health atau /ready. Endpoint probe tidak cukup untuk membuktikan fitur bisnis berjalan normal.

Skenario dependency belum siap dan cara menanganinya

Database belum menerima koneksi

Jika aplikasi tidak bisa melayani request tanpa database, maka /ready memang harus gagal. Ini mencegah instance menerima traffic yang pasti berujung error. Namun gunakan timeout pendek dan logging yang jelas agar diagnosis cepat.

Cache atau message broker belum siap

Jika cache hanya mempercepat performa dan aplikasi masih bisa fallback ke database, pertimbangkan agar /ready tetap sukses. Sebaliknya, jika broker wajib untuk memproses request sinkron tertentu, jadikan ia bagian dari readiness.

Migrasi skema belum kompatibel

Ini salah satu penyebab rollback paling rumit. Readiness probe tidak akan menyelesaikan masalah jika perubahan skema database tidak kompatibel dengan versi lama. Karena itu, jika memungkinkan, gunakan strategi migrasi yang backward-compatible selama fase rollout, misalnya menambah kolom baru lebih dulu sebelum kode lama-benar-benar ditinggalkan.

Kesalahan umum saat menerapkan readiness probe

  • Menyamakan /health dan /ready sehingga keduanya kehilangan fungsi operasional yang jelas.
  • Membuat probe terlalu berat dan lambat, hingga probe sendiri menjadi sumber beban.
  • Tidak menangani mode shutdown/drain, sehingga instance masih menerima request baru saat akan dimatikan.
  • Tidak memberi timeout pada pengecekan dependency.
  • Mengandalkan probe saja tanpa smoke test dan metrik pasca-rilis.
  • Menganggap rollback selalu aman, padahal perubahan skema atau data bisa membuat rollback berisiko.

Checklist operasional singkat

Sebelum deploy

  • Pastikan ada endpoint /health dan /ready yang terpisah.
  • Tentukan dependency mana yang benar-benar mempengaruhi readiness.
  • Siapkan timeout probe yang pendek dan konsisten.
  • Pastikan log startup, dependency failure, dan shutdown mudah dibaca.
  • Siapkan jalur rollback ke rilis stabil sebelumnya.

Saat deploy

  • Jangan kirim traffic ke instance baru sebelum /ready sukses.
  • Amati error rate, latency, dan status dependency beberapa menit pertama.
  • Jalankan smoke test ke endpoint bisnis penting.

Saat rollback

  • Tandai instance baru sebagai not ready.
  • Drain traffic sebelum proses dihentikan.
  • Rollback ke versi stabil.
  • Verifikasi pemulihan dengan metrik dan smoke test ulang.

Postmortem singkat setelah insiden

Setelah layanan pulih, buat postmortem singkat dan fokus pada perbaikan sistem, bukan menyalahkan individu. Struktur sederhananya bisa seperti ini:

  • Ringkasan insiden: kapan deploy dimulai, kapan gejala muncul, kapan rollback selesai.
  • Dampak: endpoint terdampak, durasi, dan tipe kegagalan.
  • Akar masalah sementara/final: misalnya dependency belum siap, perubahan skema tidak kompatibel, atau konfigurasi salah.
  • Yang berjalan baik: readiness probe mencegah seluruh traffic masuk ke instance baru, rollback cepat, alarm berbunyi tepat waktu.
  • Perbaikan lanjutan: tambah smoke test, perbaiki timeout dependency, pisahkan dependency opsional dari readiness, ubah strategi migrasi.

Catatan: insiden kecil yang ditangani cepat tetap layak dipostmortem jika penyebabnya bisa terulang. Tujuannya adalah mengurangi peluang kegagalan yang sama pada deploy berikutnya.

Penutup

Pada aplikasi Go Fiber, rollback aman usai deploy gagal sangat bergantung pada pemisahan yang jelas antara /health dan /ready, ditambah mekanisme drain traffic saat shutdown. Readiness probe bekerja karena ia mengontrol kapan instance boleh menerima request, bukan sekadar menandai proses hidup.

Jika Anda menerapkan probe yang tepat, timeout dependency yang masuk akal, smoke test pasca-rilis, dan observability minimum, proses deploy akan lebih aman dan keputusan rollback bisa diambil lebih cepat dengan risiko lebih kecil. Fokus utamanya sederhana: cegah instance yang belum siap menerima traffic, dan keluarkan instance bermasalah dari rotasi sebelum dampaknya meluas.