Tampilan
Kenapa penting
Begitu aplikasi berjalan di container, kamu tidak lagi bisa "melihat" apakah ia sehat. Load balancer perlu tahu instance mana yang boleh menerima trafik, Kubernetes perlu tahu kapan pod harus di-restart, dan kamu perlu tahu versi mana yang sedang berjalan saat insiden. Saat deploy, instance lama harus berhenti tanpa memutus pembayaran yang sedang diproses. Spring Boot Actuator menyediakan semua sinyal ini — tapi kalau dikonfigurasi asal, ia justru jadi sumber masalah: pod yang restart berantai, atau endpoint heapdump yang membocorkan secret ke internet.
Konsep inti
Actuator dan endpoint penting
Tambahkan starter-nya, dan aplikasi mendapat endpoint operasional di bawah /actuator:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>| Endpoint | Isi | Sensitif? |
|---|---|---|
health | Status UP/DOWN, plus grup liveness/readiness | Rendah (detail bisa disembunyikan) |
info | Info build, git, versi | Rendah |
metrics | Daftar metrik Micrometer | Sedang |
prometheus | Metrik format Prometheus (butuh registry) | Sedang |
loggers | Lihat/ubah level log saat runtime | Tinggi (bisa diubah) |
env | Properti & sumbernya (nilai disamarkan) | Tinggi |
heapdump | File dump memori JVM | Sangat tinggi |
threaddump | Stack semua thread | Sedang |
Secara default, hanya health yang diekspos lewat HTTP. Endpoint lain harus kamu buka secara eksplisit.
Keamanan endpoint: expose seperlunya, port terpisah
Dua prinsip: buka hanya yang dipakai, dan jangan taruh di port publik. Port manajemen terpisah memudahkan aturan jaringan — port 8080 dibuka ke load balancer publik, port 8081 hanya bisa dijangkau dari jaringan internal (Prometheus, probe Kubernetes).
yaml
management:
server:
port: 8081
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
show-details: when-authorized # detail komponen hanya untuk user berwenangheapdump adalah endpoint paling berbahaya: dump memori berisi apa pun yang sedang ada di heap — password database hasil binding, token, data pelanggan. Nilai di endpoint env dan configprops memang disamarkan secara default, tapi jangan jadikan itu alasan membukanya ke publik. Bila endpoint sensitif benar-benar dibutuhkan, lindungi dengan Spring Security dan role khusus.
Liveness vs readiness
Dua pertanyaan berbeda yang sering tertukar:
| Probe | Pertanyaan | Kalau gagal | Isi yang tepat |
|---|---|---|---|
| Liveness | Apakah proses ini rusak permanen? | Container di-restart | State internal saja (livenessState) |
| Readiness | Apakah instance ini siap menerima trafik? | Dikeluarkan dari load balancer, tidak di-restart | readinessState + dependency kritis (db) |
Spring Boot mengelola keduanya lewat ApplicationAvailability dan mengeksposnya sebagai grup health: /actuator/health/liveness dan /actuator/health/readiness. Readiness otomatis REFUSING_TRAFFIC saat aplikasi masih start atau sedang shutdown. Kamu bisa menambahkan health indicator lain ke grup readiness:
yaml
management:
endpoint:
health:
group:
readiness:
include: readinessState,db
probes:
add-additional-paths: true # juga sediakan /livez dan /readyz di port utamaOpsi add-additional-paths penting bila kamu memakai port manajemen terpisah: tanpa itu, probe di port 8081 bisa UP padahal port utama 8080 sudah tidak menerima koneksi.
Di Kubernetes, probe dipetakan seperti ini:
yaml
# deployment.yaml — potongan
containers:
- name: shop-api
image: registry.example.com/shop-api:1.4.0
ports:
- containerPort: 8080
startupProbe:
httpGet: { path: /livez, port: 8080 }
periodSeconds: 5
failureThreshold: 24 # beri waktu start sampai 2 menit
livenessProbe:
httpGet: { path: /livez, port: 8080 }
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet: { path: /readyz, port: 8080 }
periodSeconds: 5
failureThreshold: 2startupProbe menahan liveness selama aplikasi masih start, sehingga JVM yang butuh waktu warm-up tidak dibunuh sebelum sempat siap. Load balancer non-Kubernetes (misalnya target group cloud) cukup diarahkan ke endpoint readiness.
Custom HealthIndicator
Untuk dependency yang tidak dicek otomatis — misalnya payment gateway — buat bean yang mengimplementasikan HealthIndicator. Di Spring Boot 4, interface ini ada di package org.springframework.boot.health.contributor. Nama komponen di output health diambil dari nama bean tanpa akhiran HealthIndicator.
java
// Potongan — Spring Boot 4.1
import org.springframework.boot.health.contributor.Health;
import org.springframework.boot.health.contributor.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class PaymentGatewayHealthIndicator implements HealthIndicator {
private final PaymentGatewayClient client;
public PaymentGatewayHealthIndicator(PaymentGatewayClient client) {
this.client = client;
}
@Override
public Health health() {
try {
long latencyMs = client.ping(); // panggilan ringan dengan timeout pendek
return Health.up().withDetail("latencyMs", latencyMs).build();
} catch (Exception e) {
return Health.down().withDetail("error", e.getClass().getSimpleName()).build();
}
}
}Komponen paymentGateway ini akan muncul di /actuator/health. Hati-hati memasukkannya ke readiness: kalau gateway pihak ketiga down, semua instance tidak ready sekaligus dan seluruh API — termasuk katalog produk yang tidak butuh pembayaran — ikut mati. Biasanya lebih baik gateway eksternal hanya tampil sebagai informasi, dan kegagalannya ditangani dengan timeout & circuit breaker di kode.
Graceful shutdown
Saat deploy, platform mengirim SIGTERM ke instance lama. Tanpa graceful shutdown, request yang sedang berjalan — misalnya membuat pesanan dan memotong stok — terputus di tengah jalan. Prinsipnya bisa dilihat dengan Java murni: shutdown hook berjalan baik saat SIGTERM maupun System.exit.
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class GracefulDemo {
static final AtomicBoolean ready = new AtomicBoolean(true);
static final ExecutorService workers = Executors.newSingleThreadExecutor();
static void handle(String orderId) {
if (!ready.get()) {
System.out.println("Tolak " + orderId + ": 503 sedang shutdown");
return;
}
workers.submit(() -> {
sleep(300);
System.out.println("Selesai proses " + orderId);
});
}
public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("SIGTERM diterima, readiness=false");
ready.set(false);
handle("ORD-3");
workers.shutdown();
try {
boolean clean = workers.awaitTermination(2, TimeUnit.SECONDS);
System.out.println(clean ? "Semua request selesai, keluar" : "Timeout, paksa berhenti");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}));
handle("ORD-1");
handle("ORD-2");
System.exit(0);
}
static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}text
SIGTERM diterima, readiness=false
Tolak ORD-3: 503 sedang shutdown
Selesai proses ORD-1
Selesai proses ORD-2
Semua request selesai, keluarUrutannya sama dengan yang dilakukan Spring Boot: berhenti menerima request baru, selesaikan yang sedang berjalan, dengan batas waktu. Di Spring Boot modern, graceful shutdown sudah aktif secara default untuk Tomcat, Jetty, dan Reactor Netty. Yang perlu kamu atur adalah batas waktunya:
yaml
server:
shutdown: graceful # default; tulis eksplisit agar jelas bagi pembaca
spring:
lifecycle:
timeout-per-shutdown-phase: 20sTimeout ini harus selaras dengan platform. Kubernetes menunggu terminationGracePeriodSeconds (default 30 detik) setelah SIGTERM, lalu mengirim SIGKILL. Ada juga jeda kecil sebelum load balancer berhenti mengirim trafik ke pod yang sedang dimatikan, sehingga banyak tim menambahkan preStop singkat:
yaml
# deployment.yaml — potongan
spec:
terminationGracePeriodSeconds: 45 # > preStop (10s) + timeout aplikasi (20s) + cadangan
containers:
- name: shop-api
lifecycle:
preStop:
sleep:
seconds: 10 # aksi sleep bawaan Kubernetes, tidak butuh shell di imageInfo build dan git
Saat insiden, pertanyaan pertama adalah "versi mana yang sedang jalan?". Endpoint info bisa menjawabnya. Goal build-info di spring-boot-maven-plugin menulis META-INF/build-info.properties (versi, waktu build), dan plugin git-commit-id-maven-plugin menulis git.properties (branch, commit). Actuator membacanya otomatis.
xml
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build-info</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>io.github.git-commit-id</groupId>
<artifactId>git-commit-id-maven-plugin</artifactId>
</plugin>json
{
"build": { "artifact": "shop-api", "version": "1.4.0", "time": "2026-10-20T08:15:02Z" },
"git": { "branch": "main", "commit": { "id": "94b1b27", "time": "2026-10-20T08:10:44Z" } }
}Contoh di dunia backend
Konfigurasi lengkap layanan pesanan yang berjalan di Kubernetes di belakang load balancer:
yaml
# application.yml — potongan
server:
port: 8080
shutdown: graceful
spring:
lifecycle:
timeout-per-shutdown-phase: 20s
management:
server:
port: 8081 # hanya dibuka di jaringan internal untuk Prometheus
endpoints:
web:
exposure:
include: health,info,prometheus
endpoint:
health:
show-details: when-authorized
probes:
add-additional-paths: true # /livez dan /readyz di port 8080 untuk probe
group:
readiness:
include: readinessState,db,redisAlur deploy versi baru: pod baru start → startupProbe menunggu → /readyz UP setelah koneksi PostgreSQL dan Redis siap → pod masuk load balancer. Pod lama menerima SIGTERM → preStop 10 detik memberi waktu load balancer mencabutnya → Spring menolak request baru dan menyelesaikan checkout yang sedang berjalan dalam 20 detik → proses keluar sebelum grace period 45 detik habis. Saat PostgreSQL maintenance, readiness turun dan pod berhenti menerima trafik, tapi liveness tetap UP sehingga tidak ada restart berantai.
Pertanyaan interview
1. Apa beda liveness probe dan readiness probe?
Liveness menjawab apakah proses rusak dan perlu di-restart — isinya cukup state internal aplikasi. Readiness menjawab apakah instance siap melayani trafik — boleh memasukkan dependency kritis seperti database. Liveness gagal memicu restart; readiness gagal hanya mengeluarkan instance dari load balancer sementara.
2. Kenapa database tidak boleh dimasukkan ke liveness probe?
Karena saat database down, semua pod gagal liveness bersamaan dan Kubernetes me-restart semuanya. Restart tidak memperbaiki database, malah menambah beban saat database pulih (semua instance start dan membuka koneksi bersamaan). Di readiness, pod cukup berhenti menerima trafik lalu kembali otomatis.
3. Bagaimana memastikan deploy tidak memutus request yang sedang berjalan?
Aktifkan graceful shutdown dengan timeout yang masuk akal, beri jeda preStop agar load balancer sempat mencabut instance, dan pastikan terminationGracePeriodSeconds lebih besar dari total jeda + timeout. Readiness juga otomatis turun saat shutdown sehingga tidak ada trafik baru.
Jebakan umum
management.endpoints.web.exposure.include=*di port publik —heapdumpdanloggersterbuka untuk siapa saja.- Memasukkan database atau API pihak ketiga ke liveness — gangguan eksternal berubah menjadi restart berantai.
- Memasukkan dependency non-kritis ke readiness — satu layanan pendukung down, seluruh API ikut tidak ready.
- Timeout graceful shutdown lebih panjang dari
terminationGracePeriodSeconds— request tetap terputus olehSIGKILL. - Probe hanya di port manajemen tanpa
add-additional-paths— health UP padahal port utama tidak melayani. - Tidak ada
startupProbeuntuk aplikasi yang start lambat — pod dibunuh liveness sebelum sempat siap.
Ringkasan
- Actuator menyediakan endpoint operasional; secara default hanya
healthyang diekspos lewat HTTP. - Expose seperlunya (
health,info,prometheus) dan taruh di port manajemen internal; lindungi endpoint sensitif. - Liveness = perlu restart?, readiness = siap trafik?; dependency eksternal hanya di readiness, itupun yang kritis.
HealthIndicatorkustom menambah komponen health; pikirkan dampaknya sebelum masuk grup readiness.- Graceful shutdown aktif secara default; selaraskan
timeout-per-shutdown-phase,preStop, dan grace period platform. - Info build & git di endpoint
infomenjawab "versi mana yang sedang jalan?" saat insiden.