Liveness, Readiness và Startup Probe trong Kubernetes
1. Tổng quan
Khi chạy ứng dụng trên Kubernetes, cluster không tự biết pod của bạn đang “sống” hay “sẵn sàng nhận traffic” — nó phải hỏi qua probe. Bài này giải thích sự khác biệt giữa liveness, readiness và startup probe, kèm ví dụ cấu hình Spring Boot Actuator và Deployment YAML. Đọc xong, bạn sẽ cấu hình được probe đúng cho một service thật như OrderService mà không làm pod bị restart loop oan.
2. Vì sao cần probe
Mặc định, Kubernetes chỉ biết process trong container còn chạy (container status Running) hay không. Nó không biết ứng dụng bên trong có bị deadlock, mất kết nối database, hay đang khởi động chưa xong.
Hệ quả nếu không có probe:
- Pod bị treo (deadlock) nhưng vẫn nhận traffic → request timeout hàng loạt.
- Pod vừa start, chưa kết nối xong DB, đã bị Service đưa vào danh sách endpoint → lỗi 500.
Ba loại probe giải quyết ba vấn đề khác nhau:
| Probe | Trả lời câu hỏi | Khi fail |
|---|---|---|
| Liveness | Container còn sống không? | Kubernetes restart container |
| Readiness | Container sẵn sàng nhận traffic chưa? | Kubernetes gỡ pod khỏi Service endpoint (không restart) |
| Startup | Container đã khởi động xong chưa? | Kubernetes chờ, tạm vô hiệu liveness/readiness |
3. Liveness probe
Liveness probe trả lời: “process này có đang ở trạng thái không thể tự phục hồi không?”. Nếu probe fail quá failureThreshold lần, kubelet kill và restart container.
3.1. Thêm Spring Boot Actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management.endpoint.health.probes.enabled=true
management.endpoints.web.exposure.include=health,info
Dòng management.endpoint.health.probes.enabled=true bật hai endpoint con: /actuator/health/liveness và /actuator/health/readiness, đúng chuẩn Kubernetes mong đợi.
3.2. Khai báo trong Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 20
periodSeconds: 10
failureThreshold: 3
initialDelaySeconds: 20: chờ 20s sau khi container start mới bắt đầu probe.periodSeconds: 10: mỗi 10s gọi probe một lần.failureThreshold: 3: fail 3 lần liên tiếp mới coi là chết → restart.
Lưu ý: Liveness probe chỉ nên kiểm tra “ứng dụng còn chạy được không”, không nên kiểm tra dependency ngoài (DB, Redis…). Nếu DB chết mà liveness fail theo, Kubernetes sẽ restart pod liên tục mà không giải quyết được gì — đây là lỗi rất thường gặp.
4. Readiness probe
Readiness probe trả lời: “pod đã sẵn sàng xử lý request chưa?”. Khi fail, pod bị gỡ khỏi endpoint của Service — traffic không bị gửi tới nữa, nhưng container không bị restart.
4.1. Custom readiness indicator
Giả sử OrderService cần kết nối tới PaymentGateway trước khi sẵn sàng:
@Component
public class PaymentGatewayReadinessIndicator implements HealthIndicator {
private final PaymentGatewayClient paymentGatewayClient;
public PaymentGatewayReadinessIndicator(PaymentGatewayClient paymentGatewayClient) {
this.paymentGatewayClient = paymentGatewayClient;
}
@Override
public Health health() {
if (paymentGatewayClient.isConnected()) {
return Health.up().withDetail("gateway", "connected").build();
}
return Health.down().withDetail("gateway", "unreachable").build();
}
}
Để indicator này gộp vào group readiness (chứ không ảnh hưởng liveness), khai báo group:
management.endpoint.health.group.readiness.include=readinessState,paymentGateway
health()trảHealth.down()khiPaymentGatewayClientchưa kết nối được — readiness probe sẽ fail, pod không nhận traffic cho tới khi gateway sẵn sàng.- Spring tự đặt tên bean
paymentGatewayReadinessIndicatorthành khóapaymentGatewaytrong response health.
4.2. Test cho indicator
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
class PaymentGatewayReadinessIndicatorTest {
@Test
void shouldReportDownWhenGatewayNotConnected() {
PaymentGatewayClient client = mock(PaymentGatewayClient.class);
when(client.isConnected()).thenReturn(false);
PaymentGatewayReadinessIndicator indicator = new PaymentGatewayReadinessIndicator(client);
Health health = indicator.health();
assertThat(health.getStatus()).isEqualTo(Status.DOWN);
assertThat(health.getDetails()).containsEntry("gateway", "unreachable");
}
}
mock(PaymentGatewayClient.class)giả lập dependency ngoài, không cần kết nối thật.assertThat(health.getStatus())xác nhận status làDOWNkhi gateway chưa connect — đúng hành vi mong đợi của readiness.
4.3. Khai báo readiness trong Deployment
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3
successThreshold: 1
successThreshold: 1: chỉ cần 1 lần probe thành công là coi như ready lại (khác với liveness, thường không cần set field này vì default đã là 1).periodSeconds: 5ngắn hơn liveness vì readiness cần phản ứng nhanh với thay đổi trạng thái dependency.
5. Startup probe
Với ứng dụng khởi động chậm (load cache lớn, chạy migration DB), nếu chỉ dùng liveness với initialDelaySeconds cố định, bạn phải đoán một con số đủ lớn — dễ sai và lãng phí thời gian chờ cho mọi lần restart sau.
Startup probe giải quyết việc này: trong lúc startup probe chưa pass, Kubernetes tạm vô hiệu liveness và readiness probe, tránh bị kill oan khi app đang khởi động hợp lệ.
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
startupProbedùngfailureThreshold: 30vàperiodSeconds: 10→ cho phép app khởi động tối đa 300s (30 × 10s) trước khi bị coi là fail.- Sau khi
startupProbepass lần đầu, Kubernetes chuyển sang theo dõilivenessProbevới threshold nhỏ hơn (3 lần × 10s = 30s) — phản ứng nhanh với sự cố thật trong lúc chạy ổn định.
Lưu ý: Nếu ứng dụng của bạn khởi động nhanh (dưới vài giây, như REST API đơn giản không load cache lớn), không cần khai báo
startupProbe— thêm vào chỉ làm YAML dài hơn mà không có lợi ích rõ ràng.
6. Kết hợp cả ba trong một Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: nonladev/order-service:1.0
ports:
- containerPort: 8080
startupProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3
Luồng hoạt động khi pod mới được tạo:
- Container start, Kubernetes bắt đầu gọi
startupProbengay (không cóinitialDelaySecondsvì startup tự xử lý việc chờ). - Trong lúc này
livenessProbevàreadinessProbechưa chạy. - Khi
startupProbetrả HTTP 200 lần đầu, Kubernetes dừng hẳn startup probe, bắt đầu chạy liveness và readiness song song. - Readiness fail → pod bị gỡ khỏi Service, traffic không tới nữa, nhưng container vẫn sống.
- Liveness fail → container bị kill, Kubernetes tạo lại container mới (cùng pod), quy trình lặp lại từ bước 1.
Kiểm tra trạng thái probe thực tế bằng kubectl describe pod:
kubectl describe pod order-service-7d9f8c6b5-xk2pq
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Events:
Type Reason Age From Message
Warning Unhealthy 2m kubelet Readiness probe failed: HTTP probe failed with statuscode: 503
Dòng Readiness probe failed cho biết readiness đang fail — kiểm tra log ứng dụng hoặc gọi trực tiếp /actuator/health/readiness để xem detail nào DOWN.
7. Các lỗi hay gặp
- Dùng chung một endpoint cho liveness và readiness: khi dependency ngoài (DB, cache) chết, cả hai probe cùng fail → pod bị restart liên tục (crash loop) dù ứng dụng chính vẫn sống khỏe. Hãy tách rõ
/actuator/health/liveness(chỉ kiểm tra nội bộ) và/actuator/health/readiness(kiểm tra cả dependency). initialDelaySecondsquá nhỏ cho app khởi động chậm: liveness probe fail ngay trong lúc app đang load, gây restart loop vô tận. Giải pháp: dùngstartupProbethay vì tănginitialDelaySecondstùy tiện.- Quên
periodSeconds×failureThresholdquá lớn: pod lỗi thật nhưng mất nhiều phút mới bị phát hiện, ảnh hưởng SLA. - Readiness probe gọi tới một service downstream không quan trọng: ví dụ gọi tới service logging, nếu service đó sập thì toàn bộ
OrderServicebị coi là not-ready oan, dù chức năng chính vẫn hoạt động tốt.
8. Kết luận
Ba loại probe phục vụ ba mục đích tách bạch: startupProbe bảo vệ ứng dụng trong giai đoạn khởi động chậm, livenessProbe phát hiện deadlock để Kubernetes tự restart, và readinessProbe điều phối traffic dựa trên tình trạng dependency thực tế. Nguyên tắc quan trọng nhất: đừng để liveness probe phụ thuộc vào hệ thống ngoài — việc đó biến một sự cố downstream thành crash loop của chính service bạn. Cấu hình đúng ba probe này giúp cluster tự phục hồi mượt mà, giảm đáng kể thời gian downtime khi deploy hoặc khi có sự cố hạ tầng.