
Kubernetes 심화: Probe와 RollingUpdate 운영 트레이드오프
1) 버전 기준
- Kubernetes 릴리즈 기준: v1.35.1 (GitHub Releases)
- 공식 문서 기준: Liveness/Readiness/Startup Probes, Deployment RollingUpdate
2) 핵심 개념
livenessProbe: 프로세스 진행 불능 시 재시작 판단readinessProbe: 트래픽 수신 가능 여부 제어startupProbe: 느린 기동 앱의 오탐 재시작 방지
Deployment RollingUpdate에서는 maxUnavailable, maxSurge, progressDeadlineSeconds를 통해 가용성과 배포 속도를 조절합니다.
3) 트레이드오프
- maxUnavailable 확대: 배포는 빨라지지만 순간 가용성 하락 위험이 큽니다.
- maxSurge 확대: 가용성은 유리하지만 추가 리소스가 필요합니다.
- startupProbe 미사용: 설정 단순하지만 콜드스타트 앱에서 재시작 루프 위험이 있습니다.
4) 장애 재현-해결
재현 A: 느린 시작 앱 + 엄격한 liveness
- 실제 시작시간보다 짧은 liveness 임계값 설정
- 앱 준비 전 liveness 실패 반복
- CrashLoopBackOff 및 서비스 지연 발생
해결 A
- startupProbe를 추가해 초기 구간을 분리합니다.
- readiness/liveness 임계값을 실제 시작 패턴에 맞춥니다.
kubectl describe pod이벤트로 probe 실패 근거를 확인합니다.
재현 B: 자원 부족 상태에서 과도한 maxSurge
- 롤아웃 시 새 Pod 스케줄 실패
- 진행 정체 후 deadline 초과
- 배포 실패 상태 진입
해결 B
- 클러스터 용량 기준으로 maxSurge/maxUnavailable 재설계
- rollout status 모니터링과 undo 절차를 런북화
- 배포 전 리소스 여유를 선점검
5) 체크리스트
- [ ] startup/readiness/liveness 역할이 분리되어 있는가?
- [ ] 느린 기동 앱에 startupProbe를 적용했는가?
- [ ] maxUnavailable/maxSurge가 SLO와 용량에 맞는가?
- [ ] progressDeadlineSeconds와 rollback 절차가 있는가?
- [ ] 배포 중 이벤트/rollout 상태를 관측하는가?
6) 공식 링크
- Probes: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
- Deployment: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
- Kubernetes Releases: https://github.com/kubernetes/kubernetes/releases
7) 실전: Probe 설정 코드와 롤링 업데이트 YAML
세 가지 Probe를 올바르게 설정해야 무중단 롤링 업데이트가 가능합니다. Startup Probe가 통과해야 Liveness/Readiness가 작동한다는 점이 핵심입니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-server
spec:
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 최대 1개 초과 Pod 허용
maxUnavailable: 0 # 기존 Pod는 모두 유지 → 무중단
template:
spec:
terminationGracePeriodSeconds: 30
containers:
- name: api
image: myapp:v2.1.0
ports:
- containerPort: 8080
# 1) Startup Probe — 최초 기동 완료 확인
startupProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 30 # 5s × 30 = 최대 150초 대기
# 2) Readiness Probe — 트래픽 수신 가능 여부
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 10
failureThreshold: 3
# 3) Liveness Probe — 살아있는지 확인 (데드락 감지)
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 15
failureThreshold: 3
8) Probe 엔드포인트 구현 (NestJS 예시)
/healthz와 /ready를 분리하는 이유: DB 연결이 끊겨도 앱 자체는 살아있을 수 있습니다. Liveness는 프로세스 상태, Readiness는 의존성 포함 상태를 반영해야 합니다.
import { Controller, Get } from '@nestjs/common';
import {
HealthCheck,
HealthCheckService,
TypeOrmHealthIndicator,
MemoryHealthIndicator,
} from '@nestjs/terminus';
@Controller()
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
private memory: MemoryHealthIndicator,
) {}
// Liveness: 프로세스가 살아있는가?
@Get('healthz')
@HealthCheck()
liveness() {
return this.health.check([
() => this.memory.checkHeap('memory_heap', 300 * 1024 * 1024),
]);
}
// Readiness: 트래픽 받을 준비가 되었는가?
@Get('ready')
@HealthCheck()
readiness() {
return this.health.check([
() => this.db.pingCheck('database', { timeout: 3000 }),
() => this.memory.checkHeap('memory_heap', 300 * 1024 * 1024),
]);
}
}
9) 흔한 실수와 디버깅
- Liveness에 DB 체크 포함: DB가 잠시 느려지면 Pod이 재시작되어 상황이 악화됩니다. Liveness는 가볍게, Readiness에 의존성을 넣으세요.
- initialDelaySeconds 부족: JVM/Spring Boot 앱은 기동에 30초 이상 걸릴 수 있습니다. startupProbe 없이 짧은 initialDelay를 설정하면 무한 재시작됩니다.
- maxUnavailable: 1 + replicas: 1: 단일 레플리카에서 maxUnavailable=1이면 다운타임이 발생합니다. 무중단이 필요하면 maxUnavailable=0으로 설정하세요.
# Probe 상태 디버깅
kubectl describe pod api-server-xxxxx | grep -A 5 "Conditions"
kubectl get events --field-selector involvedObject.name=api-server-xxxxx
# Pod이 재시작되는 경우 — 원인 확인
kubectl get pod api-server-xxxxx -o jsonpath='{.status.containerStatuses[0].lastState}'
10) 관련 글
- Kubernetes 무중단 롤아웃의 핵심 — Probe와 함께 terminationGracePeriodSeconds, preStop hook의 전체 흐름을 다룹니다.
- NestJS 그레이스풀 셧다운 가이드 — Probe가 fail 처리된 뒤 앱이 안전하게 종료되는 과정을 설명합니다.
- Kubernetes HPA 스케일링 가이드 — Readiness Probe와 HPA의 상호작용: 준비되지 않은 Pod은 스케일링 계산에서 제외됩니다.