infra
Platform

모듈 맵

[Kubernetes] Liveness, Readiness, Startup Probe 헬스 체크 설정

0 / 29 완료

펼치기
0 / 29 완료0%

Kubernetes · 13 / 29

[Kubernetes] Liveness, Readiness, Startup Probe 헬스 체크 설정

Liveness, Readiness, Startup Probe로 파드의 생명주기와 트래픽 수신을 정밀하게 제어합니다

🚨INCIDENT ALERT
HIGH

배포 직후 파드는 Running인데 실제 요청은 503으로 실패합니다. 앱이 준비되기 전에 Service가 트래픽을 보내거나, 죽은 프로세스를 kubelet이 감지하지 못하면 장애가 길어집니다. Liveness, Readiness, Startup Probe는 Kubernetes가 애플리케이션 상태를 올바르게 판단하게 해줍니다.

신규 버전을 배포했습니다. kubectl rollout status가 완료됐고 파드는 Running입니다. 그런데 배포 직후 5분 동안 고객들이 503 에러를 받았습니다. 무슨 일이 벌어진 걸까요? 앱이 시작되는 데 30초가 필요한데, Kubernetes는 컨테이너가 Running 상태가 되는 즉시 트래픽을 보냈습니다. 아직 준비가 안 된 파드가 요청을 받아 에러를 반환한 겁니다.

이 문제의 해답이 Probe입니다. Kubernetes는 세 가지 Probe로 컨테이너의 상태를 주기적으로 확인합니다. Liveness Probe는 "이 컨테이너가 살아있는가? 아니면 재시작해야 하는가"를 판단하고, Readiness Probe는 "이 컨테이너가 트래픽을 받을 준비가 됐는가"를 판단합니다. Startup Probe는 초기화가 오래 걸리는 앱을 위한 특별한 유예 기간입니다. 세 Probe를 올바르게 설정하면 배포 중에도 무중단 서비스가 가능합니다.

이번 챕터에서 배울 것
  • 1Liveness Probe: 앱 장애 자동 감지 및 재시작
  • 2Readiness Probe: 준비 전 트래픽 차단
  • 3Startup Probe: 느린 초기화 대응
  • 4HTTP, TCP, Exec 세 가지 Probe 유형
  • 5Probe 파라미터 튜닝 (initialDelaySeconds, periodSeconds 등)
  • 6배포 중 무중단을 위한 Probe 설계 패턴
실습 환경 준비
실습용 네임스페이스 생성
kubectl create namespace probe-demo
예제 앱 배포 가능 여부 확인
kubectl run probe-test --image=nginx:1.25 -n probe-demo --dry-run=client
기존 파드 엔드포인트 확인 방법 숙지
kubectl get endpoints -n probe-demo 2>/dev/null || echo 'ready'
이벤트 확인 방법 숙지
kubectl get events --sort-by=.lastTimestamp -n probe-demo 2>/dev/null | head -5 || echo 'ready'
💡개념

Liveness Probe: 데드락과 무한루프를 감지해 재시작

앱이 Running 상태지만 실제로는 응답을 못하는 상황이 있습니다. 데드락, 메모리 누수로 인한 GC 멈춤, 무한 루프 등이 대표적입니다. Liveness Probe는 이를 감지해 컨테이너를 재시작합니다.

Liveness Probe: 데드락과 무한루프를 감지해 재시작

YAML
# liveness-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
  namespace: probe-demo
spec:
  containers:
  - name: app
    image: registry.k8s.io/e2e-test-images/agnhost:2.40
    args:
    - liveness
    livenessProbe:
      httpGet:
        path: /healthz     # 이 엔드포인트가 200-399 반환하면 정상
        port: 8080
        httpHeaders:       # 필요 시 헤더 추가
        - name: Custom-Header
          value: Awesome
      initialDelaySeconds: 5   # 컨테이너 시작 후 5초 대기 후 첫 체크
      periodSeconds: 10         # 10초마다 체크
      timeoutSeconds: 3         # 3초 내 응답 없으면 실패
      failureThreshold: 3       # 3번 연속 실패 시 재시작
      successThreshold: 1       # 1번 성공하면 정상으로 간주

TCP 소켓 방식 (DB처럼 HTTP 엔드포인트가 없는 경우):

YAML
livenessProbe:
  tcpSocket:
    port: 3306
  initialDelaySeconds: 15
  periodSeconds: 20

Exec 방식 (커스텀 스크립트 실행):

YAML
livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5
Kubernetes
# 배포 후 Probe 상태 확인
kubectl apply -f liveness-demo.yaml
kubectl describe pod liveness-http -n probe-demo | grep -A 10 "Liveness"
# Liveness:  http-get http://:8080/healthz delay=5s timeout=3s period=10s #success=1 #failure=3

# Probe 실패 이벤트 확인
kubectl get events -n probe-demo --sort-by=.lastTimestamp | grep -i "liveness\|unhealthy"
# Warning  Unhealthy  5s    pod/liveness-http  Liveness probe failed: HTTP probe failed with statuscode: 500
# Normal   Killing    5s    pod/liveness-http  Container app failed liveness probe, will be restarted
🔍실행 후 확인할 것
  • NAME조회 대상 리소스 이름이 예상한 대상과 일치하는지 확인합니다.
  • STATUS/READYRunning, Ready, Available처럼 정상 상태를 나타내는 필드가 있는지 봅니다.
  • RESTARTS/EVENTS재시작 횟수나 Warning 이벤트가 증가하지 않는지 확인합니다.
💡개념

Readiness Probe: 준비된 파드만 트래픽 받기

롤링 업데이트 중 새 파드가 Running 상태가 되는 순간, 아직 DB 연결도 못 맺고 캐시도 워밍업 안 된 상태에서 실제 요청이 들어오면 503이 발생합니다. Liveness Probe가 실패했다고 해서 바로 재시작하면 서비스가 더 불안정해질 수 있습니다. Readiness Probe는 이 두 가지를 명확히 분리합니다. 파드가 살아있더라도 아직 준비가 안 됐으면 Service 엔드포인트에서 제외해 트래픽을 차단하고, 준비가 완료되면 자동으로 복귀시킵니다.

Readiness Probe는 Service가 트래픽을 보낼 파드를 결정합니다. 실패하면 파드가 Service 엔드포인트에서 제거되어 트래픽이 차단되고, 다시 성공하면 복귀합니다. 파드는 종료되지 않습니다.

Readiness Probe: 준비된 파드만 트래픽 받기

이 동작이 Liveness Probe와의 핵심 차이입니다:

  • Liveness 실패 → 컨테이너 재시작
  • Readiness 실패 → 트래픽 차단 (파드는 유지)
YAML
# readiness-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: probe-demo
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
      - name: web
        image: nginx:1.25
        ports:
        - containerPort: 80
        readinessProbe:
          httpGet:
            path: /ready      # /ready가 200 반환할 때만 트래픽 수신
            port: 80
          initialDelaySeconds: 10   # 앱 초기화 대기
          periodSeconds: 5
          failureThreshold: 3
          successThreshold: 1
        livenessProbe:
          httpGet:
            path: /healthz
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
          failureThreshold: 3
Kubernetes
kubectl apply -f readiness-deployment.yaml

# 엔드포인트 확인 (Readiness 통과한 파드만 목록에 있음)
kubectl get endpoints web-app-svc -n probe-demo
# NAME         ENDPOINTS                                         AGE
# web-app-svc  10.244.1.5:80,10.244.1.6:80,10.244.2.3:80       30s

# 특정 파드의 Readiness 상태
kubectl get pod -n probe-demo -o wide
# READY 컬럼: 3/3이면 모든 컨테이너가 Readiness 통과
# READY 컬럼: 0/1이면 Readiness 실패 중
💡개념

Startup Probe: 느린 앱을 위한 유예 기간

Spring Boot, JVM 기반 서비스, 대규모 딥러닝 모델 로딩처럼 초기화에 수십 초가 필요한 앱에서 Liveness Probe의 initialDelaySeconds만으로는 대응이 어렵습니다. 시작 시간에 편차가 있기 때문입니다.

Startup Probe가 성공하기 전까지 Liveness Probe는 실행되지 않습니다. 즉, "초기화 완료 신호"를 보내기 전까지 Liveness가 파드를 재시작하지 않도록 보호합니다.

YAML
containers:
- name: spring-app
  image: myapp/spring-api:v2.0
  ports:
  - containerPort: 8080
  startupProbe:
    httpGet:
      path: /actuator/health/liveness
      port: 8080
    failureThreshold: 30    # 최대 30번 × 10초 = 300초(5분) 대기
    periodSeconds: 10
  livenessProbe:
    httpGet:
      path: /actuator/health/liveness
      port: 8080
    initialDelaySeconds: 0  # Startup이 성공하면 즉시 시작
    periodSeconds: 10
    failureThreshold: 3
  readinessProbe:
    httpGet:
      path: /actuator/health/readiness
      port: 8080
    initialDelaySeconds: 0
    periodSeconds: 5
    failureThreshold: 3

Spring Boot Actuator의 /actuator/health/liveness/actuator/health/readiness는 K8s Probe에 최적화된 전용 엔드포인트입니다. 각각 livenessStatereadinessState를 독립적으로 노출합니다.

Kubernetes
# Startup Probe 동작 확인
kubectl describe pod spring-app-xxx -n probe-demo | grep -A 5 "Startup"
# Startup:  http-get http://:8080/actuator/health/liveness
#           delay=0s timeout=1s period=10s #success=1 #failure=30

# 시작 중인 파드 이벤트 확인
kubectl get events -n probe-demo --sort-by=.lastTimestamp | head -10
# Normal  Starting  15s  pod/spring-app  Startup probe succeeded
# Normal  Ready     15s  pod/spring-app  Container is ready

실습: 완전한 Probe 설정을 가진 Deployment

로컬 터미널
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: full-probe-app
  namespace: probe-demo
spec:
  replicas: 2
  selector:
    matchLabels:
      app: full-probe-app
  template:
    metadata:
      labels:
        app: full-probe-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
        startupProbe:
          httpGet:
            path: /
            port: 80
          failureThreshold: 10
          periodSeconds: 5
        livenessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 10
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 5
          failureThreshold: 2
          successThreshold: 2   # 2번 연속 성공해야 Ready
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
          limits:
            cpu: "300m"
            memory: "256Mi"
EOF

# 배포 상태 확인
kubectl rollout status deployment/full-probe-app -n probe-demo

# 모든 Probe 설정 확인
kubectl describe deployment full-probe-app -n probe-demo | grep -A 20 "Containers:"

새 버전을 배포했습니다. kubectl rollout status가 완료됐는데 고객 모니터링에 503 에러 스파이크가 잡혔습니다. 5분 후 자연히 사라졌지만, 배포할 때마다 이 패턴이 반복됩니다.

위험 명령어Git이나 매니페스트와 다른 임시 상태가 생기고 잘못된 패치가 즉시 운영 트래픽에 영향을 줄 수 있습니다.

운영 리소스 직접 패치

안전한 실행 조건: 변경 내용을 코드에 반영할 계획이 있고 영향 범위를 검토했을 때만 실행하세요.

실행 전 반드시 확인

  • 현재 컨텍스트와 Namespace가 의도한 대상인지 확인했는가
  • 운영 트래픽이나 상태 저장 데이터에 미치는 영향을 확인했는가
  • 되돌릴 매니페스트, 백업, 또는 복구 절차가 준비되어 있는가
kubectl patch deployment api -n production --type='json' -p='[

위 항목을 모두 확인한 후 복사할 수 있습니다

Kubernetes
# 1단계: 배포 시점 이벤트 확인
kubectl get events -n production --sort-by=.lastTimestamp | tail -20
# Normal  Scheduled   2m  pod/api-v2-xxx  Successfully assigned
# Normal  Pulled      2m  pod/api-v2-xxx  Container image already present
# Normal  Started     2m  pod/api-v2-xxx  Started container api
# (Readiness 관련 이벤트가 없음 — Probe 미설정)

# 2단계: 현재 Deployment에 Probe 설정 여부 확인
kubectl get deployment api -n production \
  -o jsonpath='{.spec.template.spec.containers[0].readinessProbe}'
# null  ← Readiness Probe 없음!

# 3단계: 배포 직후 파드 Ready 전환 시간 파악
# 앱 로그에서 "Application started" 메시지 타임스탬프 확인
kubectl logs -l app=api -n production --since=10m | grep -i "started\|ready\|listening"
# 2026-05-16 03:15:22 INFO  Application started in 23.4 seconds

# 4단계: Readiness Probe 추가 (앱의 헬스 엔드포인트 사용)
kubectl patch deployment api -n production --type='json' -p='[
  {
    "op": "add",
    "path": "/spec/template/spec/containers/0/readinessProbe",
    "value": {
      "httpGet": {"path": "/health", "port": 3000},
      "initialDelaySeconds": 30,
      "periodSeconds": 5,
      "failureThreshold": 3,
      "successThreshold": 1
    }
  }
]'

# 5단계: 롤아웃하며 엔드포인트 변화 관찰
kubectl rollout restart deployment/api -n production

# 터미널 1: 엔드포인트 모니터링
watch kubectl get endpoints api-svc -n production
# 배포 중에는 기존 파드 IP만 있다가
# Readiness 통과한 신규 파드 IP가 추가됨
# 그 후 구 파드 IP 제거

# 터미널 2: 서비스 응답 모니터링
while true; do curl -s -o /dev/null -w "%{http_code}\n" http://api-svc/health; sleep 1; done
# 200 200 200 200 200 ...  ← 배포 중에도 503 없음

근본 원인: Readiness Probe 없이 Rolling Update를 하면, 신규 파드가 Running 상태가 되는 즉시 트래픽을 받습니다. 앱 초기화가 완료되기 전이므로 503이 발생합니다. Readiness Probe를 추가하면 실제로 요청을 처리할 수 있는 상태가 됐을 때만 엔드포인트에 추가됩니다.

추가 팁: minReadySeconds를 Deployment에 설정하면 Readiness Probe 통과 후 추가로 N초를 기다린 후 다음 파드를 교체합니다. 프로세스 재시작 후 웜업이 필요한 캐시 기반 서비스에 유용합니다.

💼
실무 맥락
현업 패턴

시나리오: 레거시 Node.js 앱에 Probe 설계 추가

팀이 운영 중인 Node.js Express 앱에 Probe가 하나도 없습니다. 팀 리드가 "이번 분기 안에 Probe 추가해"라고 요청했습니다. 앱 코드를 수정하는 것부터 K8s 설정까지 전 과정을 진행합니다.

JS
// app.js에 헬스 엔드포인트 추가
const express = require('express');
const app = express();

// 상태 변수
let isReady = false;

// 앱 초기화 (DB 연결, 캐시 워밍 등)
async function initialize() {
  await connectDatabase();    // DB 연결
  await warmupCache();        // 캐시 워밍
  isReady = true;             // 준비 완료
  console.log('Application ready');
}

// Liveness: 앱 프로세스가 살아있는지
app.get('/healthz', (req, res) => {
  res.status(200).json({ status: 'alive' });
});

// Readiness: DB 연결, 캐시 등 실제 서비스 가능 여부
app.get('/ready', async (req, res) => {
  if (!isReady) {
    return res.status(503).json({ status: 'not ready' });
  }
  try {
    await db.query('SELECT 1');  // DB 연결 확인
    res.status(200).json({ status: 'ready' });
  } catch (err) {
    res.status(503).json({ status: 'db error', error: err.message });
  }
});

initialize();
app.listen(3000);
YAML
# Kubernetes Probe 설정
containers:
- name: node-api
  image: myapp/node-api:v1.2.0
  ports:
  - containerPort: 3000
  startupProbe:
    httpGet:
      path: /ready
      port: 3000
    failureThreshold: 12    # 최대 60초 대기 (5초 × 12)
    periodSeconds: 5
  livenessProbe:
    httpGet:
      path: /healthz
      port: 3000
    periodSeconds: 10
    failureThreshold: 3
  readinessProbe:
    httpGet:
      path: /ready
      port: 3000
    periodSeconds: 5
    failureThreshold: 2     # 10초 연속 실패 시 트래픽 차단
    successThreshold: 1
Kubernetes
# 배포 후 Probe 동작 검증
kubectl apply -f node-api-deployment.yaml
kubectl rollout status deployment/node-api -n production

# Probe 통과 이벤트 확인
kubectl describe pod -l app=node-api -n production | grep -E "Started|Ready|Startup|Liveness|Readiness"

# 의도적으로 DB를 내려서 Readiness Probe 실패 동작 확인
kubectl exec -n production $(kubectl get pod -l app=postgres -o name) -- pg_ctl stop
watch kubectl get endpoints node-api-svc -n production
# 파드가 엔드포인트에서 빠지는 것 확인

실무 포인트: /healthz(Liveness)와 /ready(Readiness)를 반드시 분리하세요. Liveness는 최소한의 프로세스 생존만 확인하고, Readiness는 의존 서비스(DB, 캐시, 외부 API)까지 포함한 실제 서비스 가능 여부를 확인해야 합니다. Liveness에 DB 체크를 넣으면 DB 장애 시 멀쩡한 앱이 계속 재시작되는 최악의 상황이 발생합니다.

핵심 요약

Probe실패 시 동작주요 용도
Liveness컨테이너 재시작데드락, 무한루프 감지
ReadinessService 엔드포인트 제거배포 중 트래픽 차단, 의존성 장애 대응
StartupLiveness 실행 유예느린 초기화 보호
파라미터의미
initialDelaySeconds컨테이너 시작 후 첫 체크까지 대기
periodSeconds체크 주기
timeoutSeconds응답 대기 시간
failureThreshold연속 실패 허용 횟수
successThreshold연속 성공 필요 횟수 (Readiness만 1 이상 가능)

지식 확인

퀴즈 — 3문제

Q1

Readiness Probe가 실패했을 때 Kubernetes가 취하는 조치로 올바른 것은?

Q2

Spring Boot처럼 초기 구동에 60초 이상 걸리는 앱을 배포할 때 Liveness Probe의 initialDelaySeconds를 길게 설정하는 것보다 Startup Probe를 사용하는 것이 더 나은 이유는?

Q3

파드가 Running이고 로그도 정상인데 curl로 서비스에 접근하면 503이 반환될 때 가장 먼저 의심해야 할 것은?

0 / 3 답변

🧪 실습으로 확인하기

K8s 기초 — Pod/Deployment/Service 생성

초급

kubectl로 nginx Pod를 생성하고 Deployment와 Service를 차례로 만들어 클러스터 외부에서 접근 가능한 상태까지 구성한다. K8s 3대 리소스의 역할과 관계를 직접 손으로 익힌다.

40📋 5단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

kubernetes중급 · 70
[Kubernetes] RBAC(Role-based Access Control) 기반 다중 사용자 보안
Kubernetes 트랙 계속
docker입문 · 30
[Docker] 백엔드 개발자에게 Docker와 컨테이너 가상화가 필수인 이유
Docker 트랙 시작점