Liveness Probe가 장애를 복구한다고요? Kubernetes가 정상 Pod를 죽이는 7가지 설정
트래픽이 몰린 순간, Kubernetes가 가장 바쁜 Pod부터 차례로 재시작했습니다.
애플리케이션이 죽은 것도 아니고 메모리가 부족한 것도 아니었습니다. 응답이 잠시 느려지자 livenessProbe가 실패했고, kubelet은 컨테이너를 재시작했습니다. 살아남은 Pod로 요청이 더 몰렸고, 그 Pod들마저 Probe에 실패했습니다. 복구 장치가 장애 증폭기가 된 순간입니다.
세 가지 Probe, 이름보다 결과가 중요하다
Kubernetes 공식 문서가 구분하는 핵심은 단순합니다. Startup은 시작을 기다리고, Readiness는 트래픽을 끊고, Liveness는 컨테이너를 재시작합니다.
| Probe | 실패하면 | 답해야 할 질문 | 대표 용도 |
|---|---|---|---|
| startup | 성공 전까지 다른 Probe를 보류 | 앱이 시작됐나? | 느린 부팅·캐시 워밍업 |
| readiness | Service 엔드포인트에서 제외 | 지금 요청을 받아도 되나? | 일시적 과부하·의존성 장애 |
| liveness | 컨테이너 재시작 | 재시작 없이는 회복 불가능한가? | 교착·복구 불가능 상태 |
정상 Pod를 죽이지 않는 7가지 규칙
1. Liveness에서는 외부 의존성을 검사하지 않는다
데이터베이스, Redis, 외부 API가 잠시 느리다는 이유로 애플리케이션 프로세스를 재시작해도 의존성은 복구되지 않습니다. 오히려 재기동 비용과 재접속 폭주가 더해집니다. Liveness는 프로세스 교착처럼 재시작해야만 회복되는 상태에 집중하세요.
2. 일시적 문제는 Readiness로 트래픽만 뺀다
요청을 처리하기 어려운 Pod는 Service 대상에서 잠시 제외하면 됩니다. Readiness 실패 시 EndpointSlice에서 해당 Pod가 준비되지 않은 상태로 반영되지만 컨테이너 자체를 죽이지는 않습니다. 과부하와 일시적 의존성 장애에는 이 차이가 매우 큽니다.
3. 느린 시작은 initialDelaySeconds가 아니라 Startup Probe로 보호한다
부팅 시간이 환경마다 달라진다면 고정 지연값 하나로 버티지 마세요. Startup Probe가 성공하기 전에는 Liveness와 Readiness가 실행되지 않습니다. 허용 시작 시간은 대략 failureThreshold × periodSeconds로 계산할 수 있습니다.
4. timeout과 실패 횟수는 감이 아니라 지연시간으로 정한다
Probe의 기본값을 그대로 복사하면 네트워크 흔들림이나 GC 구간을 장애로 오판할 수 있습니다. 실제 시작 시간과 헬스 엔드포인트의 p99 지연시간을 측정한 뒤 timeoutSeconds, periodSeconds, failureThreshold를 명시하세요.
5. /live와 /ready의 판단 기준을 분리한다
두 Probe가 같은 URL을 호출하면 이름만 다르고 결과는 같아집니다. /live는 프로세스 내부의 최소 상태만 빠르게 확인하고, /ready는 요청 처리에 반드시 필요한 조건을 확인하세요. 무거운 SQL, 전체 테이블 조회, 외부 인터넷 호출은 Probe 경로에서 피해야 합니다.
6. 아래 YAML은 출발점일 뿐, 그대로 복사하지 않는다
containers:
- name: api
image: example/api:1.0.0
ports:
- name: http
containerPort: 8080
startupProbe:
httpGet:
path: /health/startup
port: http
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 60 # 최대 5분 대기
readinessProbe:
httpGet:
path: /health/ready
port: http
periodSeconds: 5
timeoutSeconds: 2
failureThreshold: 2
successThreshold: 1
livenessProbe:
httpGet:
path: /health/live
port: http
periodSeconds: 10
timeoutSeconds: 2
failureThreshold: 3
이 값은 예시입니다. 실제 서비스의 부팅 분포, 정상 지연시간, 복구 시간 목표에 맞춰 조정하고 먼저 일부 Pod에 카나리로 적용하세요.
7. 재시작 횟수와 Probe 실패를 배포 성공 조건에 넣는다
HTTP 200 응답만 보고 배포 성공으로 판단하지 마세요. 새 버전의 restartCount, NotReady 시간, Probe 실패 이벤트를 함께 관찰해야 합니다. 재시작이 늘면 자동 승격을 중단하고 이전 버전으로 되돌릴 수 있어야 합니다.
장애가 났다면 이 세 줄부터 확인한다
kubectl describe pod <pod-name>
kubectl logs <pod-name> --previous
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[*].restartCount}'
describe의 Events에서 Unhealthy와 Killing을 찾고, --previous로 재시작 직전 로그를 확인하세요. Probe 값을 무작정 늘리기 전에 실제 실패가 타임아웃인지, 잘못된 경로인지, 애플리케이션 교착인지 구분해야 합니다.
- 이 실패는 컨테이너 재시작으로 실제 복구되는가?
- 외부 의존성 실패를 애플리케이션 사망으로 오판하고 있지 않은가?
- 프로세스가 이미 스스로 종료하고 restartPolicy로 복구된다면 Liveness가 꼭 필요한가?
마무리: Kubernetes는 Probe가 거짓말하는지 모른다
Kubernetes는 헬스 엔드포인트의 의미를 해석하지 않습니다. 실패라는 신호를 받으면 설정된 행동을 정확하게 수행할 뿐입니다. 그래서 좋은 Probe는 많은 것을 검사하는 Probe가 아니라 행동의 결과까지 정확히 설계한 Probe입니다.
죽지 않은 Pod를 살리겠다며 재시작하는 순간, 자동 복구는 자동 장애가 됩니다.
참고 문서: Kubernetes — Liveness, Readiness, and Startup Probes · Configure Probes · Pod Lifecycle
댓글
댓글 쓰기