Rolling deploy zero-downtime pada Spring Boot tidak cukup hanya dengan menjalankan dua pod atau dua instance lalu mengganti traffic. Masalah utama biasanya bukan di proses deploy-nya, melainkan pada kapan instance dianggap siap menerima request. Jika health check salah, load balancer dapat mengirim traffic ke proses yang belum selesai warm-up, koneksi database belum stabil, cache belum siap, atau migrasi skema belum aman digunakan.
Artikel ini membahas cara menyusun rolling update Spring Boot dengan downtime seminimal mungkin melalui pengaturan Actuator health, readiness, liveness, graceful shutdown, connection draining, startup probe, dan timeout yang realistis. Di bagian akhir ada contoh insiden singkat akibat readiness yang salah, langkah rollback cepat, metrik yang perlu diamati, serta contoh postmortem ringan.
Mengapa rolling deploy masih bisa menimbulkan error singkat
Pola kegagalan yang paling umum terlihat seperti ini:
- Instance baru dijalankan.
- Port aplikasi sudah terbuka, sehingga load balancer atau orchestrator menganggap instance sehat.
- Traffic mulai masuk terlalu cepat.
- Aplikasi sebenarnya masih memuat cache, membuat koneksi pool, memanggil service dependensi, atau menunggu migrasi selesai.
- Request pertama gagal dengan 5xx, timeout, atau lonjakan latensi.
Kesalahan inti biasanya adalah menyamakan "proses hidup" dengan "siap menerima traffic produksi". Dalam operasi nyata, keduanya berbeda.
Peran liveness, readiness, dan startup probe
- Liveness: menandakan proses masih hidup. Jika gagal, platform boleh me-restart container.
- Readiness: menandakan instance siap menerima traffic. Jika gagal, instance harus dikeluarkan dari rotasi traffic tanpa harus di-restart.
- Startup probe: memberi waktu startup yang panjang tanpa membuat liveness terlalu agresif. Ini penting jika bootstrap aplikasi memang lama.
Untuk rolling deploy, sinyal yang paling krusial adalah readiness. Inilah yang menentukan kapan traffic mulai dialihkan ke versi baru dan kapan traffic berhenti dikirim ke instance lama.
Desain rolling deploy Spring Boot yang aman
Prinsip alur deploy
- Jalankan instance baru.
- Tunggu sampai readiness = UP.
- Baru setelah itu traffic dialihkan bertahap ke instance baru.
- Saat instance lama akan dihentikan, ubah readiness menjadi tidak siap.
- Biarkan connection draining menyelesaikan request yang masih berjalan.
- Setelah tidak ada request aktif, lakukan shutdown dengan graceful shutdown.
Jika salah satu tahap dipercepat, zero-downtime mudah berubah menjadi error sesaat yang sulit direproduksi.
Komponen minimum yang sebaiknya ada
- Spring Boot Actuator aktif.
- Endpoint readiness dan liveness terpisah.
- Graceful shutdown aktif.
- Timeout shutdown cukup untuk request terpanjang yang masih wajar.
- Load balancer atau orchestrator mendukung health check dan draining.
- Log dan metrik deploy dipantau selama rollout.
Konfigurasi Spring Boot untuk readiness, liveness, dan graceful shutdown
Mengaktifkan Actuator dan probe
Contoh konfigurasi yang umum dipakai:
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoint.health.probes.enabled=true
management.endpoint.health.show-details=always
management.health.livenessstate.enabled=true
management.health.readinessstate.enabled=true
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30sIntinya:
- health.probes.enabled memungkinkan endpoint probe operasional.
- livenessstate dan readinessstate memisahkan dua status penting.
- server.shutdown=graceful membuat aplikasi berhenti menerima request baru saat shutdown, lalu menunggu request aktif selesai.
- timeout-per-shutdown-phase harus cukup realistis. Terlalu pendek membuat request terputus; terlalu panjang memperlambat rollback atau scaling event.
Catatan: Jangan menjadikan semua dependensi eksternal sebagai syarat liveness. Jika database lambat sesaat lalu liveness gagal, container bisa terus restart dan memperburuk insiden. Untuk sebagian besar kasus, masalah dependensi lebih tepat memengaruhi readiness, bukan liveness.
Membedakan readiness aplikasi dari sekadar port terbuka
Secara default, aplikasi bisa saja sudah membuka port HTTP sebelum benar-benar siap. Karena itu, readiness sebaiknya merepresentasikan kondisi seperti:
- Application context selesai start.
- Koneksi pool inti sudah bisa dipakai.
- Consumer background yang wajib untuk melayani request sudah aktif, jika memang relevan.
- Warm-up penting selesai, misalnya cache referensi dasar sudah terisi.
Namun jangan terlalu berlebihan. Jika readiness menunggu proses yang tidak memengaruhi jalur request utama, deploy justru menjadi lambat dan rapuh.
Contoh health indicator readiness khusus
Jika aplikasi butuh warm-up tertentu sebelum aman menerima traffic, Anda bisa menambahkan indikator readiness sendiri:
@Component
public class WarmupReadinessHealthIndicator implements HealthIndicator {
private final AtomicBoolean warmupDone = new AtomicBoolean(false);
@EventListener(ApplicationReadyEvent.class)
public void onReady() {
// Contoh: preload data referensi penting
// Jika warm-up async dipakai, tandai true hanya setelah benar-benar selesai
warmupDone.set(true);
}
@Override
public Health health() {
if (warmupDone.get()) {
return Health.up().build();
}
return Health.outOfService()
.withDetail("reason", "warmup not finished")
.build();
}
}Gunakan pendekatan ini hanya untuk hal yang benar-benar menentukan kesiapan menerima traffic. Jika warm-up bisa ditunda tanpa memecah request awal, lebih baik tidak mengunci readiness.
Connection draining dan shutdown yang tidak memutus request
Apa yang terjadi saat instance lama dimatikan
Pada rolling update, instance lama idealnya tidak langsung dihentikan. Urutan yang aman adalah:
- Tandai instance not ready.
- Load balancer berhenti mengirim request baru ke instance tersebut.
- Request yang masih berjalan diberi waktu untuk selesai.
- Baru proses shutdown berlanjut sampai benar-benar berhenti.
Inilah yang disebut connection draining. Tanpa draining, request in-flight bisa gagal di tengah jalan walaupun readiness versi baru sudah benar.
Timeout yang realistis
Tidak ada satu angka yang cocok untuk semua sistem, tetapi prinsipnya jelas:
- Shutdown timeout harus lebih besar dari mayoritas durasi request normal dan sedikit ruang untuk lonjakan.
- Readiness check interval jangan terlalu jarang, agar traffic cepat dialihkan saat instance benar-benar siap.
- Failure threshold jangan terlalu agresif, agar gangguan sesaat tidak memicu flapping.
- Load balancer drain timeout harus selaras dengan waktu shutdown aplikasi.
Kesalahan umum adalah timeout tidak sinkron. Misalnya, aplikasi diberi waktu 30 detik untuk graceful shutdown, tetapi load balancer memutus koneksi lebih cepat. Hasilnya tetap ada request gagal, walaupun konfigurasi aplikasi terlihat benar.
Shutdown hook untuk menolak traffic baru lebih awal
Pada sistem tertentu, Anda mungkin ingin secara eksplisit menurunkan readiness sesaat sebelum terminasi. Ini berguna bila platform Anda tidak otomatis mengatur urutan drain dan stop dengan baik. Prinsipnya: keluarkan instance dari rotasi traffic dulu, baru tunggu request aktif selesai.
Contoh probe dan alur operasional di orchestrator
Istilah dan format bisa berbeda antar platform, tetapi polanya serupa:
- startup probe mengizinkan startup lambat tanpa dianggap gagal.
- readiness probe menentukan apakah instance menerima traffic.
- liveness probe menentukan apakah proses perlu di-restart.
Contoh konfigurasi konseptual:
startupProbe:
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30
periodSeconds: 5
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3Jangan menyalin angka mentah ke semua layanan. API yang startup-nya berat atau sering melakukan warm-up butuh startup probe yang lebih longgar. Sebaliknya, readiness harus cukup cepat mendeteksi bahwa instance sudah aman menerima traffic.
Skenario insiden: rolling deploy memicu error singkat karena readiness salah
Gejala
Tim merilis versi baru layanan Spring Boot. Deploy berjalan rolling, satu instance baru naik lalu satu instance lama dimatikan. Secara dashboard, semua pod terlihat running. Namun selama 20-40 detik pertama, error 503 dan beberapa timeout muncul pada endpoint login dan checkout.
Penyebab teknis
Readiness probe diarahkan ke endpoint health umum yang hanya menunjukkan proses hidup dan konteks aplikasi berhasil start. Masalahnya, aplikasi masih menjalankan:
- Inisialisasi cache referensi.
- Pemanasan koneksi ke service otorisasi eksternal.
- Pembentukan pool koneksi yang belum stabil pada request awal.
Akibatnya, load balancer mulai mengirim traffic saat instance belum benar-benar siap.
Tanda yang biasanya muncul di log dan metrik
- Lonjakan HTTP 5xx setelah pod baru masuk rotasi.
- P95/P99 latency naik tajam saat rollout.
- Log seperti connection timeout, dependency unavailable, atau cache not initialized.
- Pod lama turun terlalu cepat sementara pod baru belum stabil.
Rollback cepat yang sebaiknya dilakukan
- Pause rollout agar versi baru tidak menyebar ke semua instance.
- Keluarkan versi baru dari rotasi traffic dengan menurunkan readiness atau menurunkan replica versi baru jika perlu.
- Naikkan kembali versi lama ke jumlah instance aman sebelumnya.
- Verifikasi error rate, latency, dan availability kembali normal.
- Simpan log dan event deploy sebelum data observabilitas hilang atau tertimpa.
Tujuan rollback cepat bukan langsung mencari akar masalah, tetapi menghentikan dampak ke user lebih dulu.
Perbaikan yang benar
Setelah rollback, tim memindahkan readiness ke endpoint yang benar-benar memeriksa kesiapan jalur request utama. Warm-up penting harus selesai dulu sebelum status readiness menjadi UP. Selain itu, startup probe dibuat lebih toleran agar liveness tidak mengganggu startup yang memang lebih lama.
Metrik dan log yang wajib diamati saat rolling deploy
Metrik aplikasi
- Request rate per instance.
- Error rate 4xx/5xx, terutama 5xx selama rollout.
- Latency p50, p95, p99.
- Active requests atau thread yang sedang melayani request.
- Connection pool usage ke database atau dependensi penting.
- JVM memory dan garbage collection bila startup berat.
Metrik infrastruktur dan platform
- Status readiness/liveness per pod atau instance.
- Waktu transisi dari start ke ready.
- Jumlah restart container.
- Event termination dan durasi draining.
- Status target di load balancer: healthy, draining, unhealthy.
Log yang paling berguna
- Log startup: kapan server listen, kapan context siap, kapan warm-up selesai.
- Log perubahan readiness jika Anda menambah indikator khusus.
- Log shutdown: kapan instance berhenti menerima request dan kapan benar-benar terminate.
- Log error dependensi eksternal di menit awal setelah deploy.
Tips debugging: beri penanda rilis di log dan metrik, misalnya versi build, commit SHA, atau deployment id. Ini memudahkan korelasi lonjakan error dengan rollout tertentu.
Contoh checklist sebelum dan sesudah deploy
Checklist sebelum deploy
- Actuator health endpoint dapat diakses.
- Readiness dan liveness terpisah fungsi.
- Readiness hanya UP jika jalur request utama benar-benar siap.
- Graceful shutdown aktif.
- Shutdown timeout sudah dibandingkan dengan durasi request nyata.
- Load balancer mendukung connection draining.
- Startup probe disetel jika startup aplikasi tidak singkat.
- Dashboard error rate, latency, dan health status sudah siap dipantau.
- Rencana rollback jelas dan dapat dieksekusi cepat.
- Perubahan skema database kompatibel untuk deploy bertahap jika ada migrasi.
Checklist saat deploy
- Pantau instance baru: start, ready, lalu menerima traffic.
- Pastikan error rate tidak naik saat satu instance lama dikeluarkan.
- Cek bahwa instance lama masuk status draining sebelum terminate.
- Verifikasi tidak ada restart akibat liveness yang terlalu agresif.
Checklist sesudah deploy
- Bandingkan latency dan error rate sebelum dan sesudah rollout.
- Periksa log error startup yang mungkin tidak terlihat di dashboard umum.
- Tinjau durasi time-to-ready per instance.
- Catat anomali kecil meskipun tidak sampai memicu rollback.
Trade-off dan kesalahan umum
Readiness terlalu longgar
Kelebihannya deploy terasa cepat, tetapi risiko request awal gagal lebih tinggi. Ini kesalahan yang paling sering menyebabkan error singkat saat rolling update.
Readiness terlalu ketat
Jika readiness menunggu komponen yang tidak wajib untuk melayani request utama, rollout menjadi lambat dan bisa tertahan terus. Gunakan sinyal yang relevan dengan pengalaman user, bukan semua proses internal.
Liveness dipakai untuk mendeteksi dependensi eksternal
Ini sering salah. Jika service eksternal terganggu, me-restart aplikasi belum tentu membantu. Bahkan dapat memperparah beban sistem dengan restart berulang.
Timeout tidak sinkron
Aplikasi, orchestrator, dan load balancer harus sejalan. Graceful shutdown di aplikasi tidak berguna bila lapisan di depannya memutus koneksi lebih dulu.
Migrasi database tidak kompatibel dengan rolling deploy
Jika versi lama dan baru tidak bisa hidup berdampingan terhadap skema yang sama, rolling deploy tetap berisiko meskipun health check sudah benar. Untuk kasus ini, gunakan strategi migrasi yang kompatibel bertahap.
Contoh postmortem ringan
Timeline
- 10:00 deploy versi baru dimulai.
- 10:02 instance baru status running dan dianggap healthy oleh platform.
- 10:03 traffic mulai dialihkan ke instance baru.
- 10:03-10:04 error 503 dan timeout meningkat pada endpoint login dan checkout.
- 10:05 rollout dipause, instance baru dikeluarkan dari rotasi traffic.
- 10:07 versi lama kembali dominan, error rate turun normal.
- 10:20 analisis awal menunjukkan readiness belum mempresentasikan warm-up yang dibutuhkan.
Dampak
Sebagian request user gagal dalam jendela waktu singkat selama rollout. Tidak ada kehilangan data, tetapi ada gangguan pengalaman pengguna dan beberapa retry dari client meningkat.
Root cause
Endpoint readiness salah desain: status UP diberikan sebelum cache referensi dan koneksi dependensi penting siap digunakan oleh jalur request utama.
Tindakan pencegahan
- Perbaiki readiness agar menunggu warm-up yang benar-benar wajib.
- Tambahkan startup probe untuk startup lambat.
- Sinkronkan timeout draining dan graceful shutdown.
- Buat alert khusus untuk lonjakan 5xx selama deployment window.
- Uji rolling deploy pada lingkungan staging dengan simulasi traffic.
Penutup
Rolling deploy zero-downtime dengan Spring Boot bergantung pada definisi kesiapan yang benar. Jika readiness hanya memeriksa bahwa proses hidup, traffic akan datang terlalu cepat dan memicu error singkat. Dengan memisahkan liveness, readiness, dan startup probe, lalu menggabungkannya dengan graceful shutdown dan connection draining, Anda bisa membuat rollout yang jauh lebih aman dan mudah di-rollback.
Jika Anda hanya memperbaiki satu hal setelah membaca artikel ini, periksa kembali apakah endpoint readiness aplikasi Anda benar-benar menjawab pertanyaan operasional berikut: "Apakah instance ini aman menerima traffic produksi sekarang?"
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!