LabHub

Envoy 내부 구조 · 장애를 다루는 장치들 · 이론

재시도가 장애를 키우는 순간

LabHub 에서 이어서 보기

한 줄 요약

재시도는 일시적 실패를 가린다. 그런데 백엔드가 과부하일 때 재시도를 켜 두면 부하를 두세 배로 키워 장애를 완성시킨다. 그래서 재시도에는 항상 상한과 예산이 필요하다.

왜 이게 필요했나

백엔드 하나가 느려진다 → 타임아웃 → 재시도 → 부하 2배 → 더 느려진다 → 재시도 → …

이 되먹임을 재시도 폭풍(retry storm)이라 한다. 메시가 서비스마다 사이드카를 두므로, 3단 호출 체인에서 각 단이 3번씩 재시도하면 맨 끝 서비스는 27배를 맞는다. 재시도는 곱해진다.

막는 장치는 셋이고, 각각 다른 문제를 푼다.

| 장치 | 무엇을 보나 | 무엇을 하나 |
|---|---|---|
| 재시도 예산 | 전체 요청 대비 재시도 비율 | 재시도가 일정 비율을 넘으면 더 안 한다 |
| 서킷 브레이커 | 동시 연결·대기 요청 수 | 한도를 넘으면 즉시 실패시킨다(빨리 죽는다) |
| 이상치 감지 | 엔드포인트별 연속 실패 | 나쁜 엔드포인트를 잠시 뺀다 |

어떻게 동작하나

서킷 브레이커는 이름과 달리 "차단기" 라기보다 한도다.

# DestinationRuletrafficPolicy:  connectionPool:    tcp: { maxConnections: 100 }    http:      http1MaxPendingRequests: 20     # 커넥션을 기다리는 요청 상한      maxRequestsPerConnection: 100

한도를 넘은 요청은 기다리지 않고 즉시 503 을 받는다. 잔인해 보이지만 이게 옳다 — 기다리게 하면 클라이언트의 스레드까지 묶여 장애가 위로 번진다. 빨리 실패하는 편이 전체 시스템에 낫다.

이상치 감지는 로드밸런싱 풀에서 나쁜 놈을 빼는 장치다.

outlierDetection:  consecutive5xxErrors: 5  interval: 10s  baseEjectionTime: 30s  maxEjectionPercent: 50      # 절반 넘게는 절대 빼지 않는다

maxEjectionPercent 가 중요하다. 이게 없으면 백엔드 전체가 잠깐 나빠졌을 때 엔드포인트를 전부 빼 버려 서비스가 통째로 죽는다. 감지 장치가 장애를 만드는 것이다.

흔한 착각

"재시도는 켜 두면 좋다" — 멱등하지 않은 요청(POST 로 결제, 주문)에 재시도를 걸면 중복이 생긴다. Envoy 의 retryOn 기본값은 안전한 조건만 담지만, 5xx 를 넣는 순간 서버가 처리하다 실패한 요청도 다시 보낸다.

타임아웃 없는 재시도. 재시도 3번에 각각 30초 타임아웃이면 최악의 경우 90초를 기다린다. 그 사이 사용자는 이미 떠났고 커넥션만 묶여 있다. 전체 시간 상한(timeout)을 재시도와 함께 정해야 한다.

실무에서 진짜 중요한 것

장애 조사에서 이 셋을 구분하는 신호가 다르다.

# 서킷 브레이커에 걸렸나 — 대기열 넘침envoy_cluster_upstream_rq_pending_overflow# 이상치 감지가 엔드포인트를 뺐나envoy_cluster_outlier_detection_ejections_active# 재시도가 얼마나 도나envoy_cluster_upstream_rq_retry / envoy_cluster_upstream_rq_total

마지막 비율이 5%를 넘으면 이미 재시도가 부하의 일부다. 그때 재시도를 늘리는 것은 불에 기름이다.