English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

게스트
1 / ?
수업 목록으로

알아두어야 할 3가지 실패 개념

환영합니다

분산 시스템은 패턴을 따라 실패합니다. 패턴을 배우면, 모든 사후 분석은 미스터리가 아니라 인식(인식과 매칭)의 과정이 됩니다.

프로덕션 실패 분석에서 중요한 대부분의 내용은 다음 세 가지 개념으로 커버됩니다:

단일 장애점 (SPOF): 더 큰 시스템의 장애를 초래하는 구성 요소입니다. 종종 숨겨져 있습니다: 모두가 의존하는 DNS 서버, 모든 것이 갱신하는 대상인 인증서, 단일 데이터베이스 마스터 등입니다.

연쇄 장애(Cascading failure): 한 구성 요소의 장애가 다른 구성 요소의 장애를 유발하고, 이것이 또 다른 구성 요소의 장애를 유발하는 현상입니다. 느린 데이터베이스가 API 계층에서 타임아웃을 일으키고, 이는 재시도를 유발하며, 이는 데이터베이스에 더 많은 부하를 걸고, 이는 더 많은 타임아웃을 유발합니다. 폭발이 퍼져 나갑니다.

영향 범위(Blast radius): 한 부분이 실패했을 때 시스템의 어느 정도가 다운되는지를 나타냅니다. 아키텍처 선택은 이 범위를 제한하거나 제한하지 않습니다. SPOF(단일 장애 지점)는 무한한 영향 범위를 가집니다. 벌크헤드(Bulkhead) 방식의 서비스는 제한된 범위를 가집니다.

이 레슨의 끝까지 다음을 수행할 수 있습니다:

- 아키텍처를 검사하여 SPOF를 식별합니다

- 연쇄 장애 패턴(썬더링 허드, 재시도 폭풍, 죽음의 큐)을 인식합니다

- 실제 타임라인을 읽고 트리거를 트리거가 드러낸 잠재적 결함에서 분리합니다

- 사람을 대신하여 시스템을 대상으로 하는 무과실(Blameless) 조치 항목을 작성하며, 예방/탐지/복구를 다룹니다

- 벌크헤드와 서킷 브레이커를 영향 범위 제한 도구로 추론합니다

단일 장애 지점(SPOF) 찾기

계층형 아키텍처 점검

작은 웹 아키텍처를 고려해 보겠습니다:

- DNS: api.example.com -> 단일 네임서버 IP 203.0.113.10, 하나의 DNS 제공업체에서 호스팅

- CDN: api.example.com 앞에 단일 CDN 벤더

- Ingress: 로드 밸런서 뒤의 두 대의 리버스 프록시 머신

- 백엔드: 두 개의 가용성 영역(각 영역당 3개)에 있는 여섯 개의 API 리플리카

- 데이터베이스: 동일한 가용성 영역에 있는 하나의 프라이머리 + 하나의 읽기 전용 리플리카

- 캐시: Redis 클러스터, 동일한 두 가용성 영역(AZ)에 분산된 세 개의 노드

질문: 어떤 구성 요소들이 SPOF(단일 장애 지점)입니까? 힌트: SPOF는 항상 눈에 띄는 '단일 머신' 형태가 아닙니다. 하나의 가용성 영역에 모두 위치한 세 대의 머신 클러스터도 해당 영역의 장애에 대한 SPOF입니다.

이 아키텍처에서 최소 3개의 SPOF를 식별하십시오. 각 SPOF에 대해, 장애 시 어떤 것이 실패하는지 명시하고, SPOF를 제거하는 구체적인 변경 사항을 제안하십시오(애플리케이션 재작성 없이).

세 가지 전형적인 연쇄 패턴

의존성을 통한 장애 전파

패턴 1: 썬더링 허드(Thundering herd). 공유 리소스(캐시, 잠금, 데이터베이스)가 실패하거나 재시작합니다. 해당 리소스에 의존하던 모든 클라이언트가 동시에 재시도합니다. 이 폭주하는 트래픽은 복구된 시스템을 압도하며, 재시도 속도가 복구 속도를 초과하여 복구가 완료되지 못합니다.

패턴 2: 재시도 폭풍(Retry storm). 하위 서비스가 지연됩니다. 상위 호출자는 실패 대신 재시도를 수행합니다. 이로 인해 원래 부하가 배가됩니다. 느려진 서비스가 더 지연되어 추가적인 재시도를 유발합니다. 결국 부하가 정상 상태의 서비스도 감당할 수 없는 수준에 도달합니다.

패턴 3: 죽음의 큐(Queue of death). 백프레셔(backpressure)가 없는 처리 큐가 처리 속도보다 빠르게 데이터를 수신합니다. 큐가 무한정 증가합니다. 메모리가 고갈되고, 컨슈머가 충돌(crash)하며, 재시작 후에도 여전히 더 큰 큐를 발견하여 다시 충돌합니다.

공통된 요인: 작은 초기 교란이 양의 피드백 루프를 유발합니다. 시스템의 자체 반응이 장애를 감쇠시키는 대신 증폭시킵니다.

감쇠 메커니즘

지수적 백오프(Exponential backoff)와 지터(Jitter). 재시도하는 클라이언트는 매번 더 긴 시간을 기다리며, 무작위 오프셋을 추가합니다. 동기화된 재시도 웨이브를 방지합니다.

서킷 브레이커(Circuit breaker). 호출자가 다운스트림의 실패율을 추적합니다. 임계값을 초과하면, 호출자는 쿨다운 기간 동안 호출을 중단하고 대신 자신의 요청을 즉시 실패 처리합니다. 불필요한 작업을 방지하고 다운스트림의 회복을 돕습니다.

벌크헤드(Bulkhead). 의존성별로 리소스를 격리합니다. 데이터베이스용 연결 풀 A와 캐시용 별도 연결 풀 B를 사용합니다. 느린 데이터베이스가 모든 연결을 기아 상태로 만들지 않으며, 캐시 호출은 계속됩니다.

로드 셰딩(Load shedding). 과부하 상태에서는 요청을 받아들이고 느리게 실패하는 것보다 엣지(Edge)에서 요청을 폐기합니다. 1ms에 걸리는 429 응답이 30초에 걸리는 500 오류보다 낫습니다.

백프레셔(Backpressure). 소비자가 따라가지 못할 때 생산자를 늦춥니다. 큐가 유계(Bounded)가 되고, 송신자가 차단되며, 작업의 원래 소스에서 마찰을 느낍니다.

연쇄 장애: 트리거 -> 증폭 -> 붕괴, 감쇠 메커니즘 포함

연쇄 장애 진단

한 팀의 API 계층이 일상적인 데이터베이스 페일오버(Failover) 중에 마비됩니다. 타임라인:

- 14:00:00 — 운영자가 스탠바이 데이터베이스를 승격합니다. 예상 중단 시간: 약 10초.

- 14:00:08 — 프라이머리가 사용 불가능해집니다. API 계층의 요청이 데이터베이스 연결 오류로 실패하기 시작합니다.

- 14:00:08 — API 계층이 재시도합니다(기본 설정: 5회 재시도, 백오프 없음, 100ms 간격).

- 14:00:11 — 스탠바이가 승격되어 새 연결을 수락합니다.

- 14:00:11 — API 계층이 동시에 수천 개의 새 데이터베이스 연결을 엽니다(모든 리플리카 × 모든 동시 요청 × 모든 재시도).

- 14:00:13 — 새 프라이머리의 연결 풀이 소진되어 새 연결이 거부됩니다.

- 14:00:13-14:05:00 — API 계층 리플리카의 연결 풀이 소진되고, 예외가 발생하며, 크래시, 재시작, 반복이 일어납니다.

- 14:05:00 — 운영자가 수동으로 API 계층 트래픽을 중단합니다; 데이터베이스가 안정화됩니다.

- 14:10:00 — 점진적인 트래픽 복원이 완료되었습니다. 총 장애 시간: 약 10분 (예상치: 약 10초).

발생한 캐스케이드 패턴을 식별하고, 이를 방지했을 댐핑(감쇠) 메커니즘을 최소 두 가지 이상 나열하며, 본래 10초의 순간적인 장애가 의도되었던 프라이머리에서 스탠바이로의 페일오버가 왜 10분간의 장애로 이어졌는지 설명하십시오.

DNS SERVFAIL: 두 가지 결함의 복합적 영향

실제 형태를 본뜬 사후 분석(Postmortem)

아래는 실제 발생한 사고의 익명화 처리된 버전입니다. 벤더 이름은 변경되고 IP 주소는 익명화되었으나, 사고의 구조, 타임라인, 그리고 교훈은 실제와 동일합니다.

요약

example.com 사이트는 약 3~4시간 동안 모든 공개 DNS 리졸버로부터 SERVFAIL 응답을 반환했습니다. 동일한 DNS 마스터에 존재하는 다른 46개 존(zone)은 영향을 받지 않았습니다. 근본 원인: 두 가지 결함이 복합적으로 작용한 것입니다.

1. 벤더 A(세컨더리 DNS 제공업체)가 프라이머리의 allow-axfr-ips 허용 목록에 없는 새로운 내부 동기화 IP를 추가했습니다.

2. example.com 존에는 수년 전부터 존재해 온 RFC 위반 CNAME 충돌(demo.example.com 레이블에 CNAME과 동시에 MX/TXT 레코드가 존재)이 있었으며, 이로 인해 벤더 A가 새로운 AXFR 시 존을 거부했습니다.

타임라인 (UTC)

- ~15:00 — 벤더 A가 인프라에 새로운 동기화 IP 198.51.100.42를 추가함

- 15:02 — 198.51.100.42에 대한 첫 번째 AXFR-out denied가 프라이머리 DNS 로그에 기록됨 (이 시그널에 대한 알림 없음)

- ~18:00 — SOA 만료 기간 도달; 벤더 A가 example.com 존을 캐시에서 제거함

- ~18:30 — 외부에서 SERVFAIL 감지됨

- ~19:45 — 근본 원인 식별됨

- 20:00 — 198.51.100.42를 allow-axfr-ips에 추가; 프라이머리 재시작

- 20:05 — NOTIFY 전송; AXFR 시작; 존이 여전히 SERVFAIL 상태 (CNAME 충돌)

- 20:07 — check-zone 실행 결과 1개 오류 발견: demo.example.com의 CNAME 충돌

- 20:09 — CNAME 레코드를 A 레코드로 교체; 존(zone) 검사 결과 깨끗함 (오류 0건)

- 20:10 — NOTIFY 전송; AXFR 완료; 벤더 A가 존 서빙 시작

- 20:11 — dig @8.8.8.8 example.com A 명령이 올바른 IP를 반환 — 해결됨

왜 example.com만 영향받았는가?

모든 47개의 존(zone)은 동일한 DNS 프라이머리를 공유합니다. AXFR IP 차단이 모든 존에 영향을 미쳤습니다. 그러나 CNAME 충돌은 example.com에만 존재했으며, 거부(deny)가 적용된 시점에 새로운 AXFR이 필요한 것도 example.com뿐이었습니다. 다른 존들은 거부 조치 전에 이미 새로고침되었거나, 아직 새로고침이 필요하지 않은 상태였습니다.

잠재적 결함

demo.example.com의 CNAME 충돌은 수년간 존재해 왔습니다. 프라이머리가 데이터베이스에서 존을 서빙하여(RFC 위반에 관대하게 처리) & 벤더 A가 위반 사항이 도입되기 전의 오래된 캐시 데이터로 서빙하고 있었기 때문에 문제가 발생하지 않았습니다. 벤더 A가 캐시를 폐기하고 새로운 데이터가 필요해졌을 때, 해당 위반 사항이 표면화되었습니다.

트리거

벤더 A가 새로운 동기화 IP를 조용히 추가했습니다. 프라이머리의 허용 목록(allowlist)에는 해당 IP가 포함되어 있지 않았습니다. AXFR 요청이 거부되었습니다. 3시간 후(SOA 만료 시점)에 벤더 A가 존(zone)을 삭제했습니다. 시스템이 복구를 시도할 때 잠재적 결함이 드러났습니다.

비귀책(Blameless) 액션 아이템 작성하기

비귀책(Blameless): 사람을 아닌 시스템을 대상으로 하다

비귀책 액션 아이템은 사람이 다르게 해야 할 일을 지목하는 것이 아니라, 시스템이 다르게 작동해야 할 부분을 지목합니다. '운영자를 교육한다'는 것은 귀책(Blameful)입니다. '배포 전에 이를 포착하는 자동화 검증을 추가한다'는 것은 비귀책(Blameless)입니다.

좋은 비귀책 액션 아이템은 세 가지 차원으로 분류됩니다:

- 예방(Prevention): 나쁜 일이 일어나기 어렵게 또는 불가능하게 만듭니다

- 탐지(Detection): 발생 시 더 빨리 인지합니다

- 복구(Recovery): 발생 시 피해를 최소화합니다

각 항목은 (1) 구체적인 시스템 변경 사항, (2) 담당 팀, (3) 해당 차원을 명시해야 합니다.

위 DNS-SERVFAIL 사후 분석(postmortem)을 다루는 무과실(blameless) 조치 항목 3가지를 작성하세요. 예방 / 탐지 / 복구 차원에 걸쳐 분배하세요(차원당 1개). 각 항목은 구체적인 시스템 변경 사항과 담당 팀을 명시해야 합니다. 어떤 개인도 원인으로 지목하지 마세요.

선박과 함께 침몰하지 않는 격실

해군 공학에서 차용

선박에는 방수 격벽(watertight bulkheads)이 있습니다. 이는 선체를 격실로 나누는 수직 벽입니다. 한 격실이 침수되어도 선박은 침몰하지 않으며, 다른 격실이 고장 나도 나머지 부분에 영향을 주지 않습니다.

분산 시스템은 같은 단어와 같은 개념을 차용합니다.

격벽 패턴(Bulkhead pattern): 의존성별로 자원을 격리합니다. 세 개의 하위 API를 호출하는 서비스는 세 개의 독립적인 연결 풀(connection pool), 세 개의 독립적인 스레드 풀(thread pool), 세 개의 독립적인 재시도 예산(retry budget)을 사용합니다. 느리거나 실패하는 하위 시스템이 나머지 두 개에 할당된 자원을 소진할 수 없습니다.

격벽이 없는 경우: 하나의 느린 의존성이 공유 스레드 풀을 소진합니다. 다른 의존성에 대한 호출은 스레드를 기다리며 차단됩니다. 결과적으로 서비스 전체가 응답하지 않게 됩니다.

격벽이 있는 경우: 하나의 느린 의존성이 자신의 풀만 소진합니다. 해당 의존성에 대한 호출은 빠르게 실패(fail fast)합니다. 다른 의존성에 대한 호출은 정상적으로 계속됩니다. 폭발 반경(blast radius)은 실패한 의존성으로 제한됩니다.

서킷 브레이커

서킷 브레이커 패턴: 실패율을 추적하는 하위 의존성 주위의 상태 있는 래퍼입니다. 세 가지 상태가 있습니다:

- 닫힘 (Closed, 정상): 호출이 통과합니다. 실패가 카운트됩니다.

- 열림 (Open, 트리핑): 실패 임계값(예: 지난 30초 동안 50% 실패)을 초과하면 브레이커가 열립니다. 호출은 의존성을 시도하지 않고 즉시 실패합니다. 이는 호출자에게 불필요한 작업을 방지하고, 의존성이 비정상적인 상태에서 부하를 받는 것을 방지합니다.

- 반열림 (Half-open, 테스트): 쿨다운 기간이 지난 후, 브레이커는 소수의 호출만 통과시킵니다. 성공하면 정상 상태(닫힘)로 돌아갑니다. 실패하면 다시 열림 상태로 전환되어 또 다른 쿨다운을 거칩니다.

핵심 통찰: 서킷 브레이커는 알려진 비정상 기간 동안의 불필요한 노력을 방지하고, 하위 시스템이 지속적인 부하 없이 회복할 수 있는 기회를 제공합니다.

벌크헤드는 폭발 범위를 제한합니다. 서킷 브레이커는 폭발이 스스로를 지속하는 것을 방지합니다.

폭발 범위 제한

API 서비스가 네 개의 하위 서비스(User Service, Recommendation Service, Notification Service, 그리고 제3자 Payment API)를 호출합니다. 팀은 'Recommendation Service가 다소 불안정하다'는 이야기를 듣고, 해당 서비스가 실패할 때 시스템의 나머지 부분이 건강하게 유지되도록 하기를 원합니다.

오늘날 이 서비스는 200개의 스레드로 구성된 단일 공유 스레드 풀과 단일 공유 HTTP 연결 풀을 사용합니다. 모든 하위 4개 시스템이 이 리소스를 두고 경쟁합니다. 서킷 브레이커는 없습니다.

이 API 서비스를 위한 벌크헤드 + 서킷 브레이커 설계를 제안하세요. 구체적으로: 4개의 종속성 간에 스레드/연결 풀을 어떻게 분할할지, 불안정한 추천 서비스(Recommendation Service)에 적합한 서킷 브레이커 임계값은 무엇인지, 그리고 추천 서비스가 오픈-서킷(open-circuited) 상태일 때 사용자 대면 API가 어떻게 동작해야 하는지 설명해야 합니다.

실패 모드 리뷰 설계

종합

검사(SPOF)를 식별하고, 연쇄 장애 패턴을 인식하며, 사후 분석(postmortem)을 읽을 때 트리거와 잠재적 결함을 구분하고, 예방/탐지/복구 범위에 걸쳐 무과실(blameless) 액션 아이템을 작성하며, 벌크헤드 + 서킷 브레이커 + 정상적 기능 저하(graceful degradation)를 통해 폭발 반경(blast radius)을 제한하는 방법을 배웠습니다.

이 다섯 가지를 모두 적용해 보세요.

팀이 search.example.com이라는 새로운 서비스를 출시하고 있으며, 이 서비스는 세 개의 하위 서비스인 주 검색 인덱스(index.example.com), 분석 서비스(analytics.example.com), 추천 서비스(recs.example.com)에 의존합니다. 팀은 출시 전에 '실패 모드 리뷰'를 주도해 달라고 요청했습니다.

주도할 실패 모드 리뷰를 개요로 작성하세요. 다음을 포함해야 합니다: SPOF를 드러내는 방법(기법 1가지), 검색 서비스와 세 개의 하위 서비스 간의 연쇄 장애를 방지하는 방법(패턴 2가지), 팀이 가장 신뢰할 수 없다고 지적한 추천 서비스에 대한 구체적인 액션 아이템 1가지, 그리고 출시 시점에 갖춰져 있어야 할 모니터링.

이 코스의 다음 단계

이 코스의 다음 단계

이제 SPOF를 식별하고, 캐스케이드를 인식하며, 사후 분석을 생산적으로 읽고, 무과실 조치 항목을 작성하고, 설계로 블래스트 래디우스를 제한할 수 있습니다.

이 코스의 마지막 레슨(cs_distsys_observability_and_capacity)은 사용자가 문제를 인식하기 전에 문제가 발생하고 있음을 파악하기 위해 무엇을 측정해야 하는지를 가르칩니다. 헬스 체크, 버전 엔드포인트, 프록시 계층의 4가지 골든 시그널, 그리고 서지 용량 결정이 관찰된 데이터와 어떻게 연결되는지입니다.

동반 레슨: geometry_of_failure_modes_and_blast_radius는 betweenness centrality(그래프 노드 중 병목인 곳)와 min-cut(블래스트 래디우스의 한계)를 유도합니다.

수고하셨습니다. 다음으로.