TL;DR

  • Circuit Breaker & Resilience 패턴 완전 가이드의 핵심 개념을 빠르게 파악할 수 있다.
  • 배경과 이유를 통해 왜 이 주제가 필요한지 맥락을 이해할 수 있다.
  • 실무에서 바로 참고할 수 있도록 주요 포인트를 구조화해 정리했다.

1. 개념

┌─────────────────────────────────────────────────────────────────┐

2. 배경

해당 주제가 필요한 실무 맥락과 기존 접근의 한계를 함께 이해해야 올바른 설계 판단이 가능하다.

3. 이유

문제 재발을 줄이고 운영 안정성을 높이기 위해 개념뿐 아니라 적용 기준과 트레이드오프를 명확히 정리할 필요가 있다.

4. 특징

핵심 용어, 동작 흐름, 구현 포인트, 주의사항을 한 문서에 묶어 빠르게 참고할 수 있다.

5. 상세 내용

Circuit Breaker & Resilience 패턴 완전 가이드

작성일: 2026-03-19 카테고리: Backend / Distributed Systems / Microservices / Resilience Engineering 키워드: Circuit Breaker, Resilience4j, Hystrix, Retry, Timeout, Bulkhead, Rate Limiter, Fallback, Graceful Degradation, Cascading Failure, Service Mesh, Istio, Envoy, Chaos Engineering, Polly, Sentinel


1. Circuit Breaker & Resilience 패턴이란?

1.1 분산 시스템의 근본적 위험: 연쇄 장애

┌─────────────────────────────────────────────────────────────────┐
│              연쇄 장애 (Cascading Failure) 시나리오              │
│                                                                 │
│  [정상 상태]                                                    │
│                                                                 │
│  Client ──► Service A ──► Service B ──► Service C ──► DB       │
│              (10ms)        (15ms)        (20ms)       (5ms)     │
│                                                                 │
│  총 응답 시간: ~50ms ✅                                         │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [Service C DB 장애 발생]                                       │
│                                                                 │
│  단계 1: Service C 응답 지연                                    │
│  Client ──► Service A ──► Service B ──► Service C ──✕── DB     │
│              (10ms)        (15ms)        (30초 대기...)          │
│                                                                 │
│  단계 2: Service B 스레드 고갈                                  │
│  ┌──────────────────────────────┐                               │
│  │ Service B Thread Pool        │                               │
│  │ [████████████████████] 200/200 │  ← C 응답 대기로 전부 점유  │
│  │ 새로운 요청 처리 불가!       │                               │
│  └──────────────────────────────┘                               │
│                                                                 │
│  단계 3: Service A도 스레드 고갈                                │
│  ┌──────────────────────────────┐                               │
│  │ Service A Thread Pool        │                               │
│  │ [████████████████████] 200/200 │  ← B 응답 대기로 전부 점유  │
│  │ 새로운 요청 처리 불가!       │                               │
│  └──────────────────────────────┘                               │
│                                                                 │
│  단계 4: 전체 시스템 다운                                       │
│  Client ──✕── Service A ──✕── Service B ──✕── Service C       │
│           503           503           503                       │
│                                                                 │
│  핵심: C 하나의 장애가 전체 시스템을 마비시킴                   │
│  이것이 Cascading Failure (연쇄 장애)                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Partial Failure (부분 장애) 란 분산 시스템에서만 존재하는 개념이다. 모놀리식에서는 프로세스가 죽으면 전체가 멈추지만, 분산 시스템에서는 일부 서비스만 실패하고 나머지는 정상이다. 문제는 이 “일부 실패”를 적절히 격리하지 않으면, 정상 서비스까지 전부 다운된다는 것이다.

┌─────────────────────────────────────────────────────────────────┐
│       Peter Deutsch의 분산 컴퓨팅의 8가지 오류 (1994)           │
│       "Fallacies of Distributed Computing"                      │
│                                                                 │
│  번호 │ 오류 (Fallacy)                    │ 현실                │
│  ─────┼───────────────────────────────────┼─────────────────────│
│   1   │ 네트워크는 신뢰할 수 있다         │ 패킷은 유실된다     │
│   2   │ 지연(Latency)은 0이다             │ 물리적 거리 존재    │
│   3   │ 대역폭은 무한하다                 │ 유한하고 경쟁한다   │
│   4   │ 네트워크는 안전하다               │ 공격받을 수 있다    │
│   5   │ 토폴로지는 변하지 않는다          │ 노드 추가/제거 상시 │
│   6   │ 관리자가 한 명이다                │ 여러 팀이 관리한다  │
│   7   │ 전송 비용은 0이다                 │ 직렬화/역직렬화 비용│
│   8   │ 네트워크는 동질적이다             │ 이기종 혼합 환경    │
│                                                                 │
│  Resilience 패턴은 이 8가지 오류를 인정하고,                    │
│  "실패는 반드시 발생한다"는 전제 위에 설계한다.                 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.2 전기 Circuit Breaker에서 소프트웨어 패턴으로

┌─────────────────────────────────────────────────────────────────┐
│       전기 회로 차단기 → 소프트웨어 Circuit Breaker              │
│                                                                 │
│  [전기 Circuit Breaker]                                         │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  정상 전류 ──── [차단기: CLOSED] ──── 부하(기기)         │    │
│  │                    │                                     │    │
│  │  과전류 발생 ──── [차단기: OPEN] ──✕── 부하              │    │
│  │                    │                 (전류 차단)          │    │
│  │  수동 리셋 ────── [차단기: CLOSED] ──── 부하              │    │
│  │                                                          │    │
│  │  목적: 과전류로 인한 화재/장비 손상 방지                  │    │
│  │  Thomas Edison, 1879년 특허 (US Patent 438,305)           │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [소프트웨어 Circuit Breaker]                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  정상 요청 ──── [CB: CLOSED] ──── 원격 서비스            │    │
│  │                    │                                     │    │
│  │  실패율 초과 ──── [CB: OPEN] ──✕── 원격 서비스           │    │
│  │                    │           (즉시 실패 반환)           │    │
│  │  대기 시간 후 ─── [CB: HALF-OPEN] ──── 원격 서비스       │    │
│  │                    │                   (제한적 시도)      │    │
│  │  성공 → CLOSED     │                                     │    │
│  │  실패 → OPEN       │                                     │    │
│  │                                                          │    │
│  │  목적: 장애 전파 차단 + 장애 서비스 복구 시간 확보       │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  핵심 차이:                                                     │
│  ├── 전기: 수동 리셋 필요                                      │
│  ├── 소프트웨어: 자동 복구 시도 (HALF-OPEN)                    │
│  └── HALF-OPEN은 소프트웨어에만 있는 개념                      │
│                                                                 │
│  역사:                                                          │
│  ├── 2007: Michael Nygard "Release It!" — 소프트웨어 CB 최초 제안│
│  ├── 2012: Netflix Hystrix — 대규모 프로덕션 최초 적용         │
│  └── 2014: Martin Fowler 블로그 — 패턴 대중화                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

1.3 Circuit Breaker 상태 머신

┌─────────────────────────────────────────────────────────────────┐
│              Circuit Breaker 상태 머신 (State Machine)           │
│                                                                 │
│                    요청 통과, 결과 기록                          │
│                   ┌───────────────┐                              │
│                   │               │                              │
│                   ▼               │                              │
│              ┌─────────┐         │                              │
│      ┌──────►│ CLOSED  │─────────┘                              │
│      │       │ (닫힘)   │                                        │
│      │       └────┬────┘                                        │
│      │            │                                              │
│      │            │ 실패율 ≥ threshold                           │
│      │            │ (예: 50% 초과)                               │
│      │            ▼                                              │
│      │       ┌─────────┐                                        │
│      │       │  OPEN   │──── 모든 요청 즉시 실패                │
│      │       │ (열림)   │     (CallNotPermittedException)        │
│      │       └────┬────┘                                        │
│      │            │                                              │
│      │            │ waitDurationInOpenState 경과                 │
│      │            │ (예: 60초)                                   │
│      │            ▼                                              │
│      │       ┌──────────┐                                       │
│      └───────┤HALF-OPEN │──── 제한된 수의 요청만 허용           │
│   성공 시    │ (반열림)  │     (permittedNumberOfCalls)           │
│              └────┬─────┘                                       │
│                   │                                              │
│                   │ 실패율 ≥ threshold                           │
│                   ▼                                              │
│              ┌─────────┐                                        │
│              │  OPEN   │ ← 다시 대기 상태로                     │
│              └─────────┘                                        │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  상태 이름의 기원 (전기공학):                                   │
│  ├── CLOSED: 회로가 연결됨 → 전류가 흐름 → 요청이 통과         │
│  ├── OPEN: 회로가 끊어짐 → 전류가 차단 → 요청이 차단           │
│  └── HALF-OPEN: 소프트웨어 전용 개념 (전기에는 없음)           │
│      → "살짝 열어서 탐침(probe) 요청을 보내본다"               │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  특수 상태 (Resilience4j):                                      │
│  ├── DISABLED: CB 비활성화, 모든 요청 통과, 상태 전이 없음      │
│  ├── FORCED_OPEN: 강제 OPEN, 모든 요청 차단                    │
│  └── METRICS_ONLY: 모든 요청 통과, 메트릭만 수집               │
│                                                                 │
│  DISABLED/FORCED_OPEN은 운영 중 수동 제어에 유용                │
│  METRICS_ONLY는 도입 초기 "관찰 모드"에 활용                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2. 핵심 Resilience 패턴 6가지

2.1 Circuit Breaker (회로 차단기)

┌─────────────────────────────────────────────────────────────────┐
│              Circuit Breaker Sliding Window 방식                 │
│                                                                 │
│  [COUNT_BASED] 최근 N개 호출 기준                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  slidingWindowSize = 10                                  │    │
│  │                                                          │    │
│  │  호출:  ✓ ✓ ✗ ✓ ✗ ✗ ✓ ✗ ✓ ✗                           │    │
│  │  번호:  1 2 3 4 5 6 7 8 9 10                             │    │
│  │                                                          │    │
│  │  실패: 5/10 = 50% → threshold 50% 도달 → OPEN           │    │
│  │                                                          │    │
│  │  새 호출 시 가장 오래된 결과 밀림 (Ring Buffer)           │    │
│  │  호출 11 성공 → [✓ ✗ ✓ ✗ ✗ ✓ ✗ ✓ ✗ ✓]                 │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [TIME_BASED] 최근 N초 기준                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  slidingWindowSize = 10 (초)                             │    │
│  │                                                          │    │
│  │  시간: |---10초 윈도우---|                                │    │
│  │        ✓✓✗✓✗  ✗✗✓✗✓✗  ← 이 구간의 호출만 집계          │    │
│  │                                                          │    │
│  │  트래픽 양에 무관하게 시간 기반 판단                      │    │
│  │  트래픽이 적은 서비스에 적합                              │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

핵심 파라미터:

파라미터 기본값 설명
failureRateThreshold 50 실패율 임계값 (%) — 이 값 이상이면 OPEN
slowCallRateThreshold 100 느린 호출 비율 임계값 (%)
slowCallDurationThreshold 60000ms 이 시간 초과하면 “느린 호출”로 분류
waitDurationInOpenState 60000ms OPEN 상태 유지 시간 (이후 HALF-OPEN)
permittedNumberOfCallsInHalfOpenState 10 HALF-OPEN에서 허용하는 호출 수
slidingWindowType COUNT_BASED COUNT_BASED 또는 TIME_BASED
slidingWindowSize 100 윈도우 크기 (호출 수 또는 초)
minimumNumberOfCalls 100 최소 호출 수 (이하면 판단 보류)
recordExceptions (empty) 실패로 기록할 예외 목록
ignoreExceptions (empty) 무시할 예외 목록 (실패 미집계)
// Resilience4j CircuitBreakerConfig 빌더 예시
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)                        // 실패율 50% 이상이면 OPEN
    .slowCallRateThreshold(80)                       // 느린 호출 80% 이상이면 OPEN
    .slowCallDurationThreshold(Duration.ofSeconds(2)) // 2초 초과 = 느린 호출
    .waitDurationInOpenState(Duration.ofSeconds(30))  // OPEN 30초 유지
    .permittedNumberOfCallsInHalfOpenState(5)         // HALF-OPEN에서 5개 시도
    .slidingWindowType(SlidingWindowType.COUNT_BASED)
    .slidingWindowSize(20)                            // 최근 20개 호출 기준
    .minimumNumberOfCalls(10)                         // 최소 10개 호출 후 판단
    .recordExceptions(IOException.class, TimeoutException.class)
    .ignoreExceptions(BusinessException.class)        // 비즈니스 예외는 미집계
    .build();

CircuitBreaker circuitBreaker = CircuitBreaker.of("paymentService", config);

2.2 Retry (재시도)

┌─────────────────────────────────────────────────────────────────┐
│              Retry 4가지 전략                                    │
│                                                                 │
│  [1. Simple Retry] 즉시 재시도                                  │
│  ├── 요청 ──✗── 즉시재시도 ──✗── 즉시재시도 ──✓               │
│  └── 일시적 네트워크 글리치에만 유효                            │
│                                                                 │
│  [2. Linear Backoff] 선형 증가 대기                             │
│  ├── 요청 ──✗── 1초 ── 재시도 ──✗── 2초 ── 재시도 ──✓        │
│  └── 예측 가능한 간격, 하지만 Thundering Herd에 취약           │
│                                                                 │
│  [3. Exponential Backoff] 지수 증가 대기                        │
│  ├── 요청 ──✗── 1초 ── 재시도 ──✗── 2초 ── 재시도 ──✗──      │
│  │          4초 ── 재시도 ──✗── 8초 ── 재시도 ──✓              │
│  └── 서버에 회복 시간을 점점 더 많이 부여                       │
│                                                                 │
│  [4. Exponential Backoff + Jitter] 지수 증가 + 무작위           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  Client A: ──✗── 0.8초 ── 재시도 ──✗── 2.3초 ──        │    │
│  │  Client B: ──✗── 1.2초 ── 재시도 ──✗── 1.7초 ──        │    │
│  │  Client C: ──✗── 0.5초 ── 재시도 ──✗── 3.1초 ──        │    │
│  │                                                          │    │
│  │  Jitter로 재시도 시점을 분산시킴                          │    │
│  │  → 동시 재시도 폭주(Thundering Herd) 방지                │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Jitter 공식:                                                   │
│  sleep = min(cap, base × 2^attempt) × random(0, 1)             │
│                                                                 │
│  예: base=1초, cap=60초                                         │
│  attempt 1: min(60, 1×2¹) × rand = 2 × 0.73 = 1.46초          │
│  attempt 2: min(60, 1×2²) × rand = 4 × 0.41 = 1.64초          │
│  attempt 3: min(60, 1×2³) × rand = 8 × 0.89 = 7.12초          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

역사적 기원:

  • Exponential Backoff: David Boggs, 1976년 Ethernet 논문에서 최초 제안. 여러 컴퓨터가 동시에 네트워크에 접근할 때 충돌을 해결하기 위해 대기 시간을 지수적으로 증가시킴. IEEE 802.3 (1983)에 공식 채택 (Binary Exponential Backoff).

  • Jitter: 원래 신호처리(Signal Processing) 용어. 신호의 시간적 변동을 의미. 소프트웨어에서는 AWS Architecture Blog (2015, “Exponential Backoff And Jitter”)에서 분산 시스템 재시도 전략으로 정립됨. Marc Brooker가 Full Jitter, Equal Jitter, Decorrelated Jitter 3가지 변종을 비교 분석.

# Spring Boot Retry 설정 (application.yml)
resilience4j:
  retry:
    instances:
      paymentService:
        maxAttempts: 3                          # 최대 3회 시도
        waitDuration: 1s                        # 기본 대기 1초
        enableExponentialBackoff: true           # 지수 백오프 활성화
        exponentialBackoffMultiplier: 2           # 2배씩 증가
        enableRandomizedWait: true               # Jitter 활성화
        randomizedWaitFactor: 0.5                # ±50% 범위
        retryExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException
        ignoreExceptions:
          - com.example.BusinessException         # 비즈니스 예외는 재시도 안 함

2.3 Timeout (타임아웃)

┌─────────────────────────────────────────────────────────────────┐
│              Timeout의 두 가지 종류                              │
│                                                                 │
│  [Connection Timeout]                                           │
│  Client ────── TCP 3-way Handshake ──────► Server               │
│          SYN ──────────────────────────►                        │
│              ◄────────────────────── SYN+ACK                   │
│          ACK ──────────────────────────►                        │
│          ← 이 과정에서 응답 없으면 Connection Timeout →         │
│                                                                 │
│  [Read Timeout (= Socket Timeout)]                              │
│  Client ────── HTTP Request ──────────► Server                  │
│                                          (처리 중...)           │
│          ← 요청 보낸 후 응답을 기다리는 시간 초과 →              │
│              Read Timeout 발생                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
구분 Connection Timeout Read Timeout
발생 시점 TCP 연결 수립 단계 데이터 수신 대기 단계
원인 서버 다운, 방화벽 차단, DNS 실패 서버 처리 지연, 부하 과중
일반 설정값 1~5초 5~30초 (서비스별 상이)
재시도 가치 높음 (일시적 네트워크 문제) 낮음 (서버 과부하 시 악화 가능)
// Resilience4j TimeLimiter 설정
TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom()
    .timeoutDuration(Duration.ofSeconds(3))  // 3초 초과 시 TimeoutException
    .cancelRunningFuture(true)               // 타임아웃 시 실행 중인 Future 취소
    .build();

TimeLimiter timeLimiter = TimeLimiter.of("paymentService", timeLimiterConfig);

Michael Nygard의 경고 (Release It!, 2007): “원격 호출에 타임아웃을 설정하지 않는 것은 프로덕션의 시한폭탄이다. 모든 원격 호출에는 반드시 타임아웃을 설정하라.”

2.4 Bulkhead (격벽)

┌─────────────────────────────────────────────────────────────────┐
│              Bulkhead 패턴의 기원: 선박 격벽                     │
│                                                                 │
│  [격벽 없는 선박]                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~         │    │
│  │  │          물이 전체로 퍼짐 → 침몰                │     │    │
│  │  │  💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧💧  │     │    │
│  │  └───────────────────────────────────────────┘          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [격벽 있는 선박]                                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~         │    │
│  │  │💧💧│      │      │      │      │      │              │    │
│  │  │💧💧│      │      │      │      │      │              │    │
│  │  └──┴──┴──────┴──────┴──────┴──────┴──────┘              │    │
│  │   침수     정상    정상    정상    정상    정상            │    │
│  │   ↑ 격벽이 물의 확산을 차단                               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  역사:                                                          │
│  ├── 고대 그리스 삼단노선(trireme): 격벽 최초 사용             │
│  ├── 1912 타이타닉: 16개 격벽이 있었지만, 상단이 열려 있어서   │
│  │   물이 넘쳐 인접 구획으로 확산 → 격벽 설계의 교훈           │
│  └── 소프트웨어: 서비스별 리소스 풀을 격리하여 장애 확산 방지  │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [소프트웨어 Bulkhead: Thread Pool 격리]                        │
│                                                                 │
│  [Bulkhead 없음] — 공유 Thread Pool                             │
│  ┌──────────────────────────────────────┐                       │
│  │ Shared Thread Pool (200 threads)     │                       │
│  │ Service A 호출 ████████              │                       │
│  │ Service B 호출 ████████████████████  │ ← B가 느려지면       │
│  │ Service C 호출 ██                    │    A, C도 영향 받음   │
│  │ 남은 스레드:   (없음!)               │                       │
│  └──────────────────────────────────────┘                       │
│                                                                 │
│  [Bulkhead 있음] — 서비스별 격리된 Thread Pool                  │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐            │
│  │ Pool A (50)  │ │ Pool B (100) │ │ Pool C (50)  │            │
│  │ ████████     │ │ ████████████ │ │ ██           │            │
│  │ 남은: 30     │ │ 남은: 0 (!)  │ │ 남은: 40     │            │
│  └──────────────┘ └──────────────┘ └──────────────┘            │
│   A 정상 동작      B 포화 → 격리     C 정상 동작               │
│                    B 초과 요청만 거부                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
구분 SemaphoreBulkhead ThreadPoolBulkhead
격리 방식 Semaphore (동시 실행 수 제한) 별도 Thread Pool
스레드 호출자 스레드 사용 전용 스레드 풀
큐 지원 없음 (초과 즉시 거부) 큐 대기 가능
오버헤드 매우 낮음 컨텍스트 스위칭 비용
리액티브/코루틴 적합 부적합 (블로킹 전용)
Timeout 분리 별도 TimeLimiter 필요 Thread Pool 자체 Timeout 가능
권장 상황 WebFlux, Kotlin Coroutine Servlet 기반 블로킹 서비스

Little’s Law로 Bulkhead 크기 산정:

┌─────────────────────────────────────────────────────────────────┐
│              Little's Law: L = λ × W                             │
│                                                                 │
│  L = 시스템 내 평균 요청 수 (필요한 동시 슬롯 수)              │
│  λ = 도착률 (초당 요청 수, TPS)                                │
│  W = 평균 체류 시간 (응답 시간)                                 │
│                                                                 │
│  예시:                                                          │
│  ├── Payment 서비스 TPS = 100 req/s                             │
│  ├── 평균 응답 시간 = 200ms = 0.2s                              │
│  ├── L = 100 × 0.2 = 20 (동시 실행 필요 슬롯)                  │
│  ├── 여유분 (피크 대비 2x) = 40                                 │
│  └── maxConcurrentCalls = 40                                    │
│                                                                 │
│  공식: maxConcurrentCalls = TPS × avgResponseTime × safetyFactor│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.5 Rate Limiter (속도 제한기)

┌─────────────────────────────────────────────────────────────────┐
│              Rate Limiter 4가지 알고리즘                         │
│                                                                 │
│  [1. Token Bucket] (토큰 양동이)                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  일정 간격으로 토큰 투입 ──► 🪣 [●●●●●○○○○○]            │    │
│  │                              bucket (최대 10개)          │    │
│  │  요청 도착 → 토큰 있으면 소비하고 통과                   │    │
│  │              토큰 없으면 거부 또는 대기                   │    │
│  │                                                          │    │
│  │  특징: 버스트 허용 (토큰이 쌓여 있으면 한번에 사용 가능) │    │
│  │  사용: AWS API Gateway, Guava RateLimiter                │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [2. Leaky Bucket] (누수 양동이)                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  요청 ──► 🪣 [큐에 쌓임] ──💧──💧──► 일정 속도 처리    │    │
│  │           bucket 가득 차면 거부                           │    │
│  │                                                          │    │
│  │  특징: 출력 속도 일정 (버스트 평활화)                    │    │
│  │  사용: 네트워크 트래픽 셰이핑, Nginx                     │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [3. Fixed Window Counter] (고정 윈도우)                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  |── 1분 ──|── 1분 ──|── 1분 ──|                        │    │
│  │  | count:95| count:10| count:87|                        │    │
│  │  |  (한도 100)                                           │    │
│  │                                                          │    │
│  │  문제: 윈도우 경계에서 2x 버스트 가능                    │    │
│  │  |...95|100...|  ← 경계 전후 2초에 195 요청 가능        │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [4. Sliding Window Log/Counter] (슬라이딩 윈도우)              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  현재 시각 기준으로 직전 1분간의 요청 수를 계산          │    │
│  │                                                          │    │
│  │  ←──────── 1분 윈도우 ────────►                          │    │
│  │         |███████████████████|                             │    │
│  │    윈도우가 시간과 함께 슬라이딩                          │    │
│  │                                                          │    │
│  │  Fixed Window의 경계 문제 해결                           │    │
│  │  메모리 비용 증가 (요청별 타임스탬프 저장)               │    │
│  │  사용: Resilience4j, Redis 기반 Rate Limiter             │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

TPS 계산 공식:

허용 TPS = limitForPeriod / limitRefreshPeriod

예: limitForPeriod = 50, limitRefreshPeriod = 1초
→ 50 TPS 허용
// Resilience4j RateLimiter 설정
RateLimiterConfig rateLimiterConfig = RateLimiterConfig.custom()
    .limitForPeriod(50)                             // 주기당 허용 호출 수
    .limitRefreshPeriod(Duration.ofSeconds(1))      // 주기 (1초마다 리셋)
    .timeoutDuration(Duration.ofMillis(500))        // 허용 대기 시간 (500ms)
    .build();

RateLimiter rateLimiter = RateLimiter.of("externalApi", rateLimiterConfig);

2.6 Fallback (대체 수단)

┌─────────────────────────────────────────────────────────────────┐
│              Fallback 패턴                                       │
│                                                                 │
│  군사 용어에서 유래:                                             │
│  "Fall back!" = "후퇴하라!" → 전선이 무너졌을 때 미리 준비된   │
│  후방 방어선으로 철수하는 것. 소프트웨어에서는 주 서비스 실패   │
│  시 대안으로 전환하는 것을 의미.                                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [3가지 Fallback 유형]                                          │
│                                                                 │
│  1. Static Fallback (정적 기본값)                               │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  추천 서비스 실패 → 인기 상품 목록 반환 (하드코딩)       │    │
│  │  환율 서비스 실패 → 마지막 알려진 환율 반환              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  2. Cache-based Fallback (캐시 기반)                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  원격 서비스 ──✗── Redis 캐시에서 이전 결과 반환         │    │
│  │  "Stale data is better than no data"                     │    │
│  │  (오래된 데이터라도 없는 것보다 낫다)                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  3. Alternative Service Fallback (대체 서비스)                  │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  주 결제 시스템(PG A) ──✗── 보조 결제 시스템(PG B)       │    │
│  │  주 CDN ──✗── 백업 CDN                                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [Graceful Degradation 계층]                                    │
│                                                                 │
│  Level 0: 정상 응답 (실시간 추천)                               │
│     │                                                           │
│     ▼ 추천 서비스 장애                                          │
│  Level 1: 캐시된 추천 결과 (5분 전 데이터)                      │
│     │                                                           │
│     ▼ 캐시 미스                                                 │
│  Level 2: 정적 인기 상품 목록 (일간 집계)                       │
│     │                                                           │
│     ▼ 정적 데이터도 불가                                        │
│  Level 3: 추천 영역 숨김 + 사용자 안내 문구                     │
│           "추천 서비스가 일시적으로 이용 불가합니다"             │
│                                                                 │
│  핵심: 서비스가 완전히 죽는 것보다                              │
│        기능을 점진적으로 축소하는 것이 낫다                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
// Kotlin Fallback 예시
@Service
class ProductRecommendationService(
    private val recommendationClient: RecommendationClient,
    private val redisTemplate: RedisTemplate<String, List<Product>>,
    private val popularProductsConfig: PopularProductsConfig
) {
    private val circuitBreaker = CircuitBreaker.ofDefaults("recommendation")

    fun getRecommendations(userId: String): List<Product> {
        return circuitBreaker.executeSuspendFunction {  // Kotlin Coroutine 지원
            recommendationClient.getPersonalized(userId)
        }.recover { throwable ->
            when (throwable) {
                is CallNotPermittedException -> getCachedRecommendations(userId)
                is TimeoutException -> getCachedRecommendations(userId)
                else -> getStaticPopularProducts()
            }
        }.get()
    }

    // Level 1: 캐시 기반 Fallback
    private fun getCachedRecommendations(userId: String): List<Product> {
        return redisTemplate.opsForValue().get("rec:$userId")
            ?: getStaticPopularProducts()   // Level 2로 강등
    }

    // Level 2: 정적 Fallback
    private fun getStaticPopularProducts(): List<Product> {
        return popularProductsConfig.defaultProducts  // application.yml에서 로드
    }
}

3. 패턴 조합 전략 (Defense in Depth)

3.1 Resilience4j 데코레이터 실행 순서

┌─────────────────────────────────────────────────────────────────┐
│         Resilience4j 데코레이터 실행 순서 (바깥 → 안쪽)         │
│                                                                 │
│  호출자 (Caller)                                                │
│    │                                                            │
│    ▼                                                            │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │ Retry (재시도)                                  [가장 바깥]│   │
│  │  ┌────────────────────────────────────────────────────┐  │   │
│  │  │ CircuitBreaker (회로 차단기)                       │  │   │
│  │  │  ┌──────────────────────────────────────────────┐  │  │   │
│  │  │  │ RateLimiter (속도 제한기)                     │  │  │   │
│  │  │  │  ┌────────────────────────────────────────┐  │  │  │   │
│  │  │  │  │ TimeLimiter (타임아웃)                  │  │  │  │   │
│  │  │  │  │  ┌──────────────────────────────────┐  │  │  │  │   │
│  │  │  │  │  │ Bulkhead (격벽)                  │  │  │  │  │   │
│  │  │  │  │  │  ┌────────────────────────────┐  │  │  │  │  │   │
│  │  │  │  │  │  │  실제 함수 호출            │  │  │  │  │  │   │
│  │  │  │  │  │  │  (원격 서비스)              │  │  │  │  │  │   │
│  │  │  │  │  │  └────────────────────────────┘  │  │  │  │  │   │
│  │  │  │  │  └──────────────────────────────────┘  │  │  │  │   │
│  │  │  │  └────────────────────────────────────────┘  │  │  │   │
│  │  │  └──────────────────────────────────────────────┘  │  │   │
│  │  └────────────────────────────────────────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                 │
│  실행 순서 (안쪽 → 바깥쪽):                                    │
│  Bulkhead → TimeLimiter → RateLimiter → CB → Retry             │
│                                                                 │
│  왜 이 순서인가?                                                │
│  ├── Retry가 가장 바깥: CB가 OPEN이면 Retry도 즉시 중단         │
│  │   (CB OPEN 상태에서 무의미한 재시도 방지)                    │
│  ├── CB가 Retry 안쪽: 각 재시도 결과가 CB에 기록됨             │
│  ├── RateLimiter가 CB 안쪽: 속도 제한 초과도 CB에 미기록        │
│  ├── TimeLimiter가 안쪽: 타임아웃 = 실패로 CB에 기록           │
│  └── Bulkhead가 가장 안쪽: 동시 실행 수 제한이 첫 관문         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

3.2 시나리오별 패턴 조합

시나리오 권장 패턴 조합 이유
동기 HTTP 호출 Retry + CB + Timeout + Fallback 가장 일반적인 조합
비동기 메시지 (Kafka 등) Retry + CB (DLQ Fallback) 메시지 재처리 + Dead Letter Queue
외부 API (3rd party) RateLimiter + Retry + CB + Fallback API 제한 준수 + 장애 대비
DB 호출 Timeout + CB + Bulkhead 커넥션 풀 보호
gRPC (양방향 스트림) Timeout + CB + Bulkhead 스트림 타임아웃 + 리소스 격리
배치 처리 Retry + CB (Bulkhead는 불필요) 순차 처리, 동시성 제한 의미 없음

도입 순서 권장 (스타트업 → 성숙기):

┌─────────────────────────────────────────────────────────────────┐
│              점진적 Resilience 패턴 도입                         │
│                                                                 │
│  Phase 1: Timeout + Retry                                       │
│  ├── 모든 원격 호출에 Timeout 설정 (가장 기본)                 │
│  └── 일시적 실패에 대한 Retry 추가                              │
│     │                                                           │
│     ▼                                                           │
│  Phase 2: Circuit Breaker + Fallback                            │
│  ├── 장애 전파 차단 (연쇄 장애 방지)                           │
│  └── CB OPEN 시 대체 응답 제공                                  │
│     │                                                           │
│     ▼                                                           │
│  Phase 3: Bulkhead + Rate Limiter                               │
│  ├── 서비스별 리소스 격리                                       │
│  └── 외부 API 호출량 제한                                       │
│     │                                                           │
│     ▼                                                           │
│  Phase 4: 모니터링 + Chaos Engineering                          │
│  ├── Prometheus/Grafana 메트릭 대시보드                         │
│  └── 장애 주입 테스트로 Resilience 검증                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4. 라이브러리/프레임워크 비교

4.1 주요 라이브러리 개요

┌─────────────────────────────────────────────────────────────────┐
│              주요 Resilience 라이브러리 계보                     │
│                                                                 │
│  2012 ────► Hystrix (Netflix)                                   │
│             │  최초의 대규모 프로덕션 Circuit Breaker            │
│             │  Thread Pool Isolation 중심                        │
│             │                                                   │
│  2017 ──┬──► Resilience4j                                       │
│         │    Java 8 함수형, 경량, 모듈러                        │
│         │    Hystrix의 정신적 후속작                             │
│         │                                                       │
│  2018 ──┼──► Hystrix Maintenance Mode (deprecated)              │
│         │    Netflix가 Resilience4j를 대안으로 추천              │
│         │                                                       │
│  2013 ──┼──► Polly 1.0 (.NET)                                   │
│         │    .NET 생태계의 표준 Resilience 라이브러리            │
│         │    v8 (2023): ResiliencePipeline, Hedging 패턴 추가    │
│         │    Simmy: Chaos Engineering 모듈 내장                  │
│         │                                                       │
│  2018 ──┼──► Sentinel (Alibaba)                                 │
│         │    Flow Control (유량 제어) 특화                       │
│         │    Double 11 (솽스이, 11.11) 실전 검증                │
│         │    실시간 대시보드 내장                                │
│         │                                                       │
│  Go ────┴──► Failsafe-go, Sony Gobreaker                        │
│              Go 생태계의 Resilience 라이브러리                  │
│              Failsafe-go: 범용 / Gobreaker: CB 특화             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.2 종합 비교표

항목 Resilience4j Hystrix Polly v8 Sentinel Failsafe-go
언어 Java/Kotlin Java .NET Java Go
유지보수 Active Deprecated (2018) Active Active Active
호출 오버헤드 <1µs ~1ms <1µs <1µs <1µs
학습 곡선 중간 높음 중간 중간 낮음
커뮤니티 매우 활발 레거시 활발 (.NET) 활발 (중국) 성장 중
Spring 통합 공식 지원 Spring Cloud Netflix N/A Spring Cloud Alibaba N/A
모니터링 Micrometer Hystrix Dashboard .NET Metrics 내장 Dashboard Prometheus
함수형 API 데코레이터 패턴 Command 패턴 Pipeline 패턴 SPI 기반 함수형
리액티브 RxJava, Reactor RxJava만 Async/Await Reactor Goroutine
CB O O O O O
Retry O X (외부) O X (외부) O
Bulkhead O (2종) O (ThreadPool) O O (Thread) O
Rate Limiter O X O O (핵심!) O
Timeout O (TimeLimiter) O (Command) O X O
Fallback 함수형 조합 Command 내장 Pipeline 체인 콜백 함수형 조합
Chaos X X O (Simmy) X X

4.3 Application-level vs Infrastructure-level Resilience

┌─────────────────────────────────────────────────────────────────┐
│         Resilience 적용 레이어 3계층                             │
│                                                                 │
│  [Layer 1] API Gateway (Ingress)                                │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Kong / AWS API Gateway / Nginx                          │    │
│  │  ├── Rate Limiting (클라이언트별)                        │    │
│  │  ├── 기본 Circuit Breaker                                │    │
│  │  └── 인증/인가                                           │    │
│  └─────────────────────────────────────────────────────────┘    │
│     │                                                           │
│     ▼                                                           │
│  [Layer 2] Service Mesh (Sidecar / Ambient)                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Istio + Envoy Sidecar                                   │    │
│  │  ├── Connection-level Circuit Breaker                    │    │
│  │  ├── Outlier Detection (이상치 감지)                     │    │
│  │  ├── Retry (HTTP/gRPC)                                   │    │
│  │  ├── Timeout                                             │    │
│  │  └── Load Balancing (Locality-aware)                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│     │                                                           │
│     ▼                                                           │
│  [Layer 3] Application Code                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Resilience4j / Polly / Sentinel                         │    │
│  │  ├── 비즈니스 로직 인지 Circuit Breaker                  │    │
│  │  ├── Semantic Retry (비즈니스 예외 구분)                  │    │
│  │  ├── Fallback (대체 비즈니스 로직)                       │    │
│  │  ├── Bulkhead (기능별 리소스 격리)                       │    │
│  │  └── Rate Limiter (비즈니스 규칙 기반)                   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
구분 Application Code Service Mesh API Gateway
장애 감지 비즈니스 예외 구분 가능 HTTP 상태 코드만 HTTP 상태 코드만
Fallback 복잡한 대체 로직 가능 불가 정적 응답만
세밀도 메서드/엔드포인트 단위 서비스/포트 단위 라우트 단위
언어 독립 언어별 구현 필요 완전 독립 완전 독립
성능 영향 없음 (~µs) Sidecar P50 +3ms 네트워크 홉 추가
배포 코드 변경 + 재배포 인프라 설정 변경 게이트웨이 설정
추천 반드시 사용 보조적 사용 보조적 사용

하이브리드 접근법 (권장):

┌─────────────────────────────────────────────────────────────────┐
│              하이브리드 Resilience 전략                           │
│                                                                 │
│  API Gateway: Rate Limiting (클라이언트별), 인증               │
│       │                                                         │
│       ▼                                                         │
│  Service Mesh: Retry (투명), Timeout (기본값), Outlier Detection│
│       │                                                         │
│       ▼                                                         │
│  Application: CB (비즈니스 예외 인지), Fallback (대체 로직),    │
│               Bulkhead (기능별 격리), 비즈니스 Rate Limit       │
│                                                                 │
│  원칙:                                                          │
│  ├── 인프라 레이어: "투명한" 보호 (코드 수정 없이)             │
│  ├── 애플리케이션 레이어: "지능적" 보호 (비즈니스 맥락 활용)   │
│  └── 둘 다 하되, 역할 중복을 최소화                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5. 실전 베스트 프랙티스

5.1 권장 설정값

# Spring Boot application.yml — Resilience4j 전체 설정 예시
resilience4j:
  circuitbreaker:
    configs:
      default:
        slidingWindowType: COUNT_BASED
        slidingWindowSize: 100
        minimumNumberOfCalls: 20
        failureRateThreshold: 50
        slowCallRateThreshold: 80
        slowCallDurationThreshold: 3s
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 10
        recordExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException
          - org.springframework.web.client.HttpServerErrorException
        ignoreExceptions:
          - com.example.BusinessException
      strict:  # 외부 API용 (더 엄격)
        slidingWindowSize: 20
        minimumNumberOfCalls: 5
        failureRateThreshold: 30
        waitDurationInOpenState: 60s
    instances:
      paymentService:
        baseConfig: default
      externalApiService:
        baseConfig: strict

  retry:
    configs:
      default:
        maxAttempts: 3
        waitDuration: 1s
        enableExponentialBackoff: true
        exponentialBackoffMultiplier: 2
        enableRandomizedWait: true
        randomizedWaitFactor: 0.5
        retryExceptions:
          - java.io.IOException
          - java.util.concurrent.TimeoutException
        ignoreExceptions:
          - com.example.BusinessException
    instances:
      paymentService:
        baseConfig: default
      externalApiService:
        maxAttempts: 5
        waitDuration: 2s

  timelimiter:
    configs:
      default:
        timeoutDuration: 3s
        cancelRunningFuture: true
    instances:
      paymentService:
        timeoutDuration: 5s   # 결제는 약간 여유
      searchService:
        timeoutDuration: 2s   # 검색은 빨라야 함

  bulkhead:
    configs:
      default:
        maxConcurrentCalls: 50
        maxWaitDuration: 100ms
    instances:
      paymentService:
        maxConcurrentCalls: 30     # 결제는 더 보수적
      catalogService:
        maxConcurrentCalls: 100    # 카탈로그는 넉넉하게

  ratelimiter:
    configs:
      default:
        limitForPeriod: 100
        limitRefreshPeriod: 1s
        timeoutDuration: 500ms
    instances:
      externalApiService:
        limitForPeriod: 10         # 외부 API 제한 준수
        limitRefreshPeriod: 1s

Kotlin/Coroutine 사용 시 주의사항:

// resilience4j-kotlin 모듈 필요
// implementation("io.github.resilience4j:resilience4j-kotlin:2.x.x")

// executeSuspendFunction으로 코루틴 지원
val result = circuitBreaker.executeSuspendFunction {
    // suspend 함수 호출 가능
    paymentClient.processPayment(request)
}

// 주의: SemaphoreBulkhead만 코루틴과 호환
// ThreadPoolBulkhead는 블로킹 전용이므로 코루틴에서 사용 금지

5.2 Bulkhead 크기 산정

┌─────────────────────────────────────────────────────────────────┐
│              Bulkhead 크기 산정 공식                              │
│                                                                 │
│  Little's Law 기반:                                             │
│  maxConcurrentCalls = TPS × avgResponseTime(초) × safetyFactor  │
│                                                                 │
│  예시 1: 일반 API                                               │
│  ├── TPS = 200 req/s                                            │
│  ├── avgResponseTime = 100ms = 0.1s                             │
│  ├── safetyFactor = 2.0 (피크 대비)                             │
│  └── maxConcurrentCalls = 200 × 0.1 × 2.0 = 40                 │
│                                                                 │
│  예시 2: 결제 API (느리지만 중요)                               │
│  ├── TPS = 50 req/s                                             │
│  ├── avgResponseTime = 500ms = 0.5s                             │
│  ├── safetyFactor = 1.5                                         │
│  └── maxConcurrentCalls = 50 × 0.5 × 1.5 = 37.5 ≈ 40          │
│                                                                 │
│  ThreadPool 크기 공식 (블로킹 전용):                            │
│  corePoolSize = TPS × avgResponseTime                           │
│  maxPoolSize = corePoolSize × 2                                 │
│  queueCapacity = maxPoolSize × 5 (최대 대기)                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.3 Timeout 설정 전략

┌─────────────────────────────────────────────────────────────────┐
│              Timeout 설정 전략                                   │
│                                                                 │
│  기본 공식:                                                     │
│  timeout = p99.9 latency + buffer(20~50%)                       │
│                                                                 │
│  예시:                                                          │
│  ├── p99.9 latency = 2초                                        │
│  ├── buffer = 50%                                               │
│  └── timeout = 2 × 1.5 = 3초                                   │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  체인 호출 Timeout 계산 (A → B → C):                            │
│                                                                 │
│  Client ──► A ──► B ──► C                                       │
│                                                                 │
│  C timeout = 2초                                                │
│  B timeout = C timeout + B 자체 처리 + buffer                   │
│            = 2초 + 0.5초 + 1초 = 3.5초                          │
│  A timeout = B timeout + A 자체 처리 + buffer                   │
│            = 3.5초 + 0.3초 + 1초 = 4.8초 ≈ 5초                 │
│                                                                 │
│  핵심 원칙:                                                     │
│  ├── 안쪽 서비스의 Timeout < 바깥 서비스의 Timeout              │
│  ├── 바깥이 더 짧으면, 안쪽 작업이 완료되어도 이미 타임아웃    │
│  └── Retry가 있다면: timeout × maxRetries < 상위 서비스 timeout │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.4 모니터링 통합

┌─────────────────────────────────────────────────────────────────┐
│         Resilience4j 모니터링 파이프라인                         │
│                                                                 │
│  Resilience4j ──► Micrometer ──► Prometheus ──► Grafana         │
│  (메트릭 생성)    (수집 추상화)   (시계열 저장)  (시각화/알림)  │
│                                                                 │
│  설정:                                                          │
│  resilience4j:                                                  │
│    circuitbreaker:                                              │
│      configs:                                                   │
│        default:                                                 │
│          registerHealthIndicator: true  # Actuator Health 연동  │
│                                                                 │
│  management:                                                    │
│    metrics:                                                     │
│      distribution:                                              │
│        percentiles-histogram:                                   │
│          resilience4j.circuitbreaker.calls: true                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

핵심 모니터링 메트릭:

메트릭 Prometheus 이름 의미 알림 조건
CB 상태 resilience4j_circuitbreaker_state 0=CLOSED, 1=OPEN, 2=HALF_OPEN state=1 (OPEN)
실패율 resilience4j_circuitbreaker_failure_rate 슬라이딩 윈도우 실패율 (%) > 30%
느린 호출율 resilience4j_circuitbreaker_slow_call_rate 느린 호출 비율 (%) > 50%
거부된 호출 resilience4j_circuitbreaker_not_permitted_calls_total CB OPEN으로 거부된 호출 수 > 0 (CB 열림 감지)
Bulkhead 가용 슬롯 resilience4j_bulkhead_available_concurrent_calls 남은 동시 호출 가능 수 < 5 (포화 임박)
Retry 횟수 resilience4j_retry_calls_total 재시도 발생 횟수 급증 시
# Prometheus AlertManager 규칙 예시
groups:
  - name: resilience4j-alerts
    rules:
      - alert: CircuitBreakerOpen
        expr: resilience4j_circuitbreaker_state == 1
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Circuit Breaker  is OPEN"
          description: >
            Circuit Breaker 이 OPEN 상태입니다.
            downstream 서비스 장애를 확인하세요.

      - alert: HighFailureRate
        expr: resilience4j_circuitbreaker_failure_rate > 30
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: " failure rate %"
          description: >
            의 실패율이 %로 높습니다.
            CB OPEN 임계값(50%) 이전에 원인을 확인하세요.

      - alert: BulkheadNearSaturation
        expr: resilience4j_bulkhead_available_concurrent_calls < 5
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "Bulkhead  nearly saturated"

Grafana Dashboard: Resilience4j 공식 대시보드 ID 21307 사용 가능.


6. 안티패턴과 함정

6.1 Retry Storm (재시도 폭풍)

┌─────────────────────────────────────────────────────────────────┐
│              Retry Storm (재시도 폭풍)                            │
│                                                                 │
│  [정상 상태]                                                    │
│  Clients ──► Service (1000 req/s) ──► DB                        │
│                                                                 │
│  [DB 장애 발생]                                                 │
│  Clients ──► Service ──✗── DB                                   │
│                                                                 │
│  각 클라이언트가 3회 재시도:                                    │
│  ┌──────────────────────────────────────┐                       │
│  │  정상: 1000 req/s                    │                       │
│  │  장애 + Retry: 1000 × 3 = 3000 req/s │  ← 3배 부하!         │
│  │                                      │                       │
│  │  DB가 회복되려는 순간...              │                       │
│  │  3000 req/s 폭풍이 DB를 다시 죽임    │                       │
│  │  → 회복 불가능한 악순환              │                       │
│  └──────────────────────────────────────┘                       │
│                                                                 │
│  실제 사례: Square Redis 장애 (2017)                            │
│  ├── Redis 서버 일시 장애                                       │
│  ├── 모든 서비스가 최대 500회(!) 연속 재시도                   │
│  ├── Redis가 복구 시도할 때마다 재시도 폭풍에 다시 다운         │
│  └── 해결까지 수 시간 소요                                      │
│                                                                 │
│  해결책:                                                        │
│  ├── Exponential Backoff + Jitter (재시도 분산)                 │
│  ├── maxRetries 제한 (3~5회)                                    │
│  ├── Circuit Breaker와 조합 (CB OPEN이면 재시도 중단)          │
│  └── 서비스 전체의 동시 재시도 수 상한 설정                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.2 Cascading Retry (재시도 증폭)

┌─────────────────────────────────────────────────────────────────┐
│              Cascading Retry (재시도 증폭) 문제                   │
│                                                                 │
│  A ──► B ──► C  (각 서비스가 독립적으로 3회 재시도)             │
│                                                                 │
│  C 실패 시:                                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  B → C: 시도 1 ──✗                                      │    │
│  │  B → C: 시도 2 ──✗                                      │    │
│  │  B → C: 시도 3 ──✗  → B가 실패 반환                     │    │
│  │                                                          │    │
│  │  A → B: 시도 1 (B가 위 과정 반복)                        │    │
│  │    B → C: ✗✗✗ (3회)                                     │    │
│  │  A → B: 시도 2                                           │    │
│  │    B → C: ✗✗✗ (3회)                                     │    │
│  │  A → B: 시도 3                                           │    │
│  │    B → C: ✗✗✗ (3회)                                     │    │
│  │                                                          │    │
│  │  결과: C에 대한 실제 호출 = 3 × 3 = 9회                  │    │
│  │  4단계 체인이면: 3 × 3 × 3 = 27회!                       │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  해결책:                                                        │
│  ├── 최상위 서비스(A)에서만 Retry                               │
│  ├── 중간 서비스(B)는 Retry 없이 즉시 실패 전파                │
│  ├── 각 서비스 경계에 Circuit Breaker 배치                      │
│  └── Retry 시 "Retry-Count" 헤더 전파 → 하위에서 중복 방지     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.3 Bulkhead 없이 Thread Pool 공유

┌─────────────────────────────────────────────────────────────────┐
│              Thread Pool 공유 문제                                │
│                                                                 │
│  [공유 Thread Pool 시나리오]                                    │
│                                                                 │
│  Service A (정상)  ──┐                                          │
│  Service B (느림)  ──┼──► 공유 Thread Pool (200)                │
│  Service C (정상)  ──┘                                          │
│                                                                 │
│  Thread Pool 상태 변화:                                         │
│                                                                 │
│  t=0  [A:10 B:10 C:10 ░░░░░░░░░░░░░░ 170 available]           │
│  t=1  [A:10 B:50 C:10 ░░░░░░░░░░░░░ 130 available]  B 느려짐  │
│  t=2  [A:10 B:150 C:10 ░░░░░░░░░░░░ 30 available]   B 누적    │
│  t=3  [A:5  B:190 C:5  ░░ 0 available]              전부 B    │
│                                                                 │
│  → A, C도 새 요청 처리 불가!                                    │
│  → B 하나의 장애가 전체 시스템 마비                             │
│                                                                 │
│  [Bulkhead 적용 후]                                             │
│                                                                 │
│  ┌── Pool A (50) ──┐ ┌── Pool B (100) ─┐ ┌── Pool C (50) ──┐  │
│  │ A:10 ░░░░ 40    │ │ B:100 (가득참!)  │ │ C:10 ░░░░ 40    │  │
│  └─────────────────┘ └─────────────────┘ └─────────────────┘   │
│   A 정상 동작         B 초과분만 거부     C 정상 동작           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.4 Fallback에서 원격 호출

┌─────────────────────────────────────────────────────────────────┐
│              Fallback 안티패턴: 원격 호출                        │
│                                                                 │
│  [BAD] Fallback에서 또 다른 원격 서비스 호출                    │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  try {                                                   │    │
│  │      return paymentServiceA.process(request) // 실패     │    │
│  │  } catch (e: Exception) {                                │    │
│  │      return paymentServiceB.process(request) // 이것도   │    │
│  │      // 같은 네트워크 문제로 실패할 가능성 높음!         │    │
│  │      // CB가 보호하는 의미가 없어짐                      │    │
│  │  }                                                       │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [GOOD] Fallback은 로컬에서 즉시 응답                           │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  circuitBreaker.executeFunction {                        │    │
│  │      paymentServiceA.process(request)                    │    │
│  │  }.recover {                                             │    │
│  │      // 로컬 캐시 또는 기본값 반환                       │    │
│  │      cachedResult ?: defaultResponse                     │    │
│  │  }                                                       │    │
│  │                                                          │    │
│  │  대체 서비스 호출이 반드시 필요하다면,                    │    │
│  │  해당 호출도 별도의 CB + Timeout으로 보호해야 함!        │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.5 Circuit Breaker Granularity 실수

┌─────────────────────────────────────────────────────────────────┐
│              CB Granularity (세분화 수준) 문제                    │
│                                                                 │
│  [BAD] 서비스 레벨 CB (너무 거친 단위)                          │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  OrderService에 대해 CB 하나:                            │    │
│  │                                                          │    │
│  │  GET  /orders       ──┐                                  │    │
│  │  POST /orders       ──┤── CB "orderService"              │    │
│  │  GET  /orders/stats ──┘                                  │    │
│  │                                                          │    │
│  │  /orders/stats만 느려져도 전체 CB가 OPEN                 │    │
│  │  → 정상인 GET /orders, POST /orders도 차단됨!           │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [GOOD] 엔드포인트 레벨 CB (적절한 단위)                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │                                                          │    │
│  │  GET  /orders       ── CB "orderService-getOrders"      │    │
│  │  POST /orders       ── CB "orderService-createOrder"    │    │
│  │  GET  /orders/stats ── CB "orderService-getStats"       │    │
│  │                                                          │    │
│  │  /orders/stats CB만 OPEN                                 │    │
│  │  → 나머지 엔드포인트는 정상 동작 유지                   │    │
│  │                                                          │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  원칙: 장애 영향 범위가 CB의 단위                               │
│  ├── 같은 DB 테이블 사용 → 같은 CB                              │
│  ├── 다른 인프라 의존성 → 다른 CB                               │
│  └── 장애 시 함께 죽는 엔드포인트 → 같은 CB                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

6.6 잘못된 Timeout 설정

┌─────────────────────────────────────────────────────────────────┐
│              Timeout 설정 실수                                   │
│                                                                 │
│  [너무 짧은 Timeout]                                            │
│  ├── 문제: 정상 요청도 timeout 초과로 실패                      │
│  ├── 결과: CB에 실패로 기록 → 오탐(false positive)으로 CB OPEN │
│  ├── 예: p99 = 2초인데 timeout = 1초 설정                      │
│  └── 1%의 정상 느린 요청이 계속 실패 처리됨                     │
│                                                                 │
│  [너무 긴 Timeout]                                              │
│  ├── 문제: 장애 시 스레드가 오래 대기                           │
│  ├── 결과: Thread Pool 고갈, Bulkhead 효과 상실                │
│  ├── 예: timeout = 60초, Bulkhead = 50 스레드                  │
│  │   → 50개 스레드가 각 60초씩 대기 → 50초간 새 요청 불가     │
│  └── Michael Nygard: "Timeout 없는 원격 호출 = 시한폭탄"       │
│                                                                 │
│  [적절한 Timeout]                                               │
│  ├── 공식: p99.9 + 20~50% buffer                                │
│  ├── 예: p99.9 = 2초 → timeout = 2.5~3초                      │
│  └── 주기적으로 latency 분포 확인 후 조정                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7. 마이그레이션 가이드

7.1 Hystrix → Resilience4j

Dependency 교체:

<!-- 제거 -->
<dependency>
    <groupId>com.netflix.hystrix</groupId>
    <artifactId>hystrix-core</artifactId>
</dependency>

<!-- 추가 -->
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot3</artifactId>
    <version>2.2.0</version>
</dependency>
<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-kotlin</artifactId>
    <version>2.2.0</version>
</dependency>

코드 마이그레이션 매핑:

Hystrix Resilience4j 비고
HystrixCommand CircuitBreaker.decorateFunction() Command → 함수형 데코레이터
@HystrixCommand(fallbackMethod = "fallback") @CircuitBreaker(name = "x", fallbackMethod = "fallback") 어노테이션 유사
HystrixCommandGroupKey CircuitBreakerRegistry 그룹 → 레지스트리
HystrixThreadPoolKey ThreadPoolBulkhead Thread Pool 분리
execution.isolation.thread.timeoutInMilliseconds TimeLimiter.timeoutDuration 별도 모듈로 분리
circuitBreaker.requestVolumeThreshold minimumNumberOfCalls 이름만 다름
circuitBreaker.errorThresholdPercentage failureRateThreshold 이름만 다름
circuitBreaker.sleepWindowInMilliseconds waitDurationInOpenState 이름만 다름

설정 매핑:

Hystrix 설정 Resilience4j 설정
hystrix.command.default.execution.isolation.strategy bulkhead (SEMAPHORE) 또는 thread-pool-bulkhead (THREAD)
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 3000 resilience4j.timelimiter.instances.xxx.timeoutDuration: 3s
hystrix.command.default.circuitBreaker.requestVolumeThreshold: 20 resilience4j.circuitbreaker.instances.xxx.minimumNumberOfCalls: 20
hystrix.command.default.circuitBreaker.errorThresholdPercentage: 50 resilience4j.circuitbreaker.instances.xxx.failureRateThreshold: 50
hystrix.threadpool.default.coreSize: 10 resilience4j.thread-pool-bulkhead.instances.xxx.coreThreadPoolSize: 10

주요 함정:

┌─────────────────────────────────────────────────────────────────┐
│              Hystrix → Resilience4j 마이그레이션 함정            │
│                                                                 │
│  1. 예외 처리 차이                                              │
│  ├── Hystrix: HystrixBadRequestException → fallback 미호출     │
│  ├── R4j: ignoreExceptions에 명시해야 함                       │
│  └── 마이그레이션 시 ignoreExceptions 설정 누락 주의!          │
│                                                                 │
│  2. Fallback 시그니처                                           │
│  ├── Hystrix: fallback 메서드에 Throwable 파라미터 선택적      │
│  ├── R4j: fallback 메서드에 Throwable 파라미터 필수            │
│  └── fun fallback(request: Req, t: Throwable): Response        │
│                                                                 │
│  3. Context Propagation                                         │
│  ├── Hystrix: HystrixRequestContext로 쓰레드 간 컨텍스트 전파  │
│  ├── R4j: ContextPropagator 인터페이스 별도 구현 필요          │
│  └── MDC, SecurityContext 등 전파 누락 주의                     │
│                                                                 │
│  4. Thread Pool vs Semaphore 기본값                             │
│  ├── Hystrix: Thread Pool이 기본 (별도 스레드에서 실행)        │
│  ├── R4j: Semaphore가 기본 (호출자 스레드에서 실행)            │
│  └── 동작 차이 인지 필요                                       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7.2 Application Level → Service Mesh 전환

┌─────────────────────────────────────────────────────────────────┐
│         Service Mesh CB의 한계와 전환 전략                       │
│                                                                 │
│  Service Mesh (Istio/Envoy)의 Circuit Breaker 한계:             │
│  ├── HTTP 상태 코드(5xx) 기반만 가능                           │
│  │   (비즈니스 예외 200 OK 응답 내부 에러 코드 미감지)         │
│  ├── Fallback 로직 불가 (요청 거부만 가능)                     │
│  ├── 느린 호출 감지 제한 (Outlier Detection은 다른 개념)       │
│  └── 세밀한 예외 구분 불가                                     │
│                                                                 │
│  전환 전략 (3단계):                                             │
│                                                                 │
│  Phase 1: 병행 운영 (Shadow Mode)                               │
│  ├── Service Mesh CB 설정 (느슨하게)                           │
│  ├── Application CB 유지 (기존대로)                             │
│  ├── 메트릭 비교 → 두 레이어의 동작 검증                       │
│  └── 기간: 2~4주                                               │
│                                                                 │
│  Phase 2: 역할 분리                                             │
│  ├── Service Mesh: Connection-level 보호 (Outlier Detection)   │
│  ├── Application: 비즈니스 로직 인지 보호 (Fallback, 예외 구분)│
│  └── 기간: 2~4주                                               │
│                                                                 │
│  Phase 3: Application 레이어 간소화                             │
│  ├── Service Mesh가 잘 처리하는 것 → Application에서 제거      │
│  │   (기본 Retry, Connection Timeout)                           │
│  ├── Application에서만 할 수 있는 것 → 유지                    │
│  │   (Fallback, 비즈니스 예외 CB, Bulkhead)                    │
│  └── 이후 운영 안정화                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

7.3 점진적 도입 전략

┌─────────────────────────────────────────────────────────────────┐
│              점진적 Resilience 도입 전략                          │
│                                                                 │
│  우선순위 매트릭스:                                             │
│                                                                 │
│  높은 위험                                                      │
│  ▲                                                              │
│  │  ①외부 API    ②서비스간 호출                                │
│  │                                                              │
│  │  ③DB 호출     ④캐시 호출                                    │
│  │                                                              │
│  └──────────────────────────────────────────────► 높은 호출량   │
│                                                                 │
│  도입 순서: ① → ② → ③ → ④                                    │
│  외부 API가 가장 불확실하고 제어 불가능 → 최우선                │
│                                                                 │
│  4주 도입 계획:                                                 │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Week 1: 외부 API 호출에 CB + Timeout + Retry           │    │
│  │          + METRICS_ONLY 모드로 관찰                      │    │
│  │                                                          │    │
│  │  Week 2: METRICS_ONLY → 실제 CB 활성화                   │    │
│  │          + 서비스 간 호출에 CB + Timeout 추가             │    │
│  │                                                          │    │
│  │  Week 3: Fallback 로직 구현                              │    │
│  │          + Bulkhead 추가 (주요 서비스별 격리)             │    │
│  │                                                          │    │
│  │  Week 4: 모니터링 대시보드 + 알림 설정                    │    │
│  │          + Chaos Engineering 테스트                       │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

8. 용어 사전

┌─────────────────────────────────────────────────────────────────┐
│                         용어 사전                                │
│                                                                 │
│  Circuit Breaker (회로 차단기)                                  │
│  ├── 전기공학에서 차용한 소프트웨어 패턴                        │
│  ├── 원격 호출 실패 감지 → 빠른 실패 반환으로 연쇄 장애 방지   │
│  └── 최초 제안: Michael Nygard, "Release It!" (2007)           │
│                                                                 │
│  CLOSED 상태                                                    │
│  ├── 전기: 회로가 연결됨 → 전류가 흐름                         │
│  └── 소프트웨어: 요청이 통과함 (정상 상태)                     │
│                                                                 │
│  OPEN 상태                                                      │
│  ├── 전기: 회로가 끊어짐 → 전류가 차단                         │
│  └── 소프트웨어: 요청이 즉시 거부됨 (장애 감지 상태)           │
│                                                                 │
│  HALF-OPEN 상태                                                 │
│  ├── 소프트웨어 전용 개념 (전기에는 없음)                      │
│  └── 제한적 요청을 보내 서비스 복구 여부 탐침(probe)           │
│                                                                 │
│  Bulkhead (격벽)                                                │
│  ├── 선박 격벽에서 유래: 침수 구역을 격리하여 전체 침몰 방지   │
│  ├── 고대 그리스 삼단노선에서 최초 사용                         │
│  └── 소프트웨어: 서비스별 리소스 풀 격리                       │
│                                                                 │
│  Retry (재시도)                                                 │
│  ├── 일시적 실패 시 동일 요청을 다시 시도                      │
│  └── 주의: 멱등성(idempotency) 보장 필수                       │
│                                                                 │
│  Exponential Backoff (지수 백오프)                               │
│  ├── 재시도 간격을 지수적으로 증가 (1초, 2초, 4초, 8초...)     │
│  ├── 기원: David Boggs, 1976년 Ethernet 충돌 해결 알고리즘     │
│  └── IEEE 802.3 (1983)에 공식 채택                              │
│                                                                 │
│  Jitter (지터)                                                  │
│  ├── 재시도 간격에 무작위성 추가 → 동시 재시도 분산            │
│  ├── 기원: 신호처리(Signal Processing)의 시간 변동              │
│  └── AWS Architecture Blog (2015)에서 분산 시스템 적용 정립     │
│                                                                 │
│  Timeout (타임아웃)                                             │
│  ├── Connection Timeout: TCP 연결 수립 대기 시간 제한           │
│  ├── Read Timeout (Socket Timeout): 데이터 수신 대기 시간 제한  │
│  └── "모든 원격 호출에 반드시 설정" — Michael Nygard           │
│                                                                 │
│  Rate Limiter (속도 제한기)                                     │
│  ├── Token Bucket: 토큰이 일정 속도로 충전, 요청 시 소비       │
│  │   → 버스트 허용                                              │
│  ├── Leaky Bucket: 요청이 큐에 들어가 일정 속도로 처리         │
│  │   → 출력 속도 일정 (트래픽 셰이핑)                          │
│  ├── Fixed Window: 고정 시간 구간별 카운터                     │
│  │   → 구현 단순하나 경계 문제                                 │
│  └── Sliding Window: 현재 시각 기준 이동 윈도우                │
│      → 정확하나 메모리 비용                                     │
│                                                                 │
│  Fallback (대체 수단)                                           │
│  ├── 군사 용어 "Fall back!" (후퇴) 에서 유래                    │
│  └── 주 서비스 실패 시 대안 응답 제공                           │
│                                                                 │
│  Graceful Degradation (우아한 성능 저하)                        │
│  ├── 장애 시 기능을 점진적으로 축소하며 서비스 유지             │
│  └── 반대: Fail-fast (빠른 실패)                                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  Resilience4j                                                   │
│  ├── "Resilience for Java"의 약자                               │
│  ├── "4j" 네이밍 컨벤션: Log4j, Hibernate4j 등 Java 관례       │
│  ├── Hystrix의 정신적 후속작 (직접 fork는 아님)                │
│  └── 경량, 모듈러, 함수형 API                                  │
│                                                                 │
│  Hystrix (히스트릭스)                                           │
│  ├── 호저(Porcupine) 속의 학명 (Hystrix cristata)              │
│  ├── Netflix가 "방어적 가시"의 이미지로 명명                   │
│  └── 2018년 유지보수 모드 전환, 사실상 deprecated              │
│                                                                 │
│  Istio (이스티오)                                               │
│  ├── 그리스어 ιστίο = "돛" (sail)                              │
│  ├── Kubernetes의 "항해(steering)" 은유와 연결                 │
│  └── Google/IBM/Lyft 공동 개발 Service Mesh                     │
│                                                                 │
│  Envoy (엔보이)                                                 │
│  ├── 프랑스어 envoyé = "메신저" (전령)                          │
│  ├── 서비스 간 통신을 중개하는 프록시 역할에서 유래             │
│  └── Lyft 개발, Istio의 데이터 플레인                          │
│                                                                 │
│  Polly                                                          │
│  ├── "Policy"에서 유래 (정책 기반 Resilience)                   │
│  └── .NET 생태계 표준 Resilience 라이브러리                     │
│                                                                 │
│  Sentinel (센티널)                                              │
│  ├── 라틴어 sentire = "감지하다" (to feel/sense)               │
│  ├── 보초, 감시자의 의미                                       │
│  └── Alibaba 개발, Flow Control (유량 제어) 특화                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  Cascading Failure (연쇄 장애)                                  │
│  ├── 하나의 장애가 연쇄적으로 다른 서비스로 전파               │
│  └── 전력 그리드의 대규모 정전과 동일한 원리                    │
│                                                                 │
│  Partial Failure (부분 장애)                                    │
│  ├── 분산 시스템에서 일부만 실패하는 상태                      │
│  └── 모놀리식에서는 존재하지 않는 개념                          │
│                                                                 │
│  Chaos Engineering (혼돈 공학)                                  │
│  ├── 프로덕션 시스템에 의도적 장애를 주입하여 내성 검증        │
│  ├── Netflix Chaos Monkey (2011): 무작위 인스턴스 종료          │
│  ├── Simian Army: Chaos Gorilla, Latency Monkey 등 도구 모음   │
│  └── "Chaos Engineering" by Casey Rosenthal et al. (2017)       │
│                                                                 │
│  SLI (Service Level Indicator)                                  │
│  ├── 서비스 수준을 측정하는 지표 (예: 가용률, 응답 시간)       │
│                                                                 │
│  SLO (Service Level Objective)                                  │
│  ├── SLI의 목표값 (예: 가용률 99.9%)                           │
│                                                                 │
│  SLA (Service Level Agreement)                                  │
│  ├── SLO에 법적/비즈니스 구속력을 부여한 계약                  │
│                                                                 │
│  Error Budget (에러 예산)                                       │
│  ├── SLO에서 허용하는 실패 여유분                              │
│  ├── 예: SLO 99.9% → 월간 43.8분 다운타임 허용                │
│  └── Google SRE 팀이 정립한 개념 (2016)                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

9. 연대표

┌─────────────────────────────────────────────────────────────────┐
│          Circuit Breaker & Resilience 역사 연대표                │
│                                                                 │
│  연도   │ 사건                                                  │
│  ───────┼───────────────────────────────────────────────────────│
│  1879   │ Thomas Edison, 전기 Circuit Breaker 특허              │
│         │ (US Patent 438,305 — 퓨즈 기반 과전류 차단)           │
│         │                                                       │
│  1982   │ Lamport, Shostak, Pease                               │
│         │ "The Byzantine Generals Problem" 발표                 │
│         │ → 분산 시스템 합의 문제의 이론적 토대                 │
│         │                                                       │
│  1994   │ Peter Deutsch, "Fallacies of Distributed Computing"   │
│         │ Sun Microsystems에서 8가지 분산 컴퓨팅 오류 정리      │
│         │ → "네트워크는 신뢰할 수 없다"                        │
│         │                                                       │
│  2000   │ Eric Brewer, CAP Theorem 발표 (PODC 2000)             │
│         │ → Consistency, Availability, Partition Tolerance      │
│         │   셋 중 둘만 선택 가능                                │
│         │                                                       │
│  2003   │ Eric Evans, "Domain-Driven Design" 출간               │
│         │                                                       │
│  2007   │ Michael Nygard, "Release It!" 출간                    │
│         │ ★ 소프트웨어 Circuit Breaker 패턴 최초 제안           │
│         │ Stability Patterns: CB, Timeout, Bulkhead 등 정립     │
│         │                                                       │
│  2008   │ Netflix DB 장애 (3일간 DVD 배송 중단)                 │
│         │ → AWS 마이그레이션 결정, 분산 시스템 전환 시작        │
│         │                                                       │
│  2010   │ Netflix 마이크로서비스 아키텍처 본격 전환             │
│         │                                                       │
│  2011   │ Netflix Chaos Monkey 오픈소스 공개                    │
│         │ ★ Chaos Engineering의 시작                            │
│         │ Hystrix 개발 착수 (Ben Christensen)                   │
│         │                                                       │
│  2012   │ Netflix Hystrix 오픈소스 공개                         │
│         │ ★ 대규모 프로덕션 Circuit Breaker 최초 사례           │
│         │ Thread Pool Isolation, Command 패턴 기반              │
│         │                                                       │
│  2013   │ Polly 1.0 출시 (.NET Circuit Breaker)                 │
│         │ Microsoft Transient Fault Handling Application Block  │
│         │                                                       │
│  2014   │ Martin Fowler, "CircuitBreaker" 블로그 포스트         │
│         │ ★ Circuit Breaker 패턴 대중화에 크게 기여             │
│         │                                                       │
│  2015   │ AWS Architecture Blog                                 │
│         │ "Exponential Backoff And Jitter" 발표                 │
│         │ Full/Equal/Decorrelated Jitter 비교 분석              │
│         │                                                       │
│  2016   │ Envoy Proxy 오픈소스 공개 (Lyft)                      │
│         │ Google SRE Book 출간 ("Site Reliability Engineering") │
│         │ → SLI/SLO/SLA/Error Budget 개념 정립                 │
│         │ Michael Nygard, "Release It! 2nd Ed." 출간            │
│         │                                                       │
│  2017   │ Resilience4j 최초 릴리스                              │
│         │ ★ Java 8 함수형 API, 경량 모듈러 설계                │
│         │ Istio 0.1 발표 (Google/IBM/Lyft)                      │
│         │ "Chaos Engineering" 서적 출간 (Casey Rosenthal 외)    │
│         │ Square Redis 장애 (Retry Storm 교훈)                  │
│         │                                                       │
│  2018   │ Istio 1.0 GA                                          │
│         │ ★ Hystrix Maintenance Mode 전환 (사실상 deprecated)   │
│         │ Netflix가 Resilience4j를 대안으로 공식 추천           │
│         │ Spring Cloud가 Resilience4j 통합 시작                 │
│         │ Sentinel 오픈소스 공개 (Alibaba)                      │
│         │ Chris Richardson, "Microservices Patterns" 출간       │
│         │                                                       │
│  2019   │ Spring Cloud Circuit Breaker 추상화 모듈 출시         │
│         │ → Hystrix, Resilience4j, Sentinel 동일 API로 추상화  │
│         │                                                       │
│  2020   │ Martin Kleppmann, "DDIA" 2nd draft 공개               │
│         │                                                       │
│  2021   │ Dapr v1.0 GA (Distributed Application Runtime)        │
│         │ → 사이드카 기반 언어 독립 Resilience                  │
│         │                                                       │
│  2023   │ Polly v8 릴리스                                       │
│         │ → ResiliencePipeline, Hedging, Simmy Chaos 내장       │
│         │ Resilience4j 2.x (Java 17+ 지원)                     │
│         │                                                       │
│  2024   │ Istio Ambient Mesh GA                                 │
│     ~   │ → Sidecar 없는 Service Mesh (ztunnel + waypoint)     │
│  2025   │ eBPF 기반 네트워킹 (Cilium) 확산                     │
│         │ → 커널 레벨에서 L4/L7 정책 적용                      │
│         │ Envoy Gateway v1.0 (Gateway API 표준 구현)            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

10. 대안 비교표

10.1 라이브러리 상세 비교표

비교 항목 Resilience4j Hystrix Polly v8 Sentinel Failsafe-go Sony Gobreaker
언어 Java/Kotlin Java C# (.NET) Java Go Go
최초 릴리스 2017 2012 2013 2018 2022 2017
최신 버전 2.2.x 1.5.18 (최종) 8.x 1.8.x 0.x 0.x
유지보수 Active Deprecated Active Active Active Active
설계 철학 함수형 데코레이터 Command 패턴 Pipeline 체인 SPI 플러그인 함수형 단일 목적
오버헤드 <1µs ~1ms <1µs <1µs <1µs <1µs
모듈성 높음 (각 패턴 별도 모듈) 낮음 (단일 JAR) 높음 중간 높음 N/A (CB만)
리액티브 Reactor, RxJava2/3 RxJava1만 async/await Reactor goroutine goroutine
Spring 통합 공식 Starter Spring Cloud Netflix N/A Spring Cloud Alibaba N/A N/A
대시보드 Grafana (외부) Hystrix Dashboard .NET Metrics 내장 Dashboard Prometheus 없음
Chaos 없음 없음 Simmy 내장 없음 없음 없음

10.2 패턴 비교표

비교 Circuit Breaker Retry 차이
목적 장애 확산 차단 일시적 실패 극복 CB는 “차단”, Retry는 “재시도”
실패 대응 빠른 실패 (Fail Fast) 재시도 후 성공 기대 CB는 포기, Retry는 재도전
조합 Retry 안쪽에 CB 배치 CB 바깥에 Retry 배치 CB OPEN이면 Retry도 중단
비교 Bulkhead Rate Limiter 차이
목적 리소스 격리 (내부 보호) 처리량 제한 (과부하 방지) 격리 vs 제한
적용 대상 서비스별 리소스 풀 엔드포인트별 호출량 내부 보호 vs 외부 제한
초과 시 BulkheadFullException RequestNotPermitted (대기/거부) 즉시 거부 vs 대기 가능
비교 Application CB Service Mesh CB 차이
장애 감지 비즈니스 예외 구분 HTTP 5xx만 세밀도 차이
Fallback 복잡한 대체 로직 불가 핵심 차이점
배포 코드 변경 + 재배포 설정 변경만 운영 편의성
언어 독립 언어별 구현 필요 완전 독립 폴리글랏 환경에서 유리

10.3 상황별 최적 선택 가이드

┌─────────────────────────────────────────────────────────────────┐
│              상황별 최적 선택 가이드                              │
│                                                                 │
│  Q1: 어떤 언어를 사용하는가?                                    │
│  ├── Java/Kotlin → Resilience4j (1순위), Sentinel (유량 제어 시)│
│  ├── .NET/C# → Polly v8                                        │
│  ├── Go → Failsafe-go (범용), Gobreaker (CB만)                 │
│  └── 다중 언어 → Service Mesh (Istio/Envoy) + Application CB   │
│                                                                 │
│  Q2: 어떤 문제를 해결하려는가?                                  │
│  ├── 연쇄 장애 방지 → Circuit Breaker                          │
│  ├── 일시적 실패 복구 → Retry + Exponential Backoff + Jitter   │
│  ├── 느린 서비스 격리 → Bulkhead + Timeout                     │
│  ├── API 호출량 제한 → Rate Limiter                            │
│  ├── 장애 시 대안 제공 → Fallback + Graceful Degradation       │
│  └── 전부 → Defense in Depth (모든 패턴 조합)                  │
│                                                                 │
│  Q3: 인프라 환경은?                                             │
│  ├── Kubernetes + Service Mesh → Istio CB + Application CB     │
│  ├── Kubernetes만 → Application CB 필수                        │
│  ├── VM/베어메탈 → Application CB 필수                          │
│  └── Serverless → 클라우드 내장 Retry + Application CB         │
│                                                                 │
│  Q4: 팀 성숙도는?                                               │
│  ├── 초기 (Resilience 경험 없음)                                │
│  │   → Timeout + Retry만 먼저 (2주)                            │
│  │   → CB + Fallback 추가 (2주)                                │
│  │   → 모니터링 구축 후 Bulkhead + RateLimiter (2주)           │
│  ├── 중급 (일부 패턴 경험)                                     │
│  │   → 전체 패턴 도입 + METRICS_ONLY 관찰 기간                │
│  └── 고급 (Resilience 운영 경험)                                │
│      → Chaos Engineering + Error Budget 기반 운영              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

11. 성능 벤치마크

┌─────────────────────────────────────────────────────────────────┐
│              성능 벤치마크                                        │
│                                                                 │
│  [1. Resilience4j 오버헤드]                                     │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Circuit Breaker 호출 오버헤드: < 1µs (마이크로초)       │    │
│  │  Bulkhead (Semaphore): ~ 0.5µs                           │    │
│  │  Rate Limiter: ~ 0.3µs                                   │    │
│  │  Retry (오버헤드만, 재시도 제외): ~ 0.1µs                │    │
│  │                                                          │    │
│  │  결론: 실제 원격 호출 (수 ms~수백 ms) 대비 무시할 수준   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [2. Hystrix vs Resilience4j 비교]                              │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  항목               │ Hystrix      │ Resilience4j       │    │
│  │  ───────────────────┼──────────────┼────────────────────│    │
│  │  호출 오버헤드       │ ~1ms         │ <1µs (1000배 빠름) │    │
│  │  CB OPEN 거부 속도   │ ~0.5ms       │ <1µs (500배 빠름)  │    │
│  │  메모리 (인스턴스당) │ ~2MB         │ ~50KB              │    │
│  │  Thread Pool 방식    │ 전용 풀 필수 │ Semaphore 기본     │    │
│  │  GC 영향            │ 높음         │ 거의 없음          │    │
│  │                                                          │    │
│  │  주요 차이 원인:                                         │    │
│  │  ├── Hystrix: RxJava Observable 생성/구독 오버헤드       │    │
│  │  ├── Hystrix: Command 객체 매번 생성                     │    │
│  │  └── R4j: Ring Buffer + Atomic 연산 (객체 생성 최소화)   │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [3. Service Mesh (Istio/Envoy) 레이턴시]                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  Istio Sidecar (Envoy) 추가 레이턴시:                    │    │
│  │  ├── P50: +2~3ms                                        │    │
│  │  ├── P99: +5~10ms                                       │    │
│  │  └── P99.9: +15~25ms                                    │    │
│  │                                                          │    │
│  │  Istio Ambient Mesh (ztunnel) 추가 레이턴시:             │    │
│  │  ├── L4 (ztunnel만): P50 +0.5ms, P99 +1~2ms            │    │
│  │  ├── L7 (waypoint): P50 +1~2ms, P99 +3~5ms             │    │
│  │  └── Sidecar 대비 약 40~60% 레이턴시 감소               │    │
│  │                                                          │    │
│  │  참고: 레이턴시 vs 가시성/보안 트레이드오프               │    │
│  │  대부분의 서비스에서 수 ms 추가 레이턴시는 수용 가능     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  [4. Thread Pool vs Semaphore 격리 비교]                        │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  항목               │ Thread Pool  │ Semaphore          │    │
│  │  ───────────────────┼──────────────┼────────────────────│    │
│  │  컨텍스트 스위칭     │ 있음 (~10µs) │ 없음               │    │
│  │  메모리 (스레드당)   │ ~512KB       │ 없음               │    │
│  │  타임아웃 분리       │ 독립 가능    │ 별도 TimeLimiter   │    │
│  │  리액티브 호환       │ 부적합       │ 적합               │    │
│  │  코루틴 호환         │ 부적합       │ 적합               │    │
│  │  격리 강도           │ 완전 격리    │ 동시성 제한만      │    │
│  │                                                          │    │
│  │  권장:                                                   │    │
│  │  ├── Servlet 블로킹: Thread Pool (강한 격리)             │    │
│  │  ├── WebFlux/Coroutine: Semaphore (오버헤드 최소)        │    │
│  │  └── 혼합 환경: Semaphore 기본, 필요 시 Thread Pool      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

12. 참고 자료

핵심 서적

서적 저자 연도 핵심 기여
Release It! (1st Ed.) Michael Nygard 2007 소프트웨어 Circuit Breaker 최초 제안, Stability Patterns
Release It! (2nd Ed.) Michael Nygard 2018 클라우드 시대 업데이트
Designing Data-Intensive Applications (DDIA) Martin Kleppmann 2017 분산 시스템 설계의 바이블
Chaos Engineering Casey Rosenthal, Nora Jones 2020 Chaos Engineering 원칙과 실전
Site Reliability Engineering (SRE) Betsy Beyer 외 2016 SLI/SLO/SLA/Error Budget, Google SRE 프랙티스
Microservices Patterns Chris Richardson 2018 44개+ 마이크로서비스 패턴 카탈로그

공식 문서

라이브러리/프레임워크 URL
Resilience4j https://resilience4j.readme.io/
Resilience4j GitHub https://github.com/resilience4j/resilience4j
Hystrix (archived) https://github.com/Netflix/Hystrix/wiki
Polly (.NET) https://github.com/App-vNext/Polly
Polly v8 Docs https://www.pollydocs.org/
Sentinel (Alibaba) https://github.com/alibaba/Sentinel
Sentinel 문서 https://sentinelguard.io/en-us/
Istio https://istio.io/latest/docs/
Envoy https://www.envoyproxy.io/docs/
Dapr https://docs.dapr.io/
Failsafe-go https://github.com/failsafe-go/failsafe-go
Sony Gobreaker https://github.com/sony/gobreaker

블로그 / 기술 포스트

제목 저자/출처 URL
CircuitBreaker Martin Fowler (2014) https://martinfowler.com/bliki/CircuitBreaker.html
Making the Netflix API More Resilient Netflix TechBlog (2011) https://netflixtechblog.com/
Exponential Backoff And Jitter AWS Architecture Blog (2015) https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
Fault Tolerance in a High Volume, Distributed System Netflix TechBlog (2012) https://netflixtechblog.com/
Fallacies of Distributed Computing Peter Deutsch (1994) https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing

벤치마크 / 비교 자료

제목 출처 비고
Resilience4j vs Hystrix 벤치마크 Resilience4j 공식 문서 오버헤드 비교
Istio Performance Benchmarks Istio 공식 문서 Sidecar 레이턴시 측정
Ambient Mesh Performance Istio Blog Sidecar vs Ambient 비교
Grafana Dashboard #21307 Grafana Labs Resilience4j 모니터링

블로그 / 기술 포스트 (추가)

제목 저자/출처 URL
Performance Under Load Netflix TechBlog (2018) https://netflixtechblog.medium.com/performance-under-load-3e6fa9a60581
Prioritized Load Shedding at Netflix Netflix TechBlog https://netflixtechblog.com/prioritized-load-shedding-in-the-overloaded-state-of-a-netflix-microservice-4b532e835f37
Using Load Shedding to Survive Brownouts AWS Architecture Blog https://aws.amazon.com/builders-library/using-load-shedding-to-avoid-overload/

장애 포스트모텀

장애 연도 공식 포스트모텀 URL
Amazon DynamoDB Retry Storm 2015 https://aws.amazon.com/message/5467D2/
Cloudflare WAF Outage 2019 https://blog.cloudflare.com/cloudflare-outage/
Google Cloud Networking Incident 2019 https://status.cloud.google.com/incident/cloud-networking/19009
Facebook/Meta BGP Outage 2021 https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/
AWS US-EAST-1 Outage 2021 https://aws.amazon.com/message/12721/
GitHub MySQL Cluster Incident 2018 https://github.blog/2018-10-30-oct21-post-incident-analysis/
Slack Transit Gateway Outage 2021 https://slack.engineering/slacks-outage-on-january-4th-2021/
카카오 판교 DC 화재 2022 https://www.kakaocorp.com/page/detail/9812

학술 논문 / RFC

논문 저자 연도 비고
The Byzantine Generals Problem Lamport, Shostak, Pease 1982 분산 합의 이론
Harvest, Yield, and Scalable Tolerant Systems Fox, Brewer 1999 CAP Theorem 전신
IEEE 802.3 CSMA/CD IEEE 1983 Exponential Backoff 공식 채택
Life beyond Distributed Transactions Pat Helland 2007 분산 트랜잭션 한계
Gray Failure: The Achilles’ Heel of Cloud-Scale Systems Huang et al. (MS Research) 2017 차등 관찰 가능성 문제
Making Reliable Distributed Systems in the Presence of Software Errors Joe Armstrong 2003 Erlang “Let It Crash” 철학, Supervision Tree
The Tail at Scale Jeff Dean, Luiz André Barroso 2013 Fan-out 지연 분포, Hedging 패턴
Congestion Avoidance and Control Van Jacobson 1988 TCP Congestion Control, AIMD, Slow Start

관련 프로젝트 내 문서

┌─────────────────────────────────────────────────────────────────┐
│                    관련 문서 링크                                 │
│                                                                 │
│  프로젝트 내 관련 문서:                                         │
│  ├── docs/backend/보상트랜잭션-saga.md                          │
│  │   └── Saga 패턴과 분산 트랜잭션 보상                         │
│  ├── docs/backend/domain-event-outbox-패턴.md                   │
│  │   └── Outbox 패턴, Eventual Consistency                      │
│  ├── docs/backend/dual-write-패턴.md                            │
│  │   └── Dual Write 문제의 상세 분석                            │
│  ├── docs/backend/kotlin-coroutine-async.md                     │
│  │   └── Kotlin Coroutine (R4j Coroutine 연동 관련)            │
│  ├── docs/cloud/istio-서비스메시.md                             │
│  │   └── Istio Service Mesh 상세                                │
│  ├── docs/cloud/destinationrule-서비스QoS.md                    │
│  │   └── Istio DestinationRule (Service Mesh CB 설정)           │
│  └── docs/backend/spring-actuator-health.md                     │
│      └── Spring Actuator (CB Health Indicator 연동)             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

13. 학술적/이론적 기반 심화

13.1 Joe Armstrong의 “Let It Crash” 철학

Joe Armstrong의 2003년 박사논문 “Making Reliable Distributed Systems in the Presence of Software Errors” 는 Erlang/OTP의 핵심 철학인 “Let It Crash” 를 정립했다. 이 철학은 오류를 방지하려 하지 말고, 오류가 발생했을 때 빠르게 감지하고 복구하라는 것이다. Circuit Breaker 패턴과 깊은 연관이 있다.

┌─────────────────────────────────────────────────────────────────┐
│          Erlang Supervision Tree vs Circuit Breaker              │
│                                                                 │
│  [Erlang Supervision Tree]                                      │
│                                                                 │
│           ┌─── Supervisor (top) ───┐                            │
│           │                        │                            │
│     Supervisor A            Supervisor B                        │
│      │      │                │      │                           │
│   Worker1 Worker2        Worker3 Worker4                        │
│                                                                 │
│  Worker3 crash → Supervisor B가 재시작                          │
│  Supervisor B 반복 실패 → Supervisor (top)이 B 전체 재시작      │
│  → 장애가 격리되고, 자동 복구됨                                 │
│                                                                 │
│  [Circuit Breaker]                                              │
│                                                                 │
│  Service A ──► CB ──► Service B                                 │
│                │                                                │
│                ├── CLOSED: 정상 통과                             │
│                ├── OPEN: 빠른 실패 (장애 격리)                  │
│                └── HALF-OPEN: 복구 시도                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
비교 항목 Erlang Supervision Tree Circuit Breaker
철학 “Let It Crash” — 실패를 허용하고 복구 “Fail Fast” — 실패를 감지하고 차단
장애 감지 Process crash (link/monitor) 오류율/지연 임계값 초과
장애 격리 Supervision 계층 구조 CB 인스턴스별 격리
복구 전략 Supervisor가 자동 재시작 Half-Open → 점진적 복구 확인
복구 범위 프로세스/노드 단위 서비스/엔드포인트 호출 단위
Escalation 상위 Supervisor로 전파 없음 (Bulkhead으로 보완)
적용 레벨 프로세스 내부 서비스 간 통신
공통점 장애를 정상 흐름의 일부로 설계에 포함 장애를 정상 흐름의 일부로 설계에 포함

핵심 통찰: Erlang의 “Let It Crash”는 프로세스 내부의 오류 격리/복구이고, Circuit Breaker는 서비스 간 오류 격리/복구이다. 두 패턴 모두 “오류는 반드시 발생한다” 는 전제 위에 설계된다.

13.2 제어이론(Control Systems Theory)과 Circuit Breaker

Circuit Breaker의 상태 전이 메커니즘은 제어이론의 Negative Feedback Loop와 정확히 대응된다. 시스템의 출력(오류율)을 측정하고, 목표값(임계값)과 비교하여, 제어 신호(상태 전이)를 발생시킨다.

┌─────────────────────────────────────────────────────────────────┐
│          Negative Feedback Loop ↔ Circuit Breaker 매핑           │
│                                                                 │
│  [제어이론 Feedback Loop]                                       │
│                                                                 │
│  Set Point ──► (+) ──► Controller ──► Actuator ──► Process     │
│                 ↑ (-)                                ↓          │
│                 └────────── Sensor ◄─────────────────┘          │
│                                                                 │
│  [Circuit Breaker 매핑]                                         │
│                                                                 │
│  오류율 임계값 ──► 비교 ──► CB 상태전이 ──► 트래픽 제어 ──► 서비스│
│      (50%)         ↑ (-)     로직                        ↓     │
│                    └────── Sliding Window ◄───────────────┘     │
│                            (오류율 측정)                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
제어이론 구성요소 Circuit Breaker 대응 설명
Set Point (목표값) failureRateThreshold (오류율 임계값) 시스템이 유지해야 할 목표 오류율
Process Variable (현재값) 현재 Sliding Window 내 오류율 실시간 측정되는 실제 오류율
Error Signal (오차) 현재 오류율 - 임계값 임계값 초과 시 양수 → 제어 필요
Controller (제어기) CB 상태전이 로직 CLOSED→OPEN→HALF-OPEN 결정
Actuator (작동기) 트래픽 허용/차단 OPEN 시 요청 차단, CLOSED 시 통과
Sensor (센서) Sliding Window (Ring Buffer) 호출 결과를 수집하여 오류율 계산
Plant (제어 대상) 대상 서비스(downstream) 보호 대상 원격 서비스

Flapping/Oscillation 문제 = 불충분한 Phase Margin

Circuit Breaker가 OPEN↔CLOSED를 빠르게 반복하는 Flapping 현상은 제어이론에서 불충분한 Phase Margin으로 인한 Oscillation과 동일한 문제이다.

┌─────────────────────────────────────────────────────────────────┐
│          Flapping (진동) 문제와 해결                              │
│                                                                 │
│  [Flapping 발생]                                                │
│                                                                 │
│  오류율 ───╲  ╱──╲  ╱──╲  ╱──╲  ╱──                            │
│  임계값 ────╳────╳────╳────╳──── (빠른 교차)                    │
│  CB상태  OPEN CLOSED OPEN CLOSED OPEN ...                       │
│                                                                 │
│  원인: waitDurationInOpenState가 너무 짧거나,                   │
│        permittedNumberOfCallsInHalfOpenState가 너무 적음        │
│                                                                 │
│  [안정화된 상태]                                                 │
│                                                                 │
│  오류율 ───╲                    ╱───                             │
│  임계값 ────╲──────────────────╱──── (충분한 회복 시간)          │
│  CB상태  OPEN  (wait 30s)  HALF-OPEN → CLOSED                   │
│                                                                 │
│  해결: 충분한 Damping (waitDuration ↑, Half-Open 호출수 ↑)      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

PID Controller의 D항과 Half-Open 상태

PID Controller에서 Derivative(D) 항은 오차의 변화율을 감시하여 과잉 제어를 방지한다. Circuit Breaker의 Half-Open 상태는 이와 유사한 역할을 한다:

PID 요소 CB 대응 역할
P (Proportional) 오류율이 임계값 초과 → OPEN 즉각 반응
I (Integral) Sliding Window 누적 오류율 과거 누적 고려
D (Derivative) Half-Open에서 점진적 복구 확인 급격한 변화 방지, 안정적 복원

13.3 대기열 이론 심화: Erlang C Formula와 Bulkhead 크기 결정

Little’s Law (L = λ × W)는 안정 상태에서만 유효하다. 실제 시스템에서 트래픽 변동성(버스트)을 고려하려면 Erlang C Formula (A.K. Erlang, 1917, M/M/c 모델)를 사용해야 한다.

┌─────────────────────────────────────────────────────────────────┐
│          Erlang C Formula로 Bulkhead 크기 결정                   │
│                                                                 │
│  M/M/c 모델 (Markov arrival / Markov service / c servers)      │
│                                                                 │
│  입력:                                                          │
│  ├── λ = 도착률 (req/s)                                        │
│  ├── μ = 서비스율 (1/평균응답시간)                              │
│  ├── c = 서버(슬롯) 수                                         │
│  └── A = λ/μ (offered traffic, Erlang 단위)                    │
│                                                                 │
│  Erlang C 확률 (대기 확률):                                     │
│                                                                 │
│              (A^c / c!) × (c / (c - A))                        │
│  P_w(c) = ──────────────────────────────────                   │
│            Σ(k=0..c-1) A^k/k! + (A^c/c!) × (c/(c-A))        │
│                                                                 │
│  예시: λ=100 req/s, 평균응답=200ms → A=100×0.2=20 Erlang       │
│                                                                 │
│  │  c (슬롯수)  │ P_w (대기확률)  │ 판정              │        │
│  │──────────────│────────────────│───────────────────│        │
│  │  20          │ ~100% (불안정!) │ c = A 이면 불안정 │        │
│  │  25          │ ~36%           │ 너무 높음          │        │
│  │  30          │ ~8%            │ 수용 가능          │        │
│  │  35          │ ~1.5%          │ 권장               │        │
│  │  40          │ ~0.2%          │ 보수적 (안전)      │        │
│                                                                 │
│  결론: maxConcurrentCalls = 35 (p99 대기 확률 < 2%)            │
│  Little's Law로는 20이지만, 실제로는 35가 적절               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Little’s Law의 한계:

항목 Little’s Law Erlang C
가정 안정 상태 (Steady State) 포아송 도착 + 지수 서비스 시간
변동성 고려 없음 (평균만) 확률적 변동 포함
결과 최소 필요 슬롯 수 목표 대기 확률 달성 슬롯 수
실무 적용 빠른 추정 정밀 산정
한계 버스트 트래픽 미고려 M/M/c 가정 (실제와 다를 수 있음)

M/M/1 불안정 조건: λ ≥ μ (도착률이 서비스율 이상이면 대기열이 무한 증가). 이것이 바로 Bulkhead 없이 느린 서비스를 호출할 때 스레드 풀이 고갈되는 근본 원인이다.

13.4 TCP Congestion Control과 Resilience 패턴

Van Jacobson의 1988년 논문 “Congestion Avoidance and Control” 은 TCP Congestion Control의 기초를 놓았다. 놀랍게도 분산 시스템 Resilience 패턴과 정확히 대응된다.

┌─────────────────────────────────────────────────────────────────┐
│          TCP Congestion Control ↔ Resilience 패턴 매핑           │
│                                                                 │
│  [TCP]                         [Resilience]                     │
│                                                                 │
│  Slow Start                    CB Half-Open (점진적 복구)       │
│  ┌──────────────┐              ┌──────────────┐                │
│  │ cwnd: 1→2→4  │              │ 허용: 3→5→10 │                │
│  │ 지수 증가    │              │ 점진적 증가  │                │
│  └──────────────┘              └──────────────┘                │
│                                                                 │
│  Congestion Avoidance          CB CLOSED (정상)                 │
│  ┌──────────────┐              ┌──────────────┐                │
│  │ cwnd 선형 ↑  │              │ 전체 트래픽  │                │
│  │ 안정 전송    │              │ 허용         │                │
│  └──────────────┘              └──────────────┘                │
│                                                                 │
│  Timeout/3-dup ACK             CB → OPEN 전이                   │
│  ┌──────────────┐              ┌──────────────┐                │
│  │ cwnd 급감    │              │ 트래픽 차단  │                │
│  │ (1 or 1/2)  │              │ (Fail Fast)  │                │
│  └──────────────┘              └──────────────┘                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
TCP Congestion Control Resilience 패턴 공통 원리
AIMD (Additive Increase, Multiplicative Decrease) CB CLOSED(증가)/OPEN(급감) 점진적 증가, 급격한 감소
Slow Start Half-Open 상태 (제한된 요청 허용) 복구 시 점진적 트래픽 증가
RTO (Retransmission Timeout) waitDurationInOpenState 재시도까지 대기 시간
cwnd (Congestion Window) permittedNumberOfCallsInHalfOpenState 허용 동시 요청 수
Exponential Backoff Retry Exponential Backoff 지수적 대기 시간 증가
Jitter (TCP randomized RTO) Retry Jitter 동시 재시도 회피
ECN (Explicit Congestion Notification) Health Check / Probe 혼잡 상태 명시적 알림

13.5 Gray Failure 논문 (Microsoft Research, 2017)

“Gray Failure: The Achilles’ Heel of Cloud-Scale Systems” (Huang et al.)는 시스템이 완전히 정상도, 완전히 장애도 아닌 “회색 영역” 에 빠지는 현상을 분석했다. 이것은 차등 관찰 가능성(Differential Observability) 문제이다: 관찰자에 따라 시스템 상태가 다르게 보인다.

┌─────────────────────────────────────────────────────────────────┐
│          Gray Failure — Circuit Breaker의 맹점                    │
│                                                                 │
│  [Binary Failure — CB가 잘 감지]                                │
│                                                                 │
│  오류율  ┃                    ████████████                      │
│  100%   ┃                    ████████████                      │
│         ┃                    ████████████                      │
│   50%   ┃─ ─ ─ ─ ─ ─ ─ ─ ─ ████████████ ─ ─ ─ 임계값        │
│         ┃                    ████████████                      │
│    0%   ┃████████████████████            ████████████           │
│         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 시간     │
│         CB: CLOSED           OPEN        CLOSED                │
│         → 명확한 전이, CB가 정확히 동작                         │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [Gray Failure — CB가 감지 못함]                                │
│                                                                 │
│  오류율  ┃          (오류율은 낮음)                              │
│   50%   ┃─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ 임계값        │
│         ┃                                                      │
│   10%   ┃          ████████████████████████                    │
│    0%   ┃██████████                        ████████             │
│         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 시간     │
│                                                                 │
│  응답시간 ┃         ████                                        │
│  5000ms  ┃        █████                                        │
│  2000ms  ┃      ████████                                       │
│   200ms  ┃█████          ████████                               │
│          ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 시간    │
│                                                                 │
│  CB: CLOSED ──────────────────────────── (OPEN 전이 안 됨!)    │
│  → 오류율은 임계값 미만이지만, 응답시간이 10x 증가!            │
│  → 사용자 경험은 심각하게 저하됨                                │
│                                                                 │
│  해결: slowCallRateThreshold + slowCallDurationThreshold 설정   │
│        Multi-dimensional health check (오류율 + 지연 + p99)     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

CB의 Gray Failure 대응 전략:

전략 구현 효과
slowCallRateThreshold 느린 호출 비율 임계값 설정 지연 증가 감지
slowCallDurationThreshold 느린 호출 기준 시간 설정 지연의 정의
Multi-signal detection 오류율 + 지연 + p99 복합 판단 차등 관찰 극복
Custom health check 애플리케이션 수준 의미론적 검사 비즈니스 레벨 Gray Failure 감지

13.6 TLA+를 이용한 Circuit Breaker 형식 검증

TLA+ (Temporal Logic of Actions)를 사용하면 Circuit Breaker의 상태 전이가 Safety Property (나쁜 일이 발생하지 않음)와 Liveness Property (좋은 일이 결국 발생함)를 만족하는지 형식적으로 검증할 수 있다.

┌─────────────────────────────────────────────────────────────────┐
│          TLA+ Circuit Breaker 형식 명세                           │
│                                                                 │
│  ---- MODULE CircuitBreaker ----                                │
│  EXTENDS Naturals, Sequences                                    │
│                                                                 │
│  VARIABLES                                                      │
│    state,        \* {CLOSED, OPEN, HALF_OPEN}                  │
│    failureCount, \* 0..WindowSize                               │
│    successCount, \* 0..HalfOpenPermitted                        │
│    timer         \* 0..WaitDuration                             │
│                                                                 │
│  TypeInvariant ==                                               │
│    /\ state \in {"CLOSED", "OPEN", "HALF_OPEN"}                │
│    /\ failureCount \in 0..WindowSize                            │
│    /\ successCount >= 0                                         │
│    /\ timer >= 0                                                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  Actions:                                                       │
│                                                                 │
│  CallSucceeds ==                                                │
│    \/ (state = "CLOSED" /\ state' = "CLOSED"                   │
│        /\ failureCount' = Max(0, failureCount - 1))            │
│    \/ (state = "HALF_OPEN" /\ successCount' = successCount + 1 │
│        /\ IF successCount + 1 >= Threshold                     │
│           THEN state' = "CLOSED" /\ failureCount' = 0          │
│           ELSE state' = "HALF_OPEN")                            │
│                                                                 │
│  CallFails ==                                                   │
│    \/ (state = "CLOSED" /\ failureCount' = failureCount + 1    │
│        /\ IF failureCount + 1 >= FailureThreshold              │
│           THEN state' = "OPEN" /\ timer' = WaitDuration        │
│           ELSE state' = "CLOSED")                               │
│    \/ (state = "HALF_OPEN" /\ state' = "OPEN"                  │
│        /\ timer' = WaitDuration)                                │
│                                                                 │
│  TimerExpires ==                                                │
│    state = "OPEN" /\ timer = 0                                  │
│    /\ state' = "HALF_OPEN"                                      │
│    /\ successCount' = 0                                         │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  Safety Properties:                                             │
│  ├── NoDirectOpenToClosed:                                      │
│  │   □(state = "OPEN" ⇒ ○(state ≠ "CLOSED"))                  │
│  │   "OPEN에서 CLOSED로 직접 전이 불가 (반드시 HALF_OPEN 경유)" │
│  ├── BoundedFailure:                                            │
│  │   □(failureCount ≤ WindowSize)                               │
│  │   "실패 카운트는 Window 크기를 초과하지 않음"                │
│  └── NoCallInOpen:                                              │
│      □(state = "OPEN" ⇒ 요청 차단)                             │
│      "OPEN 상태에서는 호출이 통과되지 않음"                     │
│                                                                 │
│  Liveness Properties:                                           │
│  ├── EventualRecovery:                                          │
│  │   □(state = "OPEN" ⇒ ◇(state = "HALF_OPEN"))               │
│  │   "OPEN 상태는 결국 HALF_OPEN으로 전이됨"                   │
│  └── EventualClose:                                             │
│      □(state = "HALF_OPEN" ∧ 서비스 정상                       │
│        ⇒ ◇(state = "CLOSED"))                                  │
│      "서비스가 복구되면 결국 CLOSED로 돌아옴"                   │
│                                                                 │
│  ====                                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

14. 최신 Resilience 패턴 심화

14.1 Hedging Pattern

Google의 Jeff Dean과 Luiz André Barroso가 2013년 발표한 “The Tail at Scale” 논문은 대규모 Fan-out 시스템에서 Tail Latency가 극적으로 증폭되는 현상을 분석하고, Hedging 패턴을 해결책으로 제시했다.

Fan-out 확률론

┌─────────────────────────────────────────────────────────────────┐
│          Fan-out에서의 Tail Latency 증폭                         │
│                                                                 │
│  단일 서버에서 p99 지연 = 10ms일 때:                             │
│                                                                 │
│  Fan-out = 1:   P(최소 1개 느림) = 1%                           │
│  Fan-out = 10:  P(최소 1개 느림) = 1-(0.99)^10  ≈ 9.6%         │
│  Fan-out = 50:  P(최소 1개 느림) = 1-(0.99)^50  ≈ 39.5%        │
│  Fan-out = 100: P(최소 1개 느림) = 1-(0.99)^100 ≈ 63.4%        │
│                                                                 │
│  확률   ┃                                            ████      │
│  100%   ┃                                       █████████      │
│         ┃                                  ██████████████      │
│  63.4%  ┃─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─████████████████ ─      │
│         ┃                          █████                        │
│  39.5%  ┃─ ─ ─ ─ ─ ─ ─ ─ ─ ██████ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─      │
│         ┃                ████                                   │
│   9.6%  ┃─ ─ ─ ─ ─ ████ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─      │
│    1%   ┃████████                                               │
│         ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Fan-out  │
│              1   10       50                 100                │
│                                                                 │
│  핵심: 전체 응답시간 = max(모든 Fan-out 응답) = 가장 느린 것   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Google BigTable 벤치마크

Google의 실측 결과 (Dean & Barroso, 2013):

지표 Hedging 미적용 Hedging 적용 (95th %ile 후 재전송) 개선
p50 10ms 10ms 동일
p99 50ms 23ms 54% 개선
p99.9 1,800ms 74ms 96% 개선
추가 요청 비용 - ~2% 매우 낮음

Polly v8 Hedging 모드

┌─────────────────────────────────────────────────────────────────┐
│          Polly v8 Hedging 3가지 모드                             │
│                                                                 │
│  [1. Latency Hedging]                                           │
│  요청 ──► Primary ─── (P95 초과 시) ──► Hedge 1 ──► Hedge 2   │
│           │                              │             │        │
│           └──────── 가장 먼저 응답한 것 사용 ──────────┘        │
│  특징: 일정 시간 후 추가 요청 전송, 추가비용 최소              │
│                                                                 │
│  [2. Parallel Hedging]                                          │
│  요청 ──┬─► Primary ───────┐                                   │
│         ├─► Hedge 1 ───────┤─► 가장 먼저 응답한 것 사용        │
│         └─► Hedge 2 ───────┘                                   │
│  특징: 동시 전송, 비용 N배이지만 지연 최소화                   │
│                                                                 │
│  [3. Fallback Hedging]                                          │
│  요청 ──► Primary ─── (실패 시) ──► Fallback 1 ──► Fallback 2 │
│  특징: 순차적 대안 시도 (전통적 Fallback과 유사)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Retry vs Hedging 비교

비교 항목 Retry Hedging
트리거 요청 실패 후 지연 임계값 초과 시 (실패 아니어도)
동시 요청 순차적 (이전 실패 후) 병렬 (이전 요청 진행 중에도)
목표 일시적 오류 극복 Tail Latency 감소
응답 사용 마지막 성공 응답 가장 빠른 응답
추가 비용 실패 시에만 항상 소량 (2~5%)
적합 상황 간헐적 오류 Fan-out, Tail Latency 민감
부적합 상황 과부하 시 (Retry Storm) 멱등성 없는 쓰기 작업

14.2 Adaptive Concurrency Limits

Netflix가 2018년 공개한 concurrency-limits 라이브러리는 TCP Congestion Control의 아이디어를 직접 애플리케이션 레이어에 적용한다. Little’s Law를 런타임에 실시간 피드백 루프로 실현한 것이다.

┌─────────────────────────────────────────────────────────────────┐
│          Adaptive Concurrency Limits — 동적 용량 결정             │
│                                                                 │
│  [정적 Bulkhead 문제]                                           │
│                                                                 │
│  설정: maxConcurrentCalls = 50                                  │
│                                                                 │
│  비수기: 실제 필요 10 → 40 슬롯 낭비                           │
│  피크:  실제 필요 80 → 30 요청 불필요하게 거부                 │
│                                                                 │
│  [Adaptive Concurrency]                                         │
│                                                                 │
│  limit ──┬──► 측정: RTT (응답시간)                              │
│          │    ├── RTT_noload (최소 응답시간)                    │
│          │    └── RTT_actual (현재 응답시간)                    │
│          │                                                      │
│          ├──► 계산: new_limit = f(RTT_noload, RTT_actual)      │
│          │                                                      │
│          └──► 적용: Semaphore 크기 동적 조정                   │
│                                                                 │
│  비수기: limit 자동 감소 → 리소스 절약                         │
│  피크:  limit 자동 증가 → 처리량 극대화                        │
│  과부하: limit 급격히 감소 → 보호                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

알고리즘 비교

알고리즘 TCP 기반 동작 방식 장점 단점
VegasLimit TCP Vegas RTT 증가 감지 → limit 감소 큐잉 지연에 민감, 사전 감지 안정 RTT 필요
Gradient2Limit - gradient = RTT_noload / RTT_actual, limit × gradient 노이즈에 강함, Smoothing Cold Start 느림
AIMDLimit TCP Reno AIMD 성공 시 +α, 실패/지연 시 ×β (0.5~0.9) 단순하고 예측 가능 변동 큼, 최적 수렴 느림

Bulkhead 정적 vs 동적 비교

비교 항목 정적 Bulkhead Adaptive Concurrency Limits
설정 수동 (maxConcurrentCalls 고정) 자동 (RTT 기반 조정)
변동 대응 없음 (고정 값) 실시간 적응
과잉 프로비저닝 높음 (피크 기준 설정) 낮음 (실시간 조정)
설정 난이도 낮음 (숫자 하나) 중간 (알고리즘 선택 필요)
예측 가능성 높음 (항상 동일) 중간 (동적 변동)
적합 상황 트래픽 패턴 안정적 트래픽 변동 큰 서비스
라이브러리 Resilience4j Bulkhead Netflix concurrency-limits

14.3 Load Shedding

Load Shedding은 서버가 자체 용량을 초과하는 요청을 의도적으로 거부하여 Goodput(유용한 처리량)을 유지하는 패턴이다. Circuit Breaker와의 핵심 차이: CB는 클라이언트가 장애 서비스를 보호하지만, Load Shedding은 서버 자신이 자신을 보호한다.

┌─────────────────────────────────────────────────────────────────┐
│          Load Shedding vs Circuit Breaker                        │
│                                                                 │
│  [Circuit Breaker — 클라이언트 보호]                             │
│                                                                 │
│  Client ──► CB ──✕──► Overloaded Server                        │
│              │                                                  │
│              └── "서버가 아프니까 내가 안 보낸다"                │
│                                                                 │
│  [Load Shedding — 서버 자기 보호]                               │
│                                                                 │
│  Client ──► Server ──► Load Shedder ──✕──► Processing          │
│                         │                                       │
│                         └── "내가 감당 못하니 거부한다"          │
│                                                                 │
│  [둘의 조합 — Defense in Depth]                                  │
│                                                                 │
│  Client ──► CB ──► Server ──► Load Shedder ──► Processing      │
│              │                  │                                │
│              │                  └── 서버 자체 보호 (1차 방어)    │
│              └── 클라이언트가 실패 인지 (2차 방어)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Priority-based Load Shedding (Netflix 우선순위 계층)

┌─────────────────────────────────────────────────────────────────┐
│          Netflix Priority-based Load Shedding                    │
│                                                                 │
│  우선순위 계층 (높음 → 낮음):                                    │
│                                                                 │
│  ┌─────────────────────────────────┐                            │
│  │ P0: CRITICAL                    │ 결제, 인증, 핵심 스트리밍  │
│  │ → 절대 거부하지 않음            │                            │
│  ├─────────────────────────────────┤                            │
│  │ P1: DEGRADED                    │ 추천, 개인화, 검색         │
│  │ → 과부하 시 품질 저하 허용      │                            │
│  ├─────────────────────────────────┤                            │
│  │ P2: BEST_EFFORT                 │ A/B 테스트, 분석, 로깅     │
│  │ → 과부하 시 먼저 거부           │                            │
│  ├─────────────────────────────────┤                            │
│  │ P3: BULK                        │ 배치, 프리페치, 웜업       │
│  │ → 부하 징후 시 즉시 거부        │                            │
│  └─────────────────────────────────┘                            │
│                                                                 │
│  과부하 시 동작:                                                │
│  1단계: P3 (BULK) 전체 거부                                     │
│  2단계: P2 (BEST_EFFORT) 전체 거부                              │
│  3단계: P1 (DEGRADED) 품질 저하                                 │
│  4단계: P0 (CRITICAL)만 처리 — 최후의 보루                      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Goodput vs Throughput

지표 정의 과부하 시
Throughput 단위 시간당 처리한 총 요청 수 증가 후 급감 (리소스 경쟁)
Goodput 단위 시간당 성공적으로 완료된 유용한 요청 수 Load Shedding으로 유지 가능

Load Shedding 없이 과부하가 발생하면, 모든 요청이 느려지며 Timeout으로 실패하여 Goodput이 0에 수렴할 수 있다.

Google Borg → K8s PriorityClass 계승

Google 내부 Borg 시스템의 우선순위 개념은 Kubernetes PriorityClass로 계승되었다:

Borg Priority K8s PriorityClass 설명
Free (0) system-node-critical 외 기본 리소스 부족 시 가장 먼저 Eviction
Best-Effort (1) BestEffort QoS 보장 리소스 없음
Production (9) Burstable/Guaranteed QoS 프로덕션 워크로드
Monitoring (10) system-cluster-critical 모니터링/로깅
Infrastructure (11) system-node-critical 인프라 필수 구성요소

15. 실제 대규모 장애 사례 연구

15.1 Amazon DynamoDB (2015) — Retry Storm

┌─────────────────────────────────────────────────────────────────┐
│          Amazon DynamoDB Retry Storm (2015)                      │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  DynamoDB의 metadata service에 일시적 지연 발생                 │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] Metadata Service 지연 발생                                 │
│       │                                                         │
│  [2] 수천 개의 Storage Node가 동시 Retry                        │
│       │                                                         │
│  [3] Retry 트래픽이 Metadata Service를 완전 마비시킴            │
│       │  ┌───────────────────────────────────┐                  │
│       │  │  정상 트래픽:    ████ (100 req/s) │                  │
│       │  │  + Retry 트래픽: ████████████████  │                  │
│       │  │  = 총 트래픽:    ████████████████████ (10,000 req/s) │
│       │  │  → Metadata Service 용량 초과!    │                  │
│       │  └───────────────────────────────────┘                  │
│       │                                                         │
│  [4] Metadata 불가 → 신규 테이블 생성/수정 전부 불가            │
│       │                                                         │
│  [5] 복구 시도도 Metadata Service 필요 → 복구 자체가 불가       │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Exponential Backoff + Jitter (Retry Storm 방지)            │
│  ├── Circuit Breaker (과도한 Retry 차단)                        │
│  └── Load Shedding (Metadata Service 자기 보호)                 │
│                                                                 │
│  교훈:                                                          │
│  ├── Retry는 반드시 Exponential Backoff + Jitter 필수           │
│  ├── "모든 클라이언트가 동시에 Retry" = Thundering Herd         │
│  └── 복구 경로도 장애 대상과 같은 의존성이면 복구 불가          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.2 Cloudflare (2019) — WAF 정규식 Catastrophic Backtracking

┌─────────────────────────────────────────────────────────────────┐
│          Cloudflare WAF Outage (2019-07-02)                      │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  WAF(Web Application Firewall) 정규식 규칙 업데이트 배포        │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 정규식에 catastrophic backtracking 패턴 포함               │
│      (?:(?:\"|'|\]|\}|\\|\d|(?:nan|infinity|true|false|...     │
│       │                                                         │
│  [2] 모든 Edge 서버 CPU 100% 고정                              │
│      ┌─────────────────────────────┐                            │
│      │ CPU ████████████████████ 100% │                          │
│      │ 하나의 HTTP 요청 처리에      │                           │
│      │ 정규식 엔진이 무한 루프 진입 │                           │
│      └─────────────────────────────┘                            │
│       │                                                         │
│  [3] 전세계 Cloudflare Edge 82%에서 트래픽 처리 불가            │
│       │                                                         │
│  [4] 27분간 전세계 고객 사이트 접근 불가                        │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Timeout (정규식 실행 시간 제한)                            │
│  ├── Bulkhead (WAF 처리를 메인 프록시에서 격리)                │
│  ├── Canary Deploy (점진적 배포로 영향 범위 최소화)            │
│  └── Circuit Breaker (CPU 임계값 초과 시 WAF 우회)             │
│                                                                 │
│  교훈:                                                          │
│  ├── 정규식은 O(2^n) 될 수 있음 — 반드시 실행 시간 제한        │
│  ├── 글로벌 동시 배포 = 최대 Blast Radius                      │
│  └── 안전장치(CPU 제한, WAF bypass) 없이 배포하면 안 됨        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.3 Google Cloud (2019) — Network Config 오류

┌─────────────────────────────────────────────────────────────────┐
│          Google Cloud Networking Incident (2019-06-02)            │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  네트워크 구성 변경 중 오류로 대규모 네트워크 혼잡 발생         │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 네트워크 구성 변경이 의도치 않게 광범위하게 적용           │
│       │                                                         │
│  [2] 대역폭 급감 → 네트워크 혼잡 (Congestion)                  │
│       │                                                         │
│  [3] 제어 플레인(Control Plane)도 같은 네트워크 사용            │
│      ┌─────────────────────────────────────┐                    │
│      │  Data Plane:    ████ (혼잡!)        │                    │
│      │  Control Plane: ████ (같이 혼잡!)   │                    │
│      │  → 수정 명령도 전달 불가!           │                    │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [4] 복구 도구도 같은 네트워크 의존 → 복구 도구 마비           │
│       │                                                         │
│  [5] 4시간+ 장애, GCE/GKE/Cloud SQL 등 다수 서비스 영향        │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Bulkhead (Control Plane / Data Plane 네트워크 분리)       │
│  ├── Out-of-band 관리 채널 (독립적 복구 경로)                  │
│  └── Blast Radius 제한 (점진적 구성 변경)                      │
│                                                                 │
│  교훈:                                                          │
│  ├── 제어 플레인과 데이터 플레인은 반드시 격리                  │
│  ├── 복구 도구가 장애 대상에 의존하면 복구 불가                │
│  └── 구성 변경은 Canary → 점진적 확대 필수                     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.4 Facebook/Meta (2021) — BGP 자가철회

┌─────────────────────────────────────────────────────────────────┐
│          Facebook/Meta 대규모 장애 (2021-10-04)                  │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  유지보수 중 BGP 경로 광고(advertisement) 철회 명령 오발행      │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] BGP 설정 변경으로 모든 Facebook IP 대역 광고 철회          │
│       │                                                         │
│  [2] 전세계 ISP 라우팅 테이블에서 Facebook 경로 소멸            │
│      ┌─────────────────────────────────────┐                    │
│      │  ISP 라우팅 테이블:                 │                    │
│      │  facebook.com → 157.240.0.0/16      │                    │
│      │  (BGP withdraw) → 경로 없음!        │                    │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [3] DNS 서버도 Facebook IP 대역 → DNS 도달 불가               │
│       │                                                         │
│  [4] 내부 도구도 같은 인프라 의존 → 원격 복구 불가             │
│       │                                                         │
│  [5] 물리적으로 데이터센터 접근하여 수동 복구                   │
│       │                                                         │
│  [6] 6시간 완전 중단 (Facebook, Instagram, WhatsApp 전체)       │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Out-of-band 관리 (독립 네트워크 경로)                     │
│  ├── Blast Radius 제한 (점진적 BGP 변경)                       │
│  ├── 자동 롤백 (이상 감지 시 즉시 복원)                        │
│  └── Bulkhead (DNS/관리도구를 별도 네트워크로 격리)            │
│                                                                 │
│  교훈:                                                          │
│  ├── 네트워크 인프라 변경은 최고 수준의 안전장치 필요           │
│  ├── "자기 자신을 접근하는 데 자기 자신이 필요" = SPOF          │
│  └── 물리적 접근 복구 계획(Break Glass) 반드시 준비            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.5 AWS US-EAST-1 (2021) — Latent Backoff 버그

┌─────────────────────────────────────────────────────────────────┐
│          AWS US-EAST-1 장애 (2021-12-07)                         │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  내부 네트워크 자동화 시스템의 트래픽 증가가 연쇄 장애 유발     │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 내부 네트워크 디바이스에 과도한 연결 요청                  │
│       │                                                         │
│  [2] 네트워크 디바이스 과부하 → 지연 증가                       │
│       │                                                         │
│  [3] 클라이언트의 Retry 로직에 Backoff 버그 존재               │
│      ┌─────────────────────────────────────┐                    │
│      │  정상 Backoff: 1s → 2s → 4s → 8s   │                    │
│      │  버그 Backoff: 1s → 1s → 1s → 1s   │ ← Latent Bug!     │
│      │  → Backoff이 실제로 동작하지 않음   │                    │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [4] Retry Storm 자가강화                                       │
│      부하 ↑ → 실패 ↑ → Retry ↑ → 부하 ↑↑ (양의 피드백)        │
│       │                                                         │
│  [5] Lambda, API Gateway, CloudWatch 등 다수 서비스 영향        │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Exponential Backoff + Jitter (버그 없는 구현 필수)         │
│  ├── Circuit Breaker (무한 Retry 차단)                          │
│  ├── Load Shedding (네트워크 디바이스 자기 보호)               │
│  └── Chaos Engineering (Latent Bug 사전 발견)                   │
│                                                                 │
│  교훈:                                                          │
│  ├── Backoff 로직은 반드시 테스트 (Latent Bug 주의!)            │
│  ├── Retry는 "일반 경로"에서만 동작 확인하면 부족              │
│  └── 장애 시에만 활성화되는 코드 경로 = 가장 위험             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.6 GitHub (2018) — 43초 네트워크 파티션

┌─────────────────────────────────────────────────────────────────┐
│          GitHub MySQL Cluster Incident (2018-10-21)              │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  US East Coast 데이터센터 간 43초 네트워크 파티션 발생           │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 43초간 네트워크 파티션 (물리적 연결 순단)                  │
│       │                                                         │
│  [2] MySQL Orchestrator가 자동 Failover 실행                    │
│      ┌─────────────────────────────────────┐                    │
│      │  Primary (DC1) ──✕── Replica (DC2)  │                    │
│      │                                      │                   │
│      │  Orchestrator: "Primary 죽었다!"     │                   │
│      │  → Replica를 새 Primary로 승격       │                   │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [3] 네트워크 복구 → 양쪽 DC 모두 Primary (Split Brain!)       │
│      ┌──────────────┐    ┌──────────────┐                      │
│      │ DC1: Primary  │    │ DC2: Primary  │                     │
│      │ (원래 Primary)│    │ (승격된 것)   │                     │
│      │ 쓰기 진행 중  │    │ 쓰기 진행 중  │                     │
│      └──────────────┘    └──────────────┘                      │
│       │                                                         │
│  [4] 데이터 불일치 → 24시간+ 데이터 정합성 복구 작업            │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Fencing Token (Split Brain 방지)                           │
│  ├── Bulkhead (DC간 격리와 일관성 전략)                        │
│  ├── Consensus (Raft/Paxos 기반 리더 선출)                     │
│  └── Manual Failover (자동 Failover 위험 인지)                 │
│                                                                 │
│  교훈:                                                          │
│  ├── 43초면 충분히 Split Brain 발생 가능                        │
│  ├── 자동 Failover는 양날의 검 (빠르지만 위험)                │
│  └── 데이터 일관성 복구는 서비스 복구보다 훨씬 오래 걸림      │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.7 Slack (2021) — Transit Gateway 포화

┌─────────────────────────────────────────────────────────────────┐
│          Slack Transit Gateway Outage (2021-01-04)               │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  새해 첫 출근일, 급격한 트래픽 증가로 Transit Gateway 포화      │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 새해 첫 출근 → 동시 접속 급증 (예상의 2~3배)              │
│       │                                                         │
│  [2] AWS Transit Gateway 대역폭 한계 도달                       │
│       │                                                         │
│  [3] Autoscaler가 더 많은 인스턴스 추가                         │
│      ┌─────────────────────────────────────┐                    │
│      │  Autoscaler: "트래픽 많다 → 스케일업!"│                  │
│      │  → 새 인스턴스가 Transit Gateway에    │                  │
│      │    추가 연결 생성 → 대역폭 더 소모!  │                  │
│      │  → 역효과: 상황 악화!                │                  │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [4] Transit Gateway 완전 포화 → 모니터링 트래픽도 차단        │
│       │                                                         │
│  [5] 모니터링 블라인드 → 장애 원인 파악 지연                    │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Rate Limiter (Transit Gateway 보호)                        │
│  ├── Bulkhead (관리/모니터링 트래픽 대역폭 예약)               │
│  ├── Load Shedding (Autoscaler 과잉 반응 억제)                 │
│  └── Capacity Planning (이벤트 기반 사전 스케일링)             │
│                                                                 │
│  교훈:                                                          │
│  ├── Autoscaler가 장애를 악화시킬 수 있음 (양의 피드백)        │
│  ├── 모니터링 경로는 데이터 경로와 반드시 분리                  │
│  └── 네트워크 대역폭도 Bulkhead 대상 (예약 필수)              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.8 카카오 (2022) — 판교 DC 화재

┌─────────────────────────────────────────────────────────────────┐
│          카카오 판교 DC 화재 (2022-10-15)                        │
│                                                                 │
│  무엇이 발생했는가:                                             │
│  SK C&C 판교 데이터센터 지하 전기실 화재                        │
│                                                                 │
│  연쇄 장애 메커니즘:                                            │
│                                                                 │
│  [1] 판교 DC 지하 전기실 리튬 배터리 화재                       │
│       │                                                         │
│  [2] 화재 진압을 위해 전체 DC 전원 차단                         │
│       │                                                         │
│  [3] Failover 시스템도 같은 DC에 위치                           │
│      ┌─────────────────────────────────────┐                    │
│      │  판교 DC:                            │                   │
│      │  ├── 카카오톡 서버       (화재!)     │                   │
│      │  ├── 카카오맵 서버       (화재!)     │                   │
│      │  ├── 카카오페이 서버     (화재!)     │                   │
│      │  ├── Failover 시스템     (화재!)     │ ← SPOF!          │
│      │  └── DR 제어 시스템      (화재!)     │ ← SPOF!          │
│      └─────────────────────────────────────┘                    │
│       │                                                         │
│  [4] DR(Disaster Recovery) 제어 시스템도 마비 → 자동 절체 불가  │
│       │                                                         │
│  [5] 5일간 부분/전체 장애 (카카오톡, 카카오맵, 카카오페이 등)  │
│                                                                 │
│  관련 Resilience 패턴:                                          │
│  ├── Multi-Region (지리적으로 분산된 Active-Active)             │
│  ├── Bulkhead (DC 수준 격리)                                   │
│  ├── Failover 독립성 (Failover 시스템은 별도 위치)             │
│  └── Out-of-band 관리 (독립적 DR 제어 채널)                    │
│                                                                 │
│  교훈:                                                          │
│  ├── "Failover 시스템 자체가 SPOF" = 가장 치명적인 설계 결함   │
│  ├── 단일 DC에 모든 서비스 집중 = 물리적 Bulkhead 부재         │
│  └── DR 훈련(GameDay)을 정기적으로 실시해야 실제 작동 확인 가능│
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

15.9 장애 사례 교차 분석

사례 근본 원인 유형 Retry Storm 복구도구 마비 Blast Radius 장애 시간
DynamoDB 2015 내부 Retry 폭주 ★★★ ★★☆ 리전 내 수 시간
Cloudflare 2019 정규식 CPU 폭발 - ★☆☆ 전세계 82% 27분
Google Cloud 2019 네트워크 구성 오류 ★☆☆ ★★★ 리전 4시간+
Facebook 2021 BGP 구성 오류 - ★★★ 전세계 100% 6시간
AWS 2021 Latent Backoff 버그 ★★★ ★★☆ US-EAST-1 수 시간
GitHub 2018 네트워크 파티션 - ★☆☆ 글로벌 (데이터) 24시간+
Slack 2021 Transit GW 포화 - ★★☆ 전체 서비스 수 시간
카카오 2022 물리적 DC 장애 - ★★★ 전체 서비스 5일

교차 분석에서 도출된 핵심 패턴:

반복되는 패턴 발생 사례 방지 전략
복구 도구가 장애 대상에 의존 Google, Facebook, 카카오 Out-of-band 관리, 독립 복구 경로
Retry Storm / 양의 피드백 DynamoDB, AWS Exponential Backoff + Jitter + CB
글로벌 동시 배포 Cloudflare Canary → 점진적 확대
자동화의 역효과 Slack (Autoscaler), GitHub (Orchestrator) 안전장치 내장, Manual override
단일 DC = SPOF 카카오 Multi-Region Active-Active

16. Chaos Engineering 도구 생태계

16.1 Chaos Engineering의 진화

┌─────────────────────────────────────────────────────────────────┐
│          Chaos Engineering 진화 타임라인                          │
│                                                                 │
│  2010   Netflix Chaos Monkey 내부 개발                          │
│         │  "랜덤하게 프로덕션 인스턴스를 종료"                  │
│         │                                                       │
│  2011   Chaos Monkey 오픈소스 공개                              │
│         │                                                       │
│  2012   Simian Army 확장                                        │
│         │  ├── Chaos Monkey: 인스턴스 종료                     │
│         │  ├── Latency Monkey: 네트워크 지연 주입              │
│         │  ├── Chaos Gorilla: 전체 AZ 장애 시뮬레이션          │
│         │  ├── Chaos Kong: 전체 Region 장애 시뮬레이션         │
│         │  ├── Janitor Monkey: 미사용 리소스 정리              │
│         │  └── Conformity Monkey: 베스트 프랙티스 준수 검사    │
│         │                                                       │
│  2014   Failure Injection Testing (FIT) — Netflix               │
│         │  → 세밀한 장애 주입, Blast Radius 제어               │
│         │                                                       │
│  2015   Jesse Robbins, "GameDay" 개념 대중화                    │
│         │                                                       │
│  2017   "Chaos Engineering" 서적 출간 (Casey Rosenthal 외)      │
│         │  principlesofchaos.org 공식 원칙 발표                 │
│         │                                                       │
│  2018   Gremlin 상용 서비스 출시                                │
│         │  LitmusChaos 프로젝트 시작                            │
│         │                                                       │
│  2019   Chaos Mesh 공개 (PingCAP/CNCF)                          │
│         │                                                       │
│  2020   AWS Fault Injection Simulator (FIS) 발표                │
│         │  LitmusChaos → CNCF Sandbox                           │
│         │                                                       │
│  2021   Chaos Mesh → CNCF Incubating                            │
│         │  Azure Chaos Studio 발표                               │
│         │                                                       │
│  현재   Chaos Engineering이 SRE 표준 프랙티스로 정착            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

16.2 Chaos Engineering 5대 원칙

(출처: principlesofchaos.org)

┌─────────────────────────────────────────────────────────────────┐
│          Chaos Engineering 5대 원칙                               │
│                                                                 │
│  [1] Steady State 가설 수립                                     │
│      "정상 상태"를 정량적으로 정의 (예: p99 < 200ms, 오류 < 1%)│
│      → 실험 전후로 이 지표가 유지되는지 확인                    │
│                                                                 │
│  [2] 실세계 이벤트를 반영                                       │
│      서버 종료, 네트워크 파티션, 디스크 꽉 참, 시계 드리프트    │
│      → "실제로 발생 가능한" 장애를 주입                         │
│                                                                 │
│  [3] 프로덕션에서 실험                                          │
│      스테이징 환경은 프로덕션과 다름 — 가능하면 프로덕션에서    │
│      → Blast Radius를 제한하여 안전하게                         │
│                                                                 │
│  [4] 자동화하여 지속적으로 실행                                 │
│      수동 실험은 확장 불가 — CI/CD 파이프라인에 통합            │
│      → 회귀 방지: 어제 견뎠던 장애를 오늘도 견디는지 확인      │
│                                                                 │
│  [5] Blast Radius 최소화                                        │
│      실험이 실제 장애가 되지 않도록 범위 제한                   │
│      → 소수 사용자/트래픽부터 시작, 이상 감지 시 즉시 중단     │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

16.3 Steady-State Hypothesis

┌─────────────────────────────────────────────────────────────────┐
│          Steady-State Hypothesis 예시                             │
│                                                                 │
│  가설: "Payment Service 인스턴스 1개가 종료되어도              │
│         p99 응답시간 < 500ms, 오류율 < 0.1% 유지"               │
│                                                                 │
│  실험 흐름:                                                     │
│                                                                 │
│  [1] Steady State 확인                                          │
│      ├── p99 응답시간: 180ms ✅ (< 500ms)                      │
│      ├── 오류율: 0.02% ✅ (< 0.1%)                             │
│      └── TPS: 500 req/s ✅ (안정)                              │
│                                                                 │
│  [2] 장애 주입                                                  │
│      └── Payment Service Pod 1개 강제 종료 (kubectl delete pod) │
│                                                                 │
│  [3] Steady State 재확인 (2분 후)                               │
│      ├── p99 응답시간: 220ms ✅ (< 500ms, 약간 증가)          │
│      ├── 오류율: 0.05% ✅ (< 0.1%, 약간 증가)                 │
│      └── TPS: 495 req/s ✅ (거의 유지)                         │
│                                                                 │
│  [4] 판정                                                       │
│      └── ✅ 가설 성립 — 시스템이 단일 인스턴스 장애에 복원됨    │
│                                                                 │
│  만약 p99 > 500ms 또는 오류율 > 0.1% 이면:                     │
│  └── ✗ 가설 기각 — Resilience 개선 필요 (CB, Retry, HPA 등)   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

16.4 도구 비교

비교 항목 Chaos Mesh LitmusChaos Gremlin AWS FIS
유형 오픈소스 (CNCF Incubating) 오픈소스 (CNCF Incubating) 상용 (SaaS) 클라우드 서비스 (AWS)
플랫폼 Kubernetes 전용 Kubernetes + VM K8s, VM, 컨테이너, 서버리스 AWS 서비스 전체
장애 유형 Pod, Network, IO, Stress, Time, DNS, JVM, HTTP Pod, Node, Network, IO, Stress, 커스텀 프로세스, 네트워크, 리소스, 상태, 커스텀 EC2, ECS, EKS, RDS, Network, S3
UI/Dashboard 웹 대시보드 웹 포탈 (ChaosCenter) 웹 콘솔 AWS 콘솔
자동 중단 있음 (Duration 기반) 있음 (Probe 기반) 있음 (Halt 조건) 있음 (Stop 조건)
CI/CD 통합 GitHub Actions, Argo GitHub Actions, GitLab CI API 기반 CloudFormation, API
비용 무료 무료 유료 (팀당 과금) 사용량 기반 과금
설치 난이도 낮음 (Helm) 중간 (Operator) 낮음 (에이전트) 없음 (관리형)
성숙도 높음 중간 높음 높음
적합 환경 K8s 네이티브 K8s + 하이브리드 멀티 환경 AWS 전용

16.5 GameDay 운영 방법론

┌─────────────────────────────────────────────────────────────────┐
│          GameDay 운영 6단계                                      │
│                                                                 │
│  [1단계] 계획 (1~2주 전)                                        │
│  ├── 실험 목표 정의 (Steady-State Hypothesis)                  │
│  ├── 범위 결정 (어떤 서비스, 어떤 장애)                        │
│  ├── Blast Radius 제한 (영향 받는 사용자 비율)                 │
│  ├── 롤백 계획 수립                                            │
│  └── 참여 팀 조율 (SRE, 개발팀, 경영진 승인)                  │
│                                                                 │
│  [2단계] 준비 (실험 당일 전)                                    │
│  ├── 모니터링 대시보드 준비 (Grafana, PagerDuty)               │
│  ├── 장애 주입 도구 설정 (Chaos Mesh, Gremlin 등)              │
│  ├── 커뮤니케이션 채널 준비 (전용 Slack 채널)                  │
│  └── 참여자 역할 배정 (진행자, 관찰자, 기록자)                │
│                                                                 │
│  [3단계] Steady State 확인 (실험 직전)                          │
│  ├── 기준 메트릭 기록 (TPS, p99, 오류율, CPU, 메모리)          │
│  ├── 알람 상태 확인 (기존 알람 없는지)                         │
│  └── "시작합니다" 공지                                         │
│                                                                 │
│  [4단계] 실험 실행                                              │
│  ├── 장애 주입 실행                                            │
│  ├── 실시간 모니터링 (대시보드 관찰)                           │
│  ├── 이상 감지 시 즉시 중단 가능 상태 유지                     │
│  └── 타임라인 기록 (t+0, t+30s, t+60s... 상태 변화)           │
│                                                                 │
│  [5단계] 관찰 및 복구                                           │
│  ├── 장애 주입 종료                                            │
│  ├── 시스템 자동 복구 관찰 (복구 시간 측정)                    │
│  ├── Steady State 재확인                                       │
│  └── 이상 있으면 수동 복구                                     │
│                                                                 │
│  [6단계] 분석 및 개선 (실험 후 1~2일)                           │
│  ├── 결과 문서화 (가설 성립/기각)                              │
│  ├── 발견된 약점 분류 (Critical/High/Medium/Low)               │
│  ├── 개선 Action Item 생성 (JIRA 티켓)                         │
│  └── 다음 GameDay 계획 (개선 후 재검증)                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17. Cloud-Native Resilience 통합

17.1 Kubernetes Probes + Circuit Breaker 상호작용

┌─────────────────────────────────────────────────────────────────┐
│          K8s Probes + CB 상호작용 주의사항                        │
│                                                                 │
│  [문제: registerHealthIndicator=true의 위험]                    │
│                                                                 │
│  Spring Boot Actuator Health:                                   │
│  ├── /actuator/health ← K8s Liveness Probe 연결               │
│  ├── CircuitBreaker Health Indicator 등록됨                     │
│  └── CB OPEN → Health Status DOWN                               │
│                                                                 │
│  연쇄 문제:                                                     │
│  CB OPEN                                                        │
│  → /actuator/health = DOWN                                      │
│  → K8s Liveness Probe 실패                                     │
│  → K8s가 Pod 재시작 (!)                                        │
│  → 재시작된 Pod도 같은 서비스 호출 → CB OPEN → 재시작 반복     │
│  → Restart Loop!                                                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [권장 설정]                                                     │
│                                                                 │
│  # application.yml                                              │
│  resilience4j:                                                  │
│    circuitbreaker:                                              │
│      instances:                                                 │
│        paymentService:                                          │
│          registerHealthIndicator: false  # ← 핵심!             │
│                                                                 │
│  이유: CB OPEN은 "이 Pod가 아프다"가 아니라                     │
│        "downstream 서비스가 아프다"이다.                        │
│        Pod를 재시작해도 downstream은 안 고쳐진다!              │
│                                                                 │
│  대안: Readiness Probe에만 연결하거나,                          │
│        커스텀 Health Indicator로 로컬 상태만 반영              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.2 PodDisruptionBudget + Bulkhead 계층 관계

┌─────────────────────────────────────────────────────────────────┐
│          PDB + Bulkhead 계층적 보호                               │
│                                                                 │
│  [애플리케이션 레벨 — Bulkhead]                                  │
│  ┌──────────────────────────────────────┐                       │
│  │ Pod 내부:                            │                       │
│  │ ├── Semaphore Bulkhead (서비스별)   │                       │
│  │ │   ├── paymentPool: max=50         │                       │
│  │ │   └── inventoryPool: max=30       │                       │
│  │ └── 기능별 리소스 격리               │                       │
│  └──────────────────────────────────────┘                       │
│                                                                 │
│  [K8s 레벨 — PodDisruptionBudget]                               │
│  ┌──────────────────────────────────────┐                       │
│  │ Deployment 수준:                     │                       │
│  │ ├── minAvailable: 2                  │ (또는 maxUnavailable) │
│  │ ├── Node drain 시에도 최소 2 Pod 유지│                       │
│  │ └── Rolling Update 시 가용성 보장    │                       │
│  └──────────────────────────────────────┘                       │
│                                                                 │
│  [인프라 레벨 — Node/AZ 분산]                                   │
│  ┌──────────────────────────────────────┐                       │
│  │ 클러스터 수준:                       │                       │
│  │ ├── Pod Topology Spread Constraints  │                       │
│  │ │   → AZ별 균등 배치                 │                       │
│  │ ├── Node Affinity / Anti-Affinity    │                       │
│  │ │   → 같은 서비스 Pod 분산           │                       │
│  │ └── AZ 하나 장애 → 다른 AZ에서 처리 │                       │
│  └──────────────────────────────────────┘                       │
│                                                                 │
│  계층 관계:                                                     │
│  인프라 Bulkhead (AZ 분산)                                      │
│    └── K8s Bulkhead (PDB, Pod 수 보장)                          │
│         └── App Bulkhead (Semaphore, 기능별 격리)               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.3 HPA + Rate Limiter 보완 관계

상황 HPA (Horizontal Pod Autoscaler) Rate Limiter 보완 효과
트래픽 점진적 증가 Pod 추가 (스케일 아웃) 불필요 (HPA가 처리) HPA 단독 충분
트래픽 급증 (spike) 스케일업 지연 (수분) 즉시 초과 요청 거부 Rate Limiter가 HPA 스케일업 시간 벌어줌
악의적 트래픽 (DDoS) 무한 스케일업 (비용 폭발) 요청 수 제한 Rate Limiter가 비용 보호
과부하 상태 Pod 추가해도 DB 병목 서비스 보호 Rate Limiter가 DB 보호
┌─────────────────────────────────────────────────────────────────┐
│          HPA + Rate Limiter 협력                                 │
│                                                                 │
│  트래픽 ────► Rate Limiter ────► Service ────► HPA 감시        │
│               │                    │              │             │
│               │ 초과 거부          │ 처리          │ CPU > 70%  │
│               │ (즉시 보호)        │              ▼             │
│               │                    │         Pod 추가           │
│               │                    │         (1~3분 소요)       │
│               │                    │              │             │
│               │                    ◄──────────────┘             │
│               │                    더 많은 Pod으로              │
│               │                    처리 가능 용량 ↑             │
│               │                                                │
│               └── Rate Limit을 동적으로 조정 가능              │
│                   (Pod 수 증가 → limit 상향)                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.4 Pod Topology Spread = 인프라 레벨 Bulkhead

┌─────────────────────────────────────────────────────────────────┐
│          Pod Topology Spread Constraints                         │
│                                                                 │
│  # K8s Deployment YAML                                          │
│  topologySpreadConstraints:                                     │
│  - maxSkew: 1                                                   │
│    topologyKey: topology.kubernetes.io/zone                     │
│    whenUnsatisfiable: DoNotSchedule                             │
│    labelSelector:                                               │
│      matchLabels:                                               │
│        app: payment-service                                     │
│                                                                 │
│  결과:                                                          │
│  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐           │
│  │   AZ-a       │ │   AZ-b       │ │   AZ-c       │           │
│  │ payment (2)  │ │ payment (2)  │ │ payment (2)  │           │
│  │ order   (1)  │ │ order   (1)  │ │ order   (1)  │           │
│  └──────────────┘ └──────────────┘ └──────────────┘           │
│   AZ-a 장애 시:    AZ-b (정상)      AZ-c (정상)               │
│   → 전체 6 Pod 중 4 Pod 생존 = 67% 용량 유지                  │
│                                                                 │
│  이것이 인프라 레벨 Bulkhead:                                   │
│  "물리적 격벽으로 장애 영향 범위를 AZ 단위로 격리"             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.5 OpenTelemetry 연동

Micrometer → OTel Bridge

┌─────────────────────────────────────────────────────────────────┐
│          Resilience4j → OpenTelemetry 연동                       │
│                                                                 │
│  [Metrics]                                                      │
│  Resilience4j ──► Micrometer ──► OTel Metrics Bridge ──► OTLP  │
│                                                                 │
│  # application.yml                                              │
│  management:                                                    │
│    otlp:                                                        │
│      metrics:                                                   │
│        export:                                                  │
│          url: http://otel-collector:4318/v1/metrics             │
│                                                                 │
│  주요 메트릭:                                                   │
│  ├── resilience4j_circuitbreaker_state (0/1/2)                 │
│  ├── resilience4j_circuitbreaker_failure_rate                  │
│  ├── resilience4j_circuitbreaker_calls_seconds (histogram)     │
│  └── resilience4j_circuitbreaker_not_permitted_calls_total     │
│                                                                 │
│  [Traces — CB 상태 변화를 Span Event로]                         │
│                                                                 │
│  // Kotlin/Java 코드                                            │
│  circuitBreaker.eventPublisher                                  │
│      .onStateTransition { event ->                              │
│          val span = Span.current()                              │
│          span.addEvent("circuit-breaker-state-change",          │
│              Attributes.of(                                     │
│                  stringKey("cb.name"), event.circuitBreakerName,│
│                  stringKey("cb.from"), event.stateTransition    │
│                      .fromState.name,                           │
│                  stringKey("cb.to"), event.stateTransition      │
│                      .toState.name                              │
│              ))                                                 │
│      }                                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Metrics-Traces-Logs 삼각 연동

┌─────────────────────────────────────────────────────────────────┐
│          Observability 삼각 연동                                  │
│                                                                 │
│              ┌───────────┐                                      │
│              │  Metrics   │                                      │
│              │ (Prometheus│                                      │
│              │  /Grafana) │                                      │
│              └─────┬─────┘                                      │
│                    │ Exemplar (traceId 포함)                    │
│                    │                                            │
│  ┌─────────┐      │      ┌─────────┐                           │
│  │  Logs    │◄─────┼─────►│ Traces  │                           │
│  │ (Loki/  │  traceId    │ (Tempo/ │                           │
│  │  ELK)   │  연결       │  Jaeger)│                           │
│  └─────────┘             └─────────┘                           │
│                                                                 │
│  흐름 예시:                                                     │
│  1. Grafana 대시보드에서 CB failure_rate 급증 감지 (Metrics)    │
│  2. Exemplar 링크 클릭 → 해당 시점의 Trace 확인 (Traces)       │
│  3. Trace의 spanId로 상세 로그 검색 (Logs)                     │
│  4. 로그에서 근본 원인 확인 (예: DB connection timeout)         │
│                                                                 │
│  CB 상태별 연동:                                                │
│  ├── CB OPEN 전이 → Alert (Metrics) + Span Event (Traces)     │
│  │                   + WARN 로그 (Logs)                        │
│  ├── CB HALF-OPEN  → Span Event + INFO 로그                   │
│  └── CB CLOSED 복구 → Alert Resolved + Span Event + INFO 로그 │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.6 Testing: WireMock, Toxiproxy, CI/CD Chaos 통합

도구 용도 테스트 레벨 시뮬레이션 대상
WireMock HTTP API 목(mock) + 지연/오류 주입 단위/통합 테스트 HTTP 응답 코드, 지연, 타임아웃
Toxiproxy TCP 레벨 네트워크 장애 주입 통합/E2E 테스트 지연, 대역폭 제한, 연결 끊김, 패킷 손실
Chaos Mesh K8s Pod/Network/IO 장애 주입 E2E/프로덕션 Pod kill, 네트워크 파티션, IO 지연
Testcontainers 테스트용 컨테이너 관리 통합 테스트 실제 DB, Redis, Kafka 등
┌─────────────────────────────────────────────────────────────────┐
│          CI/CD 파이프라인 Chaos 통합                              │
│                                                                 │
│  ┌──────┐   ┌──────┐   ┌──────────┐   ┌──────────┐   ┌─────┐ │
│  │Build │──►│Unit  │──►│Integration│──►│Chaos     │──►│Deploy│ │
│  │      │   │Test  │   │Test      │   │Test      │   │     │ │
│  └──────┘   └──────┘   └──────────┘   └──────────┘   └─────┘ │
│                          WireMock       Toxiproxy              │
│                          Testcontainers Chaos Mesh             │
│                                                                 │
│  Integration Test (WireMock):                                   │
│  ├── Downstream 500 응답 주입 → CB OPEN 전이 확인              │
│  ├── 지연 주입 → Timeout + slowCall 동작 확인                  │
│  └── 정상 복구 → CB CLOSED 복귀 확인                           │
│                                                                 │
│  Chaos Test (Toxiproxy/Chaos Mesh):                             │
│  ├── 네트워크 파티션 → Fallback 동작 확인                      │
│  ├── 네트워크 지연 주입 → Bulkhead 격리 확인                   │
│  └── Pod 종료 → 서비스 가용성 유지 확인                        │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.7 Dynamic Config: Spring Cloud Config + Feature Flags

┌─────────────────────────────────────────────────────────────────┐
│          동적 구성 관리                                           │
│                                                                 │
│  [Spring Cloud Config + Refresh]                                │
│                                                                 │
│  Config Server ──► Git Repo (resilience4j 설정)                │
│       │                                                         │
│       ▼                                                         │
│  Service Pod ──► @RefreshScope + /actuator/refresh              │
│       │                                                         │
│       └── CB 설정 변경이 재배포 없이 반영                       │
│           (failureRateThreshold, waitDuration 등)               │
│                                                                 │
│  [Feature Flags + CB 연동]                                      │
│                                                                 │
│  Feature Flag ──► "new-payment-provider" = true                │
│       │                                                         │
│       ▼                                                         │
│  if (featureFlag.isEnabled("new-payment-provider")) {          │
│      // 새 결제 제공자 (자체 CB 인스턴스)                       │
│      newPaymentCB.executeSupplier { newProvider.pay() }        │
│  } else {                                                       │
│      // 기존 결제 제공자                                        │
│      legacyPaymentCB.executeSupplier { legacyProvider.pay() }  │
│  }                                                              │
│                                                                 │
│  [etcd / Consul Watch — K8s 환경]                               │
│                                                                 │
│  etcd ──► ConfigMap 변경 감지 ──► Pod 환경변수 갱신            │
│           (watch 메커니즘)        또는 Volume 재마운트          │
│                                                                 │
│  장점: 장애 발생 시 실시간으로 CB 설정 조정 가능               │
│  예: waitDurationInOpenState: 30s → 60s (복구 시간 확보)       │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.8 Multi-Region Resilience

DNS Failover vs Application CB

비교 DNS Failover Application CB
감지 속도 느림 (DNS TTL, 30s~300s) 빠름 (밀리초~초)
세밀도 서비스 전체 단위 엔드포인트/메서드 단위
Failover 대상 다른 Region 서비스 Fallback 로직, 캐시, 기본값
비용 높음 (Multi-Region 인프라) 낮음 (코드 레벨)
적합 상황 Region 전체 장애 서비스/엔드포인트 장애

Cell-based Architecture

┌─────────────────────────────────────────────────────────────────┐
│          Cell-based Architecture — 궁극의 Bulkhead               │
│                                                                 │
│  [기존 Multi-Region]                                            │
│  Region A (Active) ◄─── DNS ───► Region B (Standby)           │
│  전체 트래픽 처리        장애 시 전환                           │
│  문제: Region 전환 = 전체 영향, 부분 장애 대응 불가            │
│                                                                 │
│  [Cell-based Architecture]                                      │
│  ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐      │
│  │ Cell 1 │ │ Cell 2 │ │ Cell 3 │ │ Cell 4 │ │ Cell 5 │      │
│  │ 사용자  │ │ 사용자  │ │ 사용자  │ │ 사용자  │ │ 사용자  │      │
│  │ A~F    │ │ G~L    │ │ M~R    │ │ S~X    │ │ Y~Z    │      │
│  │ 독립DB │ │ 독립DB │ │ 독립DB │ │ 독립DB │ │ 독립DB │      │
│  └────────┘ └────────┘ └────────┘ └────────┘ └────────┘      │
│                                                                 │
│  Cell 3 장애 → 사용자 M~R만 영향                               │
│  → Cell 3 사용자를 다른 Cell로 재매핑                          │
│  → 전체의 20%만 영향, 80%는 무관                               │
│                                                                 │
│  핵심: 각 Cell은 완전히 독립된 "미니 시스템"                   │
│  → Blast Radius = 1/N (N = Cell 수)                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

17.9 Global Rate Limiting: Bucket4j + Redis

┌─────────────────────────────────────────────────────────────────┐
│          분산 Rate Limiting — Bucket4j + Redis                   │
│                                                                 │
│  [문제: 인스턴스별 Rate Limiter]                                │
│                                                                 │
│  Pod 1: limit=100 ──┐                                          │
│  Pod 2: limit=100 ──┤── 총 허용 = 100 × N (인스턴스 수 비례)  │
│  Pod 3: limit=100 ──┘    → 스케일 아웃 시 전체 limit 증가!    │
│                                                                 │
│  [해결: Redis 기반 분산 Rate Limiter]                           │
│                                                                 │
│  Pod 1 ──┐                                                     │
│  Pod 2 ──┤──► Redis (공유 Token Bucket)                        │
│  Pod 3 ──┘    └── 전체 limit = 100 (인스턴스 수 무관)          │
│                                                                 │
│  // Bucket4j + Redis (Lettuce) 예시                             │
│  val proxyManager = Bucket4jRedis                               │
│      .casBasedBuilder(lettuceBasedProxyManager)                 │
│      .build()                                                   │
│                                                                 │
│  val bucket = proxyManager.builder()                            │
│      .addLimit(                                                 │
│          BandwidthBuilder.builder()                             │
│              .capacity(100)                                     │
│              .refillGreedy(100, Duration.ofSeconds(1))          │
│              .build()                                           │
│      )                                                          │
│      .build(key)                                                │
│                                                                 │
│  주의사항:                                                      │
│  ├── Redis 자체가 SPOF → Redis Cluster/Sentinel 필수           │
│  ├── 네트워크 지연 추가 (~1ms) → 허용 가능한 수준             │
│  └── Redis 장애 시 Fallback: 로컬 Rate Limiter로 전환         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

18. Jitter 변종 및 Sliding Window 심화

18.1 Jitter 변종 수학적 공식

Full Jitter

┌─────────────────────────────────────────────────────────────────┐
│          Full Jitter                                             │
│                                                                 │
│  공식: sleep = random(0, min(cap, base × 2^attempt))           │
│                                                                 │
│  특징:                                                          │
│  ├── 대기 시간이 0부터 최대값까지 균일 분포                    │
│  ├── 가장 공격적인 분산 → 서버 부하 가장 고르게 분포           │
│  └── 단점: 운 나쁘면 0에 가까운 대기 → 즉시 재시도 가능       │
│                                                                 │
│  Python 구현:                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  import random                                          │    │
│  │                                                          │    │
│  │  def full_jitter(base_ms, cap_ms, attempt):             │    │
│  │      exp_backoff = min(cap_ms, base_ms * (2 ** attempt))│    │
│  │      return random.uniform(0, exp_backoff)              │    │
│  │                                                          │    │
│  │  # 예: base=100ms, cap=10000ms                          │    │
│  │  # attempt 0: random(0, 100)    → 0~100ms              │    │
│  │  # attempt 1: random(0, 200)    → 0~200ms              │    │
│  │  # attempt 2: random(0, 400)    → 0~400ms              │    │
│  │  # attempt 5: random(0, 3200)   → 0~3200ms             │    │
│  │  # attempt 7: random(0, 10000)  → 0~10000ms (cap)      │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Equal Jitter

┌─────────────────────────────────────────────────────────────────┐
│          Equal Jitter                                            │
│                                                                 │
│  공식: temp = min(cap, base × 2^attempt)                       │
│        sleep = temp/2 + random(0, temp/2)                      │
│                                                                 │
│  특징:                                                          │
│  ├── 최소 대기 시간 = 지수 백오프의 절반 보장                  │
│  ├── 나머지 절반을 랜덤으로 → 적절한 분산 + 최소 대기 보장    │
│  └── Full Jitter보다 보수적, 안정적                            │
│                                                                 │
│  Python 구현:                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  import random                                          │    │
│  │                                                          │    │
│  │  def equal_jitter(base_ms, cap_ms, attempt):            │    │
│  │      temp = min(cap_ms, base_ms * (2 ** attempt))       │    │
│  │      return temp / 2 + random.uniform(0, temp / 2)      │    │
│  │                                                          │    │
│  │  # 예: base=100ms, cap=10000ms                          │    │
│  │  # attempt 0: 50 + random(0, 50)   → 50~100ms          │    │
│  │  # attempt 1: 100 + random(0, 100) → 100~200ms         │    │
│  │  # attempt 2: 200 + random(0, 200) → 200~400ms         │    │
│  │  # attempt 5: 1600 + random(0, 1600) → 1600~3200ms     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Decorrelated Jitter

┌─────────────────────────────────────────────────────────────────┐
│          Decorrelated Jitter                                     │
│                                                                 │
│  공식: sleep = min(cap, random(base, prev_sleep × 3))          │
│                                                                 │
│  특징:                                                          │
│  ├── 이전 대기 시간에 기반 (상태 유지)                          │
│  ├── 지수적 증가가 아닌 확률적 증가                             │
│  ├── 시도 간 상관관계 없음 (decorrelated)                      │
│  └── AWS 권장 (가장 균일한 서버 부하 분포)                     │
│                                                                 │
│  Python 구현:                                                   │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  import random                                          │    │
│  │                                                          │    │
│  │  def decorrelated_jitter(base_ms, cap_ms, prev_sleep_ms):│   │
│  │      sleep = random.uniform(base_ms, prev_sleep_ms * 3) │    │
│  │      return min(cap_ms, sleep)                          │    │
│  │                                                          │    │
│  │  # 사용 예:                                              │    │
│  │  sleep_ms = base_ms  # 초기값                           │    │
│  │  for attempt in range(max_retries):                     │    │
│  │      try:                                               │    │
│  │          return call_service()                          │    │
│  │      except TransientError:                             │    │
│  │          sleep_ms = decorrelated_jitter(                │    │
│  │              base_ms, cap_ms, sleep_ms)                 │    │
│  │          time.sleep(sleep_ms / 1000)                    │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

AWS 시뮬레이션 비교 (100 클라이언트 동시 Retry)

지표 No Jitter Full Jitter Equal Jitter Decorrelated Jitter
서버 피크 부하 100 req/s (전부 동시) ~15 req/s ~25 req/s ~12 req/s
평균 완료 시간 가장 느림 (경쟁) 빠름 중간 가장 빠름
완료 시간 분산 매우 큼 중간 작음 중간
최소 대기 보장 있음 (고정값) 없음 (0 가능) 있음 (절반) 있음 (base 이상)

선택 기준 가이드

상황 권장 Jitter 이유
서버 부하 분산이 최우선 Full Jitter 가장 고른 분포
최소 대기 시간 보장 필요 Equal Jitter 절반은 보장
상태 기반 적응형 대기 필요 Decorrelated Jitter 이전 대기에 기반한 자연스러운 조정
AWS 서비스 호출 Decorrelated Jitter AWS 공식 권장
잘 모르겠을 때 Full Jitter 단순하고 효과적

18.2 Sliding Window 내부 구현 심화

COUNT_BASED: Ring Buffer

┌─────────────────────────────────────────────────────────────────┐
│          COUNT_BASED Ring Buffer 내부 구현                        │
│                                                                 │
│  slidingWindowSize = 10                                         │
│                                                                 │
│  Ring Buffer (고정 크기 배열):                                   │
│  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐                    │
│  │ S │ F │ S │ S │ F │ S │ S │ S │ F │ S │                    │
│  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘                    │
│    0   1   2   3   4   5   6   7   8   9                       │
│                                          ↑                      │
│                                        head (다음 기록 위치)   │
│                                                                 │
│  누적 카운터 (O(1) Subtract-on-Evict):                         │
│  ├── totalCalls = 10                                           │
│  ├── failedCalls = 3                                           │
│  ├── failureRate = 3/10 = 30%                                  │
│  └── slowCalls = 1                                             │
│                                                                 │
│  새 호출 결과 기록 (SUCCESS):                                   │
│  1. head 위치의 기존 값 확인: buffer[0] = SUCCESS              │
│  2. 기존 값 카운터에서 빼기: (SUCCESS이므로 변화 없음)         │
│  3. 새 값 기록: buffer[0] = SUCCESS                            │
│  4. 새 값 카운터에 더하기: (SUCCESS이므로 변화 없음)           │
│  5. head = (head + 1) % size                                   │
│                                                                 │
│  핵심: 매 기록마다 O(1) — 전체 Window 재계산 불필요!           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

TIME_BASED: Partial Aggregation Buckets

┌─────────────────────────────────────────────────────────────────┐
│          TIME_BASED Sliding Window 내부 구현                     │
│                                                                 │
│  slidingWindowSize = 10 (초)                                    │
│                                                                 │
│  시간 기반 부분 집계 (1초 단위 Bucket):                         │
│                                                                 │
│  현재: 12:00:15                                                 │
│  ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┐│
│  │:06  │:07  │:08  │:09  │:10  │:11  │:12  │:13  │:14  │:15  ││
│  │ 5/50│ 3/45│ 8/60│ 2/40│ 6/55│ 4/48│ 7/52│ 1/38│ 5/44│ 3/42││
│  │fail │fail │fail │fail │fail │fail │fail │fail │fail │fail ││
│  │/tot │/tot │/tot │/tot │/tot │/tot │/tot │/tot │/tot │/tot ││
│  └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘│
│                                                                 │
│  집계: totalFailed = 5+3+8+2+6+4+7+1+5+3 = 44                 │
│        totalCalls = 50+45+60+40+55+48+52+38+44+42 = 474       │
│        failureRate = 44/474 = 9.28%                             │
│                                                                 │
│  :16이 되면:                                                    │
│  1. :06 Bucket 제거 (Subtract): failed-=5, total-=50           │
│  2. :16 Bucket 추가: 새 집계 시작                              │
│  → O(1) 연산 (Subtract-on-Evict)                               │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

메모리 사용량 비교

항목 COUNT_BASED TIME_BASED
메모리 windowSize × (결과 1byte) windowSize × (Bucket 집계 ~40bytes)
예시 (size=100) ~100 bytes ~4,000 bytes
예시 (size=1000) ~1,000 bytes ~40,000 bytes
갱신 비용 O(1) 항상 O(1) 평균 (Bucket 만료 시)
정밀도 정확히 N개 호출 초 단위 근사

트래픽 패턴별 윈도우 선택 기준

트래픽 패턴 권장 윈도우 이유
고정 TPS (일정한 트래픽) COUNT_BASED 호출 수 일정 → 시간/개수 차이 없음, 메모리 효율적
변동 TPS (피크/비수기 차이 큼) TIME_BASED 비수기에 COUNT_BASED는 오래된 데이터 포함 가능
간헐적 호출 (배치, 스케줄러) COUNT_BASED TIME_BASED는 호출 없는 시간에 Window가 비어 무의미
높은 TPS (수천 req/s) TIME_BASED COUNT_BASED는 Window가 매우 빠르게 회전 → 순간 오류 놓칠 수 있음
잘 모르겠을 때 COUNT_BASED (기본값) 단순하고 메모리 효율적, 대부분의 상황에 적합

19. Error Budget과 Circuit Breaker 연동

19.1 Google SRE Error Budget 기본 공식

┌─────────────────────────────────────────────────────────────────┐
│          Error Budget 기본 개념                                   │
│                                                                 │
│  SLO (Service Level Objective) = 99.9% 가용성                   │
│                                                                 │
│  Error Budget = 1 - SLO = 1 - 0.999 = 0.1%                    │
│                                                                 │
│  30일 기준:                                                     │
│  Error Budget = 30일 × 24시간 × 60분 × 0.001                  │
│               = 43.2분                                          │
│                                                                 │
│  의미: "30일 동안 43.2분의 장애는 허용된다"                     │
│                                                                 │
│  ┌──────────────────────────────────────────────┐              │
│  │  SLO      │ Error Budget (30일) │ 허용 장애   │              │
│  │───────────┼────────────────────┼────────────│              │
│  │  99%      │ 432분 (7.2시간)     │ 넉넉함     │              │
│  │  99.9%    │ 43.2분             │ 보통       │              │
│  │  99.95%   │ 21.6분             │ 빡빡함     │              │
│  │  99.99%   │ 4.32분             │ 매우 빡빡  │              │
│  │  99.999%  │ 0.432분 (26초)     │ 극도로 빡빡│              │
│  └──────────────────────────────────────────────┘              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

19.2 Burn Rate와 Multiwindow Multi-Burn-Rate Alert

┌─────────────────────────────────────────────────────────────────┐
│          Burn Rate 개념                                          │
│                                                                 │
│  Burn Rate = 실제 오류 소비 속도 / 정상 소비 속도               │
│                                                                 │
│  예: SLO 99.9% (30일), Error Budget = 43.2분                   │
│                                                                 │
│  Burn Rate 1x = 43.2분/30일 = 정상 속도 (30일에 딱 소진)      │
│  Burn Rate 2x = 15일에 소진                                    │
│  Burn Rate 10x = 3일에 소진                                    │
│  Burn Rate 36x = 20시간에 소진 → 긴급!                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

Multiwindow Multi-Burn-Rate Alert 표

Alert 수준 Burn Rate Long Window Short Window 의미
P1 (Critical) 14.4x 1시간 5분 2일 내 Budget 소진, 즉시 대응
P2 (High) 6x 6시간 30분 5일 내 Budget 소진, 빠른 대응
P3 (Medium) 3x 1일 2시간 10일 내 Budget 소진, 당일 대응
P4 (Low) 1x 3일 6시간 30일 내 Budget 소진, 계획 대응

Long Window와 Short Window 모두에서 Burn Rate가 초과해야 Alert 발생 → 오탐(false positive) 방지.

19.3 Error Budget 상태 × CB 설정 조정 매트릭스

Error Budget 잔여량 CB failureRateThreshold CB waitDuration CB slowCallThreshold Retry maxAttempts 정책
> 75% (여유) 50% (기본) 30s 80% 3 일반 운영
50~75% (주의) 40% (약간 엄격) 45s 70% 2 보수적 전환
25~50% (경고) 30% (엄격) 60s 60% 1 방어적 운영
< 25% (위험) 20% (매우 엄격) 120s 50% 0 (Retry 비활성) Feature Freeze + 안정화 집중
소진됨 (0%) 10% 300s 30% 0 변경 금지, 복구만
┌─────────────────────────────────────────────────────────────────┐
│          Error Budget 기반 CB 동적 조정 흐름                     │
│                                                                 │
│  Prometheus ──► Error Budget 잔여량 계산                        │
│       │                                                         │
│       ▼                                                         │
│  Budget > 75% ──► 기본 CB 설정 유지                            │
│  Budget 50~75% ──► CB threshold 강화 (40%)                     │
│  Budget 25~50% ──► CB threshold 강화 (30%) + Retry 축소        │
│  Budget < 25% ──► CB 매우 엄격 (20%) + Retry 비활성            │
│       │            + Feature Flag로 신규 기능 비활성             │
│       ▼                                                         │
│  Spring Cloud Config ──► 재배포 없이 설정 반영                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

19.4 CB 메트릭과 SLO 연동 Prometheus 쿼리

┌─────────────────────────────────────────────────────────────────┐
│          Prometheus 쿼리 예시                                    │
│                                                                 │
│  [1] 서비스별 Error Budget 잔여량 (30일 Rolling)]               │
│                                                                 │
│  # 30일간 실제 오류율                                           │
│  actual_error_rate = (                                          │
│    sum(rate(http_requests_total{status=~"5.."}[30d]))          │
│    /                                                            │
│    sum(rate(http_requests_total[30d]))                          │
│  )                                                              │
│                                                                 │
│  # Error Budget 잔여량 (%)                                     │
│  error_budget_remaining = (                                     │
│    1 - (actual_error_rate / (1 - 0.999))                       │
│  ) * 100                                                        │
│                                                                 │
│  [2] Burn Rate 계산                                             │
│                                                                 │
│  # 1시간 Burn Rate                                              │
│  burn_rate_1h = (                                               │
│    sum(rate(http_requests_total{status=~"5.."}[1h]))           │
│    /                                                            │
│    sum(rate(http_requests_total[1h]))                           │
│  ) / (1 - 0.999)                                                │
│                                                                 │
│  [3] CB 상태와 Error Budget 연동 대시보드                       │
│                                                                 │
│  # CB가 OPEN인 서비스의 Error Budget 영향                      │
│  sum by (service) (                                             │
│    resilience4j_circuitbreaker_state{state="open"} == 1        │
│  )                                                              │
│  *                                                              │
│  on(service) group_left()                                       │
│  (error_budget_remaining)                                       │
│                                                                 │
│  [4] CB 차단율과 SLO 위반 상관관계                              │
│                                                                 │
│  # CB가 차단한 요청 비율                                       │
│  cb_rejection_rate = (                                          │
│    sum(rate(resilience4j_circuitbreaker_not_permitted_calls     │
│      _total[5m]))                                               │
│    /                                                            │
│    sum(rate(resilience4j_circuitbreaker_calls_seconds_count    │
│      [5m]))                                                     │
│  )                                                              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

19.5 비용 분석: Resilience 패턴 도입 비용 vs 장애 비용

┌─────────────────────────────────────────────────────────────────┐
│          Resilience 투자 대비 수익 (ROI) 분석                    │
│                                                                 │
│  [장애 비용 산정]                                                │
│                                                                 │
│  직접 비용:                                                     │
│  ├── 매출 손실 = 시간당 매출 × 장애 시간                       │
│  ├── SLA 위약금 = 계약 기반 (보통 월 매출의 10~30%)            │
│  └── 복구 인건비 = 엔지니어 수 × 시급 × 복구 시간             │
│                                                                 │
│  간접 비용:                                                     │
│  ├── 고객 이탈 = 장애 경험 고객의 5~15% 이탈 (산업 평균)      │
│  ├── 브랜드 신뢰 하락 (정량화 어려움)                          │
│  └── 개발 생산성 저하 (장애 대응 + 포스트모텀 시간)            │
│                                                                 │
│  예시: 시간당 매출 1억원 서비스                                  │
│  ├── 연간 2회 × 2시간 장애 = 4시간 × 1억 = 4억원 직접 손실    │
│  ├── SLA 위약금: 1억원                                         │
│  ├── 고객 이탈: 2억원 (추정)                                   │
│  └── 총 장애 비용: ~7억원/년                                   │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [Resilience 패턴 도입 비용]                                    │
│                                                                 │
│  개발 비용:                                                     │
│  ├── CB + Retry + Timeout: 2주 (1명) = 400만원                 │
│  ├── Bulkhead + Rate Limiter: 1주 = 200만원                    │
│  ├── 모니터링 대시보드: 1주 = 200만원                          │
│  └── Chaos Engineering 구축: 2주 = 400만원                     │
│                                                                 │
│  운영 비용 (연간):                                              │
│  ├── 모니터링 인프라: 500만원                                  │
│  ├── GameDay 운영 (분기별): 200만원                            │
│  └── 설정 튜닝/유지보수: 300만원                               │
│                                                                 │
│  총 도입 비용: ~2,200만원 (첫해), ~1,000만원 (이후 연간)       │
│                                                                 │
│  ROI = (7억 - 0.22억) / 0.22억 = 약 30배                      │
│  (장애 비용을 50%만 줄여도 ROI 15배)                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

19.6 단계별 도입 로드맵

┌─────────────────────────────────────────────────────────────────┐
│          Resilience 패턴 단계별 도입 로드맵                      │
│                                                                 │
│  Phase 1: 기초 (2주)                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Timeout 설정 (모든 외부 호출)                       │    │
│  │  ├── Retry + Exponential Backoff + Jitter               │    │
│  │  ├── 기본 Health Check                                   │    │
│  │  └── 메트릭 수집 시작 (Micrometer + Prometheus)          │    │
│  │                                                          │    │
│  │  성과: 기본적인 일시적 오류 복구 능력 확보               │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Phase 2: 핵심 (2주)                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Circuit Breaker (핵심 서비스 호출)                  │    │
│  │  ├── Fallback (Graceful Degradation)                     │    │
│  │  ├── Grafana 대시보드 구축                               │    │
│  │  └── METRICS_ONLY 모드로 관찰 기간 운영                 │    │
│  │                                                          │    │
│  │  성과: 연쇄 장애 차단 능력 확보, 가시성 확보            │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Phase 3: 격리 (2주)                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Bulkhead (서비스별 격리)                            │    │
│  │  ├── Rate Limiter (외부 API 호출 제한)                   │    │
│  │  ├── WireMock 기반 통합 테스트                           │    │
│  │  └── Alert 규칙 설정                                     │    │
│  │                                                          │    │
│  │  성과: 리소스 격리 + 과부하 방지                         │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Phase 4: 검증 (2주)                                            │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Chaos Engineering 도입 (Toxiproxy / Chaos Mesh)    │    │
│  │  ├── 첫 번째 GameDay 실시                                │    │
│  │  ├── SLO/Error Budget 정의                               │    │
│  │  └── Error Budget 기반 Alert 설정                        │    │
│  │                                                          │    │
│  │  성과: 장애 내성 검증 + SLO 기반 운영 시작              │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
│  Phase 5: 최적화 (지속적)                                       │
│  ┌─────────────────────────────────────────────────────────┐    │
│  │  ├── Adaptive Concurrency Limits 도입                    │    │
│  │  ├── Load Shedding (Priority-based)                      │    │
│  │  ├── Hedging Pattern (Fan-out 서비스)                    │    │
│  │  ├── Error Budget 기반 CB 동적 조정                      │    │
│  │  ├── 정기 GameDay (분기별)                               │    │
│  │  └── Multi-Region Failover                               │    │
│  │                                                          │    │
│  │  성과: 자동화된 최적화 + 지속적 개선                     │    │
│  └─────────────────────────────────────────────────────────┘    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

19.7 YAGNI + Resilience 균형 의사결정 프레임워크

┌─────────────────────────────────────────────────────────────────┐
│          YAGNI와 Resilience의 균형                               │
│                                                                 │
│  YAGNI (You Aren't Gonna Need It):                              │
│  "필요할 때까지 만들지 마라"                                    │
│                                                                 │
│  Resilience Engineering:                                        │
│  "장애는 반드시 발생한다. 미리 대비하라"                        │
│                                                                 │
│  → 모순처럼 보이지만, 둘 다 맞다.                               │
│  → 핵심은 "어디까지가 과잉이고, 어디부터가 필수인가?"          │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  의사결정 프레임워크:                                            │
│                                                                 │
│  Q1: 이 서비스가 죽으면 매출에 직접 영향이 있는가?             │
│  ├── YES → Timeout + Retry + CB + Fallback (Phase 1~2 즉시)   │
│  └── NO  → Timeout + Retry만 (Phase 1)                        │
│                                                                 │
│  Q2: Fan-out이 10개 이상인가?                                  │
│  ├── YES → Hedging + Bulkhead (Phase 3~5 병행)                │
│  └── NO  → Bulkhead는 선택사항                                │
│                                                                 │
│  Q3: 트래픽이 예측 불가능하게 변동하는가?                      │
│  ├── YES → Adaptive Concurrency + Load Shedding               │
│  └── NO  → 정적 Bulkhead + 고정 Rate Limit으로 충분           │
│                                                                 │
│  Q4: SLA에 가용성 조항이 있는가?                               │
│  ├── YES → SLO/Error Budget 정의 + Chaos Engineering 필수     │
│  └── NO  → 비즈니스 임팩트 기반 판단                          │
│                                                                 │
│  Q5: Multi-Region이 필요한가?                                  │
│  ├── YES → Cell-based Architecture 검토                       │
│  └── NO  → 단일 Region + Multi-AZ로 충분                     │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  [절대 YAGNI가 아닌 것들 — 항상 필수]                           │
│  ├── 모든 외부 호출에 Timeout 설정                             │
│  ├── Retry에 Exponential Backoff + Jitter                      │
│  ├── 모니터링 (메트릭 + 로그 + 트레이스)                       │
│  └── Health Check                                               │
│                                                                 │
│  [YAGNI일 수 있는 것들 — 필요할 때 추가]                       │
│  ├── Adaptive Concurrency Limits (트래픽 안정적이면 불필요)    │
│  ├── Hedging (Fan-out 없으면 불필요)                           │
│  ├── Multi-Region (단일 Region으로 SLO 달성 가능하면)          │
│  ├── Cell-based Architecture (수십만 사용자 이하면 과잉)       │
│  └── Chaos Engineering (서비스 1~2개 수준이면 과잉)            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘