Sửa lỗi Readiness Probe Failed: HTTP Probe Failed with statuscode: 500 trong Kubernetes

intermediate☸️ Kubernetes2026-03-21| Kubernetes 1.20+, mọi cloud provider (EKS, GKE, AKS) hoặc cluster tự quản lý

Error Message

Readiness probe failed: HTTP probe failed with statuscode: 500
#kubernetes#readiness-probe#pod#deployment

Lỗi gặp phải

Readiness probe failed: HTTP probe failed with statuscode: 500

Lỗi này xảy ra sau khi deploy phiên bản service mới — các pod khởi động bình thường, nhưng bị kẹt ở trạng thái 0/1 Running và không nhận được traffic. Service vẫn tiếp tục định tuyến đến các pod cũ. Readiness probe đang gọi đến health endpoint, nhận về mã 500, và Kubernetes âm thầm loại các pod mới ra khỏi vòng xoay.

Nguyên nhân

Kubernetes dùng readiness probe để kiểm soát việc nhận traffic. Nếu endpoint của probe trả về bất kỳ mã nào ngoài 2xx hoặc 3xx, pod sẽ bị đánh dấu là not ready và bị xóa khỏi danh sách endpoint của Service. Tiến trình vẫn đang chạy — chỉ là không nhận request mà thôi.

Các nguyên nhân phổ biến khiến health endpoint trả về 500:

  • Ứng dụng đang trong quá trình khởi tạo (kết nối DB, warmup cache, parse config vẫn chưa xong)
  • Một dependency — Postgres, Redis, hoặc upstream API — bị down hoặc không thể kết nối từ pod
  • Bản thân health endpoint bị lỗi exception chưa được xử lý
  • Một biến môi trường hoặc secret bị thiếu, rỗng, hoặc trỏ sai host
  • Probe đang kiểm tra sai path hoặc sai port

Các bước khắc phục

Bước 1: Kiểm tra trạng thái Pod và Events

kubectl get pods -n <namespace>
kubectl describe pod <pod-name> -n <namespace>

Cuộn xuống phần Events ở cuối output của lệnh describe. Bạn sẽ thấy các dòng lặp lại như sau:

Warning  Unhealthy  5s    kubelet  Readiness probe failed: HTTP probe failed with statuscode: 500

Đồng thời, hãy kiểm tra cấu hình probe được in ra — xác nhận rằng path, port, và các giá trị timing thực sự khớp với những gì ứng dụng của bạn expose.

Bước 2: Kiểm tra log của ứng dụng

kubectl logs <pod-name> -n <namespace>
# Nhiều container trong pod?
kubectl logs <pod-name> -c <container-name> -n <namespace>
# Xem log trực tiếp:
kubectl logs -f <pod-name> -n <namespace>

Mã 500 hầu như luôn có nguyên nhân được ghi lại bởi chính ứng dụng. Tìm kiếm stack trace, lỗi connection refused, hoặc các thông báo "missing required env var" trong vài giây đầu sau khi khởi động.

Bước 3: Gọi trực tiếp Health Endpoint

Exec vào pod và tự curl đến probe path. Bỏ qua việc đoán mò:

kubectl exec -it <pod-name> -n <namespace> -- sh
# Bên trong pod:
curl -v http://localhost:8080/health

Đọc kỹ response body. Một mã 500 từ ứng dụng Spring Boot có thể hiển thị "Unable to acquire JDBC Connection". Một service Node.js có thể trả ra stack trace. Body chính là chẩn đoán thực sự — đừng bỏ qua nó.

Bước 4: Kiểm tra cấu hình Probe

Lấy toàn bộ YAML của deployment:

kubectl get deployment <deployment-name> -n <namespace> -o yaml

Tìm block readinessProbe:

readinessProbe:
  httpGet:
    path: /health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5
  failureThreshold: 3

Ba lỗi thường gặp:

  • Sai path — ứng dụng phục vụ tại /healthz nhưng probe kiểm tra /health, nhận về 404 mà một số framework chuyển thành 500
  • Sai port — ứng dụng lắng nghe trên cổng 3000, nhưng probe lại gọi đến cổng 8080
  • initialDelaySeconds quá thấp — các ứng dụng JVM thường cần 45–60 giây để sẵn sàng, nhưng probe lại kích hoạt sau 10 giây

Bước 5: Kiểm tra biến môi trường và Secrets

kubectl exec -it <pod-name> -n <namespace> -- env | grep -i db
kubectl exec -it <pod-name> -n <namespace> -- env | grep -i redis

Nếu DATABASE_URL rỗng hoặc trỏ đến localhost thay vì DB service thực tế, mọi health check cố mở kết nối đều sẽ thất bại. Hãy xác nhận secrets được mount đúng:

kubectl get secret <secret-name> -n <namespace> -o yaml
kubectl describe pod <pod-name> -n <namespace> | grep -A5 "Environment"

Bước 6: Tạm thời tăng initialDelaySeconds

Ứng dụng khởi động chậm? Tăng thời gian delay trước khi triển khai bản fix:

kubectl patch deployment <deployment-name> -n <namespace> --type='json' \
  -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/initialDelaySeconds", "value": 60}]'

Hoặc chỉnh sửa trực tiếp:

kubectl edit deployment <deployment-name> -n <namespace>

Cách này giúp có thêm thời gian để chẩn đoán vấn đề thực sự mà không để hàng loạt lỗi probe làm hỏng quá trình rollout.

Bước 7: Khắc phục sự cố kết nối Dependency

Nếu mã 500 xuất phát từ việc ứng dụng cố ping Postgres hoặc Redis mà thất bại, hãy kiểm tra kết nối từ bên trong pod trước:

# Postgres:
kubectl exec -it <pod-name> -n <namespace> -- nc -zv <db-host> 5432
# Redis:
kubectl exec -it <pod-name> -n <namespace> -- nc -zv <redis-host> 6379

Không kết nối được? Vấn đề không phải ở cấu hình probe. Hãy kiểm tra NetworkPolicies, DNS resolution của Service, và xem DB pod có đang ở trạng thái ready không. Khắc phục kết nối xong, kết quả probe sẽ tự ổn thôi.

Xác nhận đã sửa xong

Theo dõi pod chuyển sang trạng thái ready theo thời gian thực:

kubectl get pods -n <namespace> -w

Cột READY phải chuyển từ 0/1 thành 1/1 sau vài chu kỳ probe. Sau đó xác nhận pod đã được thêm vào danh sách endpoint:

kubectl get endpoints <service-name> -n <namespace>

IP của pod phải xuất hiện ở đó. Thực hiện một bài test end-to-end nhanh:

kubectl port-forward svc/<service-name> 8080:80 -n <namespace>
curl http://localhost:8080/

Lưu ý để tránh lặp lại

  • Tách biệt liveness và readiness: liveness chỉ nên kiểm tra xem tiến trình còn sống không — không bị treo, không bị deadlock. Readiness kiểm tra xem nó có thể phục vụ traffic không. Đưa kiểm tra kết nối DB vào liveness sẽ gây restart pod không cần thiết khi DB của bạn gặp sự cố lúc 2 giờ sáng.
  • Giữ health endpoint gọn nhẹ: đừng chạy query SELECT 1 mỗi lần probe gọi. Với chu kỳ 10 giây trên 20 replica, con số lên tới 120 DB query mỗi phút chỉ từ health check. Kiểm tra connection pool là đủ rồi.
  • Dùng startupProbe cho ứng dụng khởi động chậm: Kubernetes 1.18+ có startupProbe. Dùng nó thay vì tăng initialDelaySeconds quá cao — nó sẽ tắt liveness/readiness cho đến khi startup probe pass, giúp ứng dụng có đủ thời gian mà không bỏ sót lỗi về sau.
  • Đừng đặt failureThreshold là 1: một lỗi thoáng qua không nên làm pod bị loại khỏi vòng xoay. Ba lần thất bại là ngưỡng hợp lý cho hầu hết các service.
# Cấu hình probe tốt cho một web service thông thường
readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 10
  failureThreshold: 3
  successThreshold: 1
livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 15
  failureThreshold: 5
startupProbe:
  httpGet:
    path: /health/live
    port: 8080
  failureThreshold: 30
  periodSeconds: 10

Related Error Notes