Kubernetes Probe와 롤링 업데이트

Kubernetes 심화: Probe와 RollingUpdate 운영 트레이드오프 요약 이미지
요약 이미지(직접 생성). 무단 사용 금지.

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

  1. 실제 시작시간보다 짧은 liveness 임계값 설정
  2. 앱 준비 전 liveness 실패 반복
  3. CrashLoopBackOff 및 서비스 지연 발생

해결 A

  1. startupProbe를 추가해 초기 구간을 분리합니다.
  2. readiness/liveness 임계값을 실제 시작 패턴에 맞춥니다.
  3. kubectl describe pod 이벤트로 probe 실패 근거를 확인합니다.

재현 B: 자원 부족 상태에서 과도한 maxSurge

  1. 롤아웃 시 새 Pod 스케줄 실패
  2. 진행 정체 후 deadline 초과
  3. 배포 실패 상태 진입

해결 B

  1. 클러스터 용량 기준으로 maxSurge/maxUnavailable 재설계
  2. rollout status 모니터링과 undo 절차를 런북화
  3. 배포 전 리소스 여유를 선점검

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) 관련 글

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux