Liveness/Readiness probe failed connection refused·context deadline 30초 결정 트리
K8s_Troubleshooting_Guide 21편
Pod 상태는 분명 Running인데, RESTARTS 카운트가 계속 올라가거나 Service로 트래픽이 안 들어옵니다. kubectl describe pod를 치면 Events 맨 아래에 probe failed 한 줄이 찍혀 있죠. 이 글은 그 에러 원문 한 줄을 Ctrl+F로 찾아 바로 점프할 수 있게 만든 런북입니다. 개념 설명은 최소화하고, "콘솔에 찍힌 문자열 → 원인 → 복붙 YAML" 흐름만 따라가세요.
1단계: 어떤 probe가 범인인가 (증상 분기표)
probe 종류부터 1차 식별합니다. 증상만 보면 누가 범인인지 거의 정해집니다.
| 증상 | 범인 probe | Pod 동작 | 핵심 |
|---|---|---|---|
| RESTARTS 계속 증가, 컨테이너가 죽고 재생성 | Liveness | 컨테이너 강제 kill 후 재시작 | 살아있는데 죽이는 중 |
Pod는 안 죽는데 트래픽이 안 들어옴 (kubectl get endpoints에 IP 없음) | Readiness | Endpoints에서 제외, restart는 안 함 | 받을 준비 안 됨 |
| 부팅 느린 앱이 기동 도중 Liveness에 맞아 죽어 무한 재시작 | Startup 부재 | 부팅 완료 전 Liveness가 먼저 때림 | startupProbe로 보호 필요 |
핵심: Liveness 실패 = 재시작, Readiness 실패 = Endpoint 제외(트래픽 차단). 재시작 루프면 Liveness, 502/연결 안 됨이면 Readiness를 먼저 의심하세요.
2단계: 에러 원문 읽는 위치
# 가장 빠른 길 — describe 맨 아래 Events 섹션
kubectl describe pod <pod-name> | tail -30
# 네임스페이스 전체를 시간순으로
kubectl get events --sort-by=.lastTimestamp -n <ns>Events 섹션의 Warning Unhealthy 줄에 probe 메시지 원문이 그대로 박혀 있습니다. 그 한 줄을 아래 표에서 찾으세요.
3단계: 에러 원문 exact-match 표
| 콘솔에 찍힌 원문 | 원인 | 바로 할 일 |
|---|---|---|
Liveness probe failed: HTTP probe failed with statuscode: 500 | 앱 헬스엔드포인트 자체가 500 반환 (DB·캐시 의존성 실패) | 앱 /healthz 로직, DB/Redis 연결 점검 |
Readiness probe failed: connection refused | 앱이 아직 그 포트로 리슨 안 함 / 미기동 | 부팅 지연 → initialDelay·startupProbe |
Liveness probe failed: Get "http://...": context deadline exceeded | timeoutSeconds 안에 응답 못 함 | timeoutSeconds 상향 + 앱 응답시간 점검 |
Readiness probe failed: dial tcp 10.x.x.x:8080: connect: connection refused | containerPort ≠ probe.port 포트 불일치 | 포트 번호 일치시키기 |
4단계: 결정 트리
probe failed 발견
├─ connection refused / dial tcp refused
│ ├─ 포트 번호가 다르다 → containerPort == probe.port 교정
│ └─ 포트는 맞다(부팅 중) → initialDelaySeconds 상향 or startupProbe 도입
├─ context deadline exceeded → timeoutSeconds 상향(1→3) + 앱 latency 확인
├─ statuscode: 500 → 헬스엔드포인트 의존성(DB·캐시) 점검 (probe 설정 문제 아님!)
└─ 재시작 루프인데 앱은 정상 → Liveness가 너무 공격적, 임계치 완화5단계: 복붙 YAML 4종
① 부팅 느린 Spring/JVM 앱 — startupProbe + 느슨한 Liveness
콜드스타트가 긴 앱은 startupProbe로 보호하고, 기동 완료 후에만 Liveness가 동작하게 합니다.
startupProbe:
httpGet:
path: /actuator/health
port: 8080
failureThreshold: 30 # 30회 × 10s = 최대 5분 기동 허용
periodSeconds: 10
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
periodSeconds: 10
failureThreshold: 3
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
periodSeconds: 5
failureThreshold: 3startupProbe가 성공할 때까지 Liveness/Readiness는 대기합니다. 더 이상 부팅 중에 Liveness에 맞아 죽지 않습니다.
② connection refused — httpGet path/port 교정
containerPort와 probe의 port가 반드시 같아야 합니다.
ports:
- containerPort: 8080 # 앱이 실제 리슨하는 포트
readinessProbe:
httpGet:
path: /healthz
port: 8080 # 위 containerPort와 동일하게!③ context deadline exceeded — 타임아웃 조정
기본 timeoutSeconds: 1은 GC 잠깐 멈춘 JVM 앱엔 너무 빡빡합니다.
livenessProbe:
httpGet:
path: /healthz
port: 8080
timeoutSeconds: 3 # 1 → 3
periodSeconds: 10
failureThreshold: 3④ tcpSocket vs httpGet 선택
HTTP 엔드포인트가 없는 DB·메시지 큐 등은 포트 오픈만 확인하는 tcpSocket이 정확합니다.
readinessProbe:
tcpSocket:
port: 5432 # 포트 열림만 확인
periodSeconds: 5
# gRPC 앱은 1.29+에서 grpc 필드 GA
livenessProbe:
grpc:
port: 50051
periodSeconds: 10실무 한마디: Liveness를 공격적으로 두지 마세요
현장에서 가장 흔한 자해 패턴이 **"Liveness를 너무 빡빡하게 잡아 멀쩡한 앱을 죽이는 것"**입니다. 트래픽 폭주로 응답이 잠깐 느려졌을 뿐인데 Liveness가 실패 → 재시작 → 워밍업 안 된 새 Pod로 트래픽 몰림 → 또 느려짐 → 또 재시작. 장애를 스스로 키우는 무한루프죠. Liveness는 "정말 복구 불가능한 데드락"만 잡도록 느슨하게, 일시적 지연은 Readiness에 맡기는 게 정석입니다. 또 Istio 같은 서비스메시 환경에선 sidecar 기동 순서 때문에 readiness 오탐이 자주 납니다(앱은 떴는데 envoy가 아직). 이땐 startupProbe + sidecar holdApplicationUntilProxyStarts로 순서를 보장하세요.
재발 방지 권장 임계치
| Probe | failureThreshold | periodSeconds | timeoutSeconds | 비고 |
|---|---|---|---|---|
| Liveness | 3 | 10 | 1 → 3 상향 | 공격적 금지, 데드락만 잡기 |
| Readiness | 3 | 5 | 1~2 | 트래픽 제어용, 자주 체크 OK |
| Startup | 30 | 10 | 1 | 30×10s = 최대 5분 기동 허용 |
참고: 공식 문서
이 글에서 다루는 동작·설정·에러의 1차 출처는 다음 공식 문서입니다. 버전별 옵션과 정확한 동작은 여기서 확인하세요.
자주 묻는 질문 (FAQ)
Q. Pod는 Running인데 트래픽이 안 들어와요. 왜죠?
A. Readiness probe 실패일 가능성이 높습니다. kubectl get endpoints <svc>로 Pod IP가 빠졌는지 확인하세요. Readiness 실패는 재시작 없이 Endpoints에서만 제외됩니다.
Q. RESTARTS가 계속 오릅니다. 앱 로그엔 에러가 없어요.
A. Liveness가 너무 공격적입니다. timeoutSeconds를 1→3으로, 부팅 느린 앱이면 startupProbe(failureThreshold 30)를 추가해 기동 완료 전 Liveness가 동작하지 않게 하세요.
Q. statuscode: 500은 probe 설정을 고치면 되나요? A. 아니요. 500은 헬스엔드포인트 코드가 실제로 500을 반환한 것이라 probe 설정 문제가 아닙니다. DB·캐시 등 의존성 연결을 점검해야 합니다.
다음 편(22편)에서는 CrashLoopBackOff와 exit code별 원인 매칭 런북을 다룹니다.
이 글은 AI 에이전트가 자료 조사와 1차 초안 작성을 담당하고, 사람 편집자가 사실관계·출처·톤과 맥락을 검토한 뒤 발행했습니다. 환경(OS·버전)에 따라 결과가 다를 수 있으니 적용 전 공식 문서를 함께 확인하세요. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
댓글
첫 번째 댓글을 남겨보세요.