Circuit Breaker
MSA 에서 하나의 서비스 장애가 전체 시스템으로 전파되는 것을 Cascading Failure 라 한다. Circuit Breaker는 이를 방지하는 패턴이다.
Circuit Breaker 는 원래 주식시장에서 주가가 급락할 때 시장에 미치는 충격을 완화하기 위해 매매를 일시적으로 중단하는 ‘비상 브레이크’ 장치로 알려져있다.
엔지니어링에서는 다른 뜻으로 사용된다.
다음과 같이 MSA에서 서비스 A가 서비스 B를 호출하는 상황을 가정해보자.
flowchart LR
A[Service A] -->|호출| B[Service B]
B -->|응답 지연/실패| A
A -.->|스레드/커넥션 고갈| A
A -.->|연쇄 장애| C[Service C]
A -.->|연쇄 장애| D[Service D]
서비스 B가 응답하지 않으면 다음과 같은 일들을 예상해볼수 있다.
-
A의 호출 스레드가 응답을 기다리며 대기 상태에 빠진다
-
대기 중인 요청이 쌓이면서 A의 스레드 풀, 커넥션 풀이 고갈된다
-
A에 의존하는 다른 서비스(C, D)도 연쇄적으로 장애가 발생한다
-
사용자가 재시도를 반복하면 상황이 더 악화된다 (retry storm)
Retry Storm 에 대해서는 별도의 글에서 어떤 전략을 사용해야 하는지 다룬다.
Circuit Breaker는 이 체인을 끊는다.
-
장애 서비스로의 요청을 즉시 차단하여 리소스 고갈을 방지한다
-
빠른 실패(Fail Fast) 로 응답 시간을 보장한다
-
장애 서비스에 추가 부하를 주지 않아 회복 시간을 확보해준다
즉 써킷 브레이커는 장애가 전파되는것을 막아주는 긴급 브레이크다.
Circuit Breaker 의 상태
Circuit Breaker는 전기 회로의 차단기처럼 세 가지 상태를 순환한다.
stateDiagram-v2
[*] --> Closed
Closed --> Open : 실패율 ≥ 임계값
Open --> HalfOpen : 대기 시간 경과
HalfOpen --> Closed : 시험 요청 성공
HalfOpen --> Open : 시험 요청 실패
Open 이 차단된 상태이고 Closed 가 정상인 상태임에 유의하자!
Closed (정상)
-
모든 요청이 정상적으로 통과한다
-
실패 횟수/비율을 슬라이딩 윈도우로 추적한다
-
실패율이 임계값을 넘으면 Open 으로 전환된다
Open (차단)
-
모든 요청을 즉시 거부한다 (원격 호출 없이 에러 반환)
-
장애 서비스를 격리하여 회복 시간을 확보한다
-
설정된 대기 시간(timeout)이 경과하면 → Half-Open 전환
Half-Open (테스트)
-
제한된 수의 시험 요청만 통과시킨다
-
시험 요청이 성공하면 → Closed 전환 (복구 완료됨)
-
시험 요청이 실패하면 → Open 전환 (아직 복구하면 안됨)
Half-Open은 복구 중인 서비스에 갑작스러운 트래픽 폭주를 방지하는 역할도 한다.
보통은 Sliding Window 알고리즘을 사용하게 된다.
트래픽이 낮은 서비스일수록 최근 N초간의 호출 결과를 집계하는 Time-based 방식이 좋다.
트래픽이 높은 서비스일수록 최근 N건의 호출 결과를 집계하는 Count-based 방식이 좋다.
Istio 와 함께하기
Istio 와 같은 서비스 메시에서 인프라 수준으로 이를 달성하는 방식이 가장 많이 쓰이고, 또 간단하다.
Istio Outlier Detection:
-
Envoy 프록시가 각 서비스 인스턴스의 상태를 자동 추적한다
-
연속 에러, 레이턴시 등의 메트릭을 분석한다
-
기준을 초과하는 인스턴스를 로드 밸런싱 풀에서 자동 제외(eject) 한다
-
Panic Mode: 너무 많은 인스턴스가 제외되면 헬스체크를 무시하고 모든 인스턴스로 라우팅한다
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: soobook
spec:
host: "soobook.io"
trafficPolicy:
outlierDetection:
consecutive5xxErrors: 7
baseEjectionTime: 3m
...
Circuit Breaker, Retry, Bulkhead
이 세 패턴은 상호 배타적이지 않으며, 함께 사용하는 것이 일반적이다.
| 패턴 | 목적 | 대상 장애 | 적용 위치 |
|---|---|---|---|
| Retry | 일시적 장애를 재시도로 극복 | 네트워크 글리치, DB 락 경합 | 호출자 |
| Circuit Breaker | 지속적 장애를 감지하고 차단 | 서비스 다운, 장기 응답 불능 | 호출자 |
| Bulkhead | 리소스 격리로 장애 전파 방지 | 리소스 고갈 | 호출자 |
flowchart LR
Request --> BH[Bulkhead<br/>리소스 격리]
BH --> CB[Circuit Breaker<br/>장애 감지/차단]
CB --> RT[Retry<br/>재시도]
RT --> TO[Timeout<br/>시간 제한]
TO --> Service[Application]
동작 흐름:
-
Bulkhead가 전용 리소스 풀에서 요청을 처리한다
-
Circuit Breaker가 열려있으면 즉시 실패한다
-
Closed 상태면 Retry가 일시적 장애 시 재시도한다
-
Timeout이 개별 호출의 최대 대기 시간을 제한한다
Rate Limiter와의 차이: Rate Limiter는 수신 요청 속도를 제한하는 피호출자 보호 패턴이고, Circuit Breaker는 발신 요청의 장애를 감지하는 호출자 보호 패턴이다.