Infra Architecture/Foundations

Foundations - 03. fault domain과 blast radius

장애는 한 점에서 시작해 한 묶음을 죽인다 — fault domain과 blast radius

2018년 10월 21일, GitHub는 미국 동부와 서부 리전 사이에 43초 동안 네트워크 분단이 생겼다. 짧은 시간이라 사용자는 거의 눈치채지 못했지만, 그 43초 동안 MySQL 동기화가 양쪽에서 동시에 진행되는 split-brain 상태가 만들어졌다. 양쪽 데이터베이스가 미세하게 다른 상태로 갱신되기 시작한 것이다. GitHub 팀이 이를 감지하고 복구 작업에 들어갔을 때, 단순히 "다시 연결"로는 끝나지 않았다 — 양쪽 상태의 불일치를 조율하는 데 24시간이 걸렸고, 그동안 GitHub 대부분의 서비스가 중지됐다. 사용자 3천5백만 명에게 영향을 미친 이 사건의 핵심은 "장애가 짧았다"가 아니라 — 두 리전이 하나의 일관성 경계 안에 있었다는 점이다. 43초의 분단이 24시간의 중단으로 변한 건, 분단 자체가 아니라 분단에 취약한 구조가 이미 깔려 있었기 때문이다.

이 사건이 보여주는 것은 장애의 원인이 아니라 장애의 확장성이다. 한 점에서 시작된 장애가 얼마나 넓은 범위까지 퍼지는가. 인프라 설계의 절반은 "장애를 안 일으키게" 하는 것이고, 나머지 절반은 — 어차피 장애는 일어나니까 — "장애가 퍼지는 범위를 어떻게 작게 묶어둘 것인가"를 다룬다. 그 두 번째 질문을 다루는 개념이 장애 도메인(fault domain)과 폭발 반경(blast radius)이다. 이 글은 두 개념이 무엇인지, 어디서 경계가 생기는지, 그리고 장애 도메인을 어떻게 작게 쪼개는 인프라를 설계하는지를 다룬다.

장애 도메인 — "같이 죽는 묶음"

장애 도메인(fault domain)의 정의는 단순하다. 같은 장애 원인에 함께 노출돼 동시에 장애가 나는 리소스들의 묶음. 같은 전원 탭에 꽂힌 컴퓨터 다섯 대는 하나의 장애 도메인이다 — 전원 탭이 죽으면 다섯 대가 동시에 죽는다. 같은 랙에 장착된 서버 40대는 하나의 장애 도메인이다 — 랙의 ToR(Top of Rack) 스위치가 죽으면 40대가 네트워크에서 잘린다. 같은 AZ에 배포된 클러스터는 하나의 장애 도메인이다 — AZ 단위 정전이나 네트워크 장애가 오면 전체가 죽는다.

이 정의가 중요한 이유는, 장애 도메인이 신뢰성의 가장 작은 단위이기 때문이다. 여러 개의 중복 컴포넌트가 있어도, 그 컴포넌트들이 같은 장애 도메인에 있으면 중복은 의미가 없다. foundations 02에서 본 병렬 가용성 공식 1 - (1-p1)(1-p2)은 "두 컴포넌트가 독립적으로 장애난다"는 가정이 전제된다. 독립적이라는 건, 두 컴포넌트가 다른 장애 도메인에 있다는 뜻이다. 같은 랙, 같은 AZ, 같은 전원에 있으면 독립성이 깨지고, 공식도 무너진다.

장애 도메인은 단일 개념이 아니라 계층이다. 같은 인프라를 여러 단위로 자를 수 있다.

flowchart TD
    R["리전<br/>지리적 분리 (지진·정전·홍수 방어)"] --> AZ1["AZ-A<br/>별도 전원·냉각·네트워크"]
    R --> AZ2["AZ-B<br/>별도 전원·냉각·네트워크"]
    AZ1 --> RACK1["랙 1<br/>동일 ToR 스위치, 동일 PDU"]
    AZ1 --> RACK2["랙 2<br/>동일 ToR 스위치, 동일 PDU"]
    RACK1 --> S1["서버 1~40<br/>동일 전원 레일"]
    RACK2 --> S2["서버 41~80<br/>동일 전원 레일"]

이 계층은 외부에서 내부로 갈수록 장애 원인이 달라진다. 리전은 지진·정전·통신 두절 같은 대규모 재해가 경계다. AZ는 정전·냉각 실패·네트워크 백본 장애가 경계다. 랙은 단일 스위치·단일 PDU(전원 분배 장치) 장애가 경계다. 서버 한 대는 전원 공급 장치(PSU)·디스크·팬 고장이 경계다. 좋은 인프라 설계는 각 계층마다 독립적인 중복을 두는 것이다 — 같은 랙에 두 서버, 같은 AZ에 두 랙, 같은 리전에 두 AZ, 같은 대륙에 두 리전.

역사 박스 — 2012년 10월 허리케인 샌디가 미국 동부를 강타했을 때, 맨해튼과 뉴저지의 데이터센터 다수가 침수·정전으로 다운됐다. 특히 Datagram의 33 Whitehall Street 데이터센터 침수로 HuffPo·Gawker·BuzzFeed 등이 함께 다운됐다. 같은 데이터센터에 있었다는 것, 즉 "같은 물리적 장애 도메인"이 발견된 것이다. 이 사건 이후 클라우드와 멀티 AZ 설계가 업계 표준으로 자리 잡았다.

blast radius — 장애가 미치는 반경

장애 도메인이 "어디까지 같이 죽을 수 있나"를 재는 단위라면, blast radius는 "실제 장애가 났을 때 어디까지 영향이 가나"를 재는 단위다. 용어 자체가 폭약에서 왔다 — 폭발 한 번에 반경 몇 m까지 파괴되는가. 인프라에서도 같은 질문이다. 장애가 한 점에서 시작됐을 때, 그 장애가 전파되는 범위가 시스템 전체의 몇 %인가.

AWS Well-Architected Framework의 안정성 기둥(Reliability Pillar)은 blast radius를 줄이는 것을 핵심 설계 원칙으로 든다. 핵심 통찰은 단순하다 — 장애는 일어난다. 중요한 건 장애 자체가 아니라 장애의 파급 범위다. 작은 blast radius를 가진 시스템은 장애가 빈번해도 견딜 수 있다. 큰 blast radius를 가진 시스템은 드문 장애 한 번에 전체가 무너진다.

이 개념을 서비스 설계에 번역하면 "장애 격리(fault isolation)"가 된다. 한 기능이 죽어도 다른 기능은 살아 있어야 한다. 한 사용자의 요청이 느려져도 다른 사용자의 요청은 빨라야 한다. 한 AZ가 통째로 날아가도 나머지 AZ가 서비스를 유지해야 한다. 이걸 가능하게 하는 유일한 방법은 — 장애 도메인을 잘게 쪼개는 것이다. blast radius는 장애 도메인 크기에 비례한다.

단일 장애점 (SPOF) — 한 점이 전체를 죽이는 구조

단일 장애점(SPOF, Single Point of Failure)은 가장 극단적인 형태의 장애 도메인이다. 한 컴포넌트가 죽으면 전체 시스템이 죽는 구조. 정의는 간단하지만, 현실의 SPOF는 발견하기 어렵다. "이거 죽으면 어떡하지?"라고 명시적으로 물어보기 전까지는 SPOF가 SPOF인 줄 모르는 경우가 대부분이다.

흔한 SPOF 카탈로그를 보자.

  • DNS — 단일 권한 DNS 서버가 죽으면, 그 도메인을 쓰는 모든 서비스가 이름 해석을 못 해 죽는다. DNS는 TTL(기본 1시간) 때문에 단기 캐싱으로 보호되지만, 캐시 만료 후엔 끝이다.
  • 로드 밸런서 — 단일 LB를 앞에 두면, LB가 죽을 때 뒤의 서버 100대가 다 살아도 서비스는 죽는다. LB 자체의 HA(active-active 또는 VRRP/keepalived)가 없으면 SPOF다.
  • 공유 DB — 단일 마스터 DB에 모든 서비스가 의존하면, DB가 죽을 때 전체 서비스가 죽는다. 읽기 복제본이 있어도 쓰기는 마스터 한 곳이 통째로 죽는다.
  • 공유 인증 서비스 — OAuth 제공자가 단일이면, 인증이 안 되어 모든 서비스 로그인이 실패한다.
  • 공유 설정 저장소 — etcd·Consul·ZooKeeper 같은 분산 조정(coordination) 서비스가 단일 클러스터면, 그 클러스터 장애 시 의존하는 모든 서비스가 설정을 못 읽어온다.
  • 공유 CI/CD — 단일 배포 시스템이 죽으면, 장애 복구에 필요한 핫픽스를 배포할 수 없다 — 2차 장애로 번진다.
  • 네트워크 게이트웨이 — 단일 NAT 게이트웨이, 단일 VPN concentrator, 단일 API 게이트웨이는 모두 SPOF.

SPOF의 교활한 점은 "논리적으로는 분산"처럼 보인다는 것이다. 데이터베이스를 마스터-복제본 구성으로 둬도, 쓰기 트래픽이 마스터 한 곳으로 몰리면 마스터는 SPOF다. 마이크로서비스로 쪼개도, 모든 서비스가 같은 DB를 공유하면 그 DB는 SPOF다. 이런 SPOF를 발견하려면 컴포넌트 다이어그램이 아니라 "이 컴포넌트가 죽으면 무엇이 죽나"라는 질문을 계속 던져야 한다.

컴포넌트 단일 SPOF 형태 해결 패턴
DNS 단일 권한 네임서버 멀티 DNS 제공자(Route 53 + Cloudflare), anycast
로드 밸런서 단일 LB 인스턴스 active-active LB, cross-zone LB
DB 마스터 단일 쓰기 노드 multi-AZ 마스터, multi-region active-active, 샤딩
인증 서비스 단일 OAuth 제공자 다중 IdP, 캐싱 토큰 (offline 검증)
설정 저장소 단일 etcd 클러스터 멀티 AZ etcd, quorum 기반
CI/CD 단일 배포 시스템 runbook 백업, 수동 배포 경로
NAT 게이트웨이 단일 NAT GW multi-AZ NAT GW

물리적 경계 — 랙, AZ, 리전

장애 도메인은 논리적인 경계로만 생기는 게 아니다. 물리적 경계가 더 강력한 장애 도메인이다. 물리적 경계를 이해하려면 데이터센터가 어떻게 구성돼 있는지부터 봐야 한다.

flowchart TD
    DC["데이터센터 건물"] --> PWR["전원 계층<br/>(PDU, UPS, 발전기)"]
    DC --> COOL["냉각 계층<br/>(HVAC, CRAC, 액체냉각)"]
    DC --> NET["네트워크 계층<br/>(ToR, aggregation, core 스위치)"]
    DC --> RACK["랙 계층<br/>(물리적 케이블, 서버 장착)"]
    PWR -. 같은 전원 레일에 있으면 같은 장애 도메인 .-> RACK
    NET -. 같은 ToR에 물리면 같은 장애 도메인 .-> RACK
    COOL -. 같은 냉각 구역이면 같은 장애 도메인 .-> RACK

랙(rack)은 데이터센터의 기본 단위다. 표준 랙은 서버 30~50대 정도를 수납한다. 한 랙은 보통 하나의 ToR(Top of Rack) 스위치와 하나 또는 두 개의 PDU(Power Distribution Unit)에 물려 있다. ToR가 죽으면 그 랙의 모든 서버가 네트워크에서 잘린다. PDU가 죽으면(또는 랙 단위의 전원 차단이 오면) 그 랙의 모든 서버가 꺼진다. 같은 랙에 두 서버를 중복 배치하는 건 거의 의미가 없다.

가용 영역(AZ, Availability Zone)은 클라우드가 정의한 독립된 데이터센터 단위다. AWS는 AZ를 "별도의 전원·냉각·네트워크를 갖춘 데이터센터 클러스터"로 정의한다. 하나의 리전 안에 보통 3 ~ 6개의 AZ가 있다. AZ 간에는 지연이 짧은(보통 1~2ms) 전용 광케이블로 연결되지만, 전원·냉각·네트워크는 완전히 분리돼 있다. 한 AZ의 정전이 다른 AZ에 영향을 주지 않아야 한다. 다만 한 도시 안에 있는 경우가 많아, 도시 단위 재해(홍수·지진)에는 함께 영향을 받을 수 있다.

리전(region)은 지리적으로 분리된 데이터센터 묶음이다. AWS의 us-east-1(버지니아), ap-northeast-2(서울), eu-west-1(아일랜드) 같은 단위. 리전 간에는 지연이 크고(수십~수백 ms) 비용도 든다. 대신 지진·홍수·전쟁·국가 단위 통제 같은 대규모 재해에 대한 방어가 된다.

각 경계가 방어하는 장애 유형이 다르다.

경계 방어하는 장애 유형 전형적 지연 전형적 비용
같은 서버(프로세스) 프로세스 크래시 0ms 무료
같은 랙, 다른 서버 서버 고장·PSU 고장·디스크 고장 0.5ms 낮음
같은 AZ, 다른 랙 ToR 고장·PDU 고장·랙 단위 정전 1ms 낮음
같은 리전, 다른 AZ AZ 정전·냉각 실패·화재 1 ~ 2ms 중간 (AZ 간 전송 비용)
다른 리전 도시 단위 재해·지진·국가 통제 20 ~ 200ms 높음 (리전 간 전송 + 데이터 중복)

인프라 설계의 기본 질문은 "이 장애 시나리오까지 방어해야 하나?"다. 결제 시스템은 보통 다른 리전까지 방어한다. 로그 수집은 한 리전 내 다중 AZ로 충분하다. 개발 환경은 한 AZ 안의 다중 서버만으로 충분할 수 있다. 정답이 하나가 아니라 서비스 요구사항(02에서 본 품질 속성 시나리오)에서 역산된다.

공유 자원이 장애 도메인을 키운다 — 분산 모놀리스

물리적 경계 외에 논리적 공유 자원도 강력한 장애 도메인이다. 이걸 놓치는 경우가 많다 — "서비스를 마이크로서비스로 쪼갰으니 이제 안전하겠지"라고 생각하지만, 서비스들이 같은 공유 자원에 의존하면 사실상 하나의 장애 도메인이다.

가장 흔한 예가 공유 데이터베이스다. 서비스 A, B, C가 각각 독립된 컨테이너로 도는데, 세 서비스가 모두 같은 PostgreSQL 클러스터에 의존한다면, DB 클러스터가 죽을 때 세 서비스가 동시에 죽는다. 서비스를 분리한 의미가 사라진다. 이 패턴을 분산 모놀리스(distributed monolith)라 부른다 — 물리적으로는 분산, 논리적으로는 결합.

같은 맥락의 공유 자원 목록은 길다.

  • 공유 DB — 가장 흔한 사례. 서비스별 DB 분리(microservice의 기본 원칙)로 해결.
  • 공유 캐시 — Redis 클러스터 하나에 모든 서비스가 의존하면, Redis가 느려질 때 모든 서비스가 동시에 느려진다. 2015년 Instagram이 Redis 장애로 전체 서비스가 느려진 사건이 대표적.
  • 공유 메시지 큐 — Kafka 클러스터 하나에 모든 서비스가 붙어 있으면, 클러스터 장애 시 모든 비동기 처리가 멈춘다.
  • 공유 서비스 디스커버리 — Consul·etcd·ZooKeeper 클러스터가 죽으면, 서비스들이 서로를 못 찾는다.
  • 공유 인증 — 단일 IdP(Identity Provider)가 죽으면, 모든 로그인이 실패한다.
  • 공유 DNS — 단일 DNS 제공자가 죽으면, 모든 도메인이 안 풀린다. 2021년 10월 Facebook(현 Meta)이 BGP 장애로 DNS가 안 풀려 6시간 다운된 사건이 대표적 — DNS 자체가 아니라 DNS를 알리는 BGP 경로가 사라진 게 원인이었다.
  • 공유 빌드/배포 — 단일 CI/CD 시스템이 죽으면, 장애 복구를 위한 핫픽스 배포도 못 한다.

이런 공유 자원이 있는 한, "서비스를 N개로 쪼갰다"는 건 환상이다. 장애 도메인은 N이 아니라 1이다. 진짜 장애 격리를 원하면 공유 자원 자체를 쪼개야 한다 — 서비스별 DB, 서비스별 캐시 네임스페이스, 서비스별 메시지 큐 파티션. 비용이 드는 작업이지만, 장애 도메인을 작게 만드는 유일한 길이다.

독립성 가정 — "진짜 독립"인가

중복의 효과는 "컴포넌트가 독립적으로 장애난다"는 가정에 의존한다. 그런데 현실의 중복은 생각보다 자주 가짜 독립이다. 중복 컴포넌트가 같은 원인에 동시에 장애나는 현상을 상관 장애(correlated failure)라 부른다. 이걸 간과하면, 가용성 계산은 종이 위의 숫자일 뿐 현실과 다르다.

가장 흔한 가짜 독립 사례를 보자.

같은 랙의 두 서버 — "2-way 중복이니까 안전하겠지"라고 생각했지만, 두 서버가 같은 랙, 같은 ToR 스위치에 물려 있으면 ToR가 죽을 때 두 서버가 함께 죽는다. 물리적 독립이 아니면 진짜 중복이 아니다. AWS EC2의 "placement group"이나 Kubernetes의 podAntiAffinity가 같은 물리적 호스트나 랙에 Pod가 몰리지 않게 분산하는 이유다.

같은 배치(batch)의 디스크 — 디스크를 한 번에 100개 사서 같은 랙에 장착하면, 그 배치의 제조 결함이 동시에 발현될 수 있다. CMU의 Schroeder-Gibson 연구(FAST 2007)는 디스크 고장이 독립적이지 않고 시간적으로 군집(cluster)한다는 걸 보였다 — 한 디스크가 고장 났을 때 이웃 디스크의 고장 확률이 올라간다. 3-way 복제도 같은 배치의 디스크들이면 한 번에 3개가 죽을 수 있다.

같은 AMI(이미지)로 띄운 인스턴스 — 같은 베이스 이미지에서 띄운 100대의 서버는, 그 이미지에 치명적 버그가 있으면 100대가 동시에 죽는다. "이미지 롤백"이라는 배포 전략이 있는 이유가 이거다 — 새 이미지를 띄우기 전에 카나리아(canary)로 일부만 검증한다.

같은 클라우드 계정의 모든 인스턴스 — 같은 AWS 계정의 모든 인스턴스는, 그 계정이 DDOS 공격이나 과금 한계로 정지될 때 함께 죽는다. 진짜 독립을 원하면 다른 계정(그리고 다른 클라우드 벤더)에 백업을 둬야 한다. 이게 "멀티 클라우드"가 단순 마케팅이 아니라 일부 서비스에겐 필수인 이유다.

같은 인증서 발급자 — TLS 인증서를 단일 CA에서만 발급받으면, 그 CA가 취소되거나 장애가 낄 때 모든 인증서 갱신이 실패한다. 2017년 Let's Encrypt의 OCSP 응답 장애로 수백만 사이트가 영향을 받은 사건이 있다.

이런 상관 장애를 피하려면 "독립"의 기준을 명확히 해야 한다.

중복 유형 진짜 독립? 이유
같은 서버의 두 프로세스 거짓 서버가 죽으면 둘 다 죽음
같은 랙의 두 서버 거짓 ToR/PDU 장애로 같이 죽음
같은 AZ, 다른 랙의 두 서버 부분 참 랙 단위 장애엔 견딤, AZ 장애엔 같이 죽음
다른 AZ의 두 서버 참 (AZ 장애 한정) AZ 정전·냉각 실패에 독립
다른 리전의 두 서버 도시 단위 재해에 독립
다른 벤더의 두 클라우드 완전 참 한 벤더가 통째로 문제여도 견딤

"어디까지 독립이 필요한가"는 다시 품질 속성 시나리오로 돌아간다. 결제 시스템은 다른 리전까지, 일반 사용자 서비스는 다른 AZ까지, 개발 환경은 같은 AZ 안에서 충분한 경우가 많다.

의존성 그래프 — 장애가 어떻게 퍼지나

장애 도메인이 정적 구조라면, 의존성 그래프(dependency graph)는 동적 전파 경로다. 한 컴포넌트의 장애가 다른 컴포넌트로 퍼지는 길을 보여준다. 이해하면 blast radius를 예측할 수 있고, 이해하지 못하면 장애가 났을 때 "왜 이것까지 죽었지?"라며 놀란다.

가장 흔한 전파 패턴을 보자.

동기 호출 체인 — 서비스 A가 서비스 B를 동기(HTTP) 호출하고, B가 C를 호출할 때, C가 느려지면 B가 응답을 기다리느라 스레드 풀을 고갈시킨다. B가 죽으면 A도 같은 이유로 죽는다. C의 장애가 A까지 전파된 것이다. 이걸 연쇄 장애(cascading failure)라 부른다. Google SRE Book은 연쇄 장애를 전용 챕터로 다룬다 — "장애가 시스템 전체로 퍼지는 가장 흔한 패턴"으로.

flowchart LR
    USER[사용자] --> A[서비스 A]
    A -->|동기 호출| B[서비스 B]
    B -->|동기 호출| C[서비스 C]
    C -. 지연 .- X[(DB 장애)]
    A -. 스레드 풀 고갈 .-> DEAD_A[서비스 A 사망]
    B -. 스레드 풀 고갈 .-> DEAD_B[서비스 B 사망]
    C -. 응답 불가 .-> DEAD_C[서비스 C 사망]

연쇄 장애를 막는 패턴들은 foundations 02의 회복탄력성에서 다시 자세히 다루지만, 핵심은 동기 호출 체인을 짧게 하고, 타임아웃·서킷 브레이커·벌크헤드로 의존성 전파를 끊는 것이다.

공유 인프라 의존 — 서비스가 DB·캐시·메시지 큐·DNS·인증 같은 공유 인프라에 의존할 때, 공유 인프라의 장애는 의존하는 모든 서비스로 전파된다. 특히 DNS는 기묘하게 자주 무시되는 의존성이다 — TTL 캐싱이 있어 단기엔 안 죽지만, TTL이 지난 후엔 모든 서비스가 동시에 이름 해석 실패로 죽는다. AWS Route 53·Cloudflare 같은 관리형 DNS조차 2017년·2023년에 장애를 겪었다.

메트릭·로그·모니터링 — 의존성 그래프에는 "가시성" 인프라도 포함된다. 모니터링 시스템이 죽으면, 장애가 나도 안 보인다 — 2차 재앙으로 번진다. Observability 시스템 자체가 또 다른 의존성이다. 그래서 SRE 팀은 모니터링 시스템을 프로덕션과 분리된 인프라에 두고, 그 자체로 다중화한다.

장애 도메인을 어떻게 쪼개나 — 설계 패턴

장애 도메인을 작게 쪼개는 건 인프라 설계의 절반을 차지하는 주제다. 주요 패턴을 짚고 가자.

셀 기반 아키텍처(cell-based architecture) — 전체 시스템을 독립적인 "셀"로 나누고, 각 셀이 자체 인프라(DB·캐시·네트워크)를 갖는다. 한 셀이 죽어도 다른 셀은 영향을 받지 않는다. AWS·Stripe·Slack 같은 대규모 서비스가 셀 기반 아키텍처로 전환한 사례가 있다. 단점은 — 셀마다 중복 비용이 든다. 1000만 사용자를 10개 셀로 나누면, 각 셀이 100만 사용자 규모의 인프라를 가져야 하므로 총비용이 늘어난다.

서비스별 DB 분리 — 마이크로서비스 원칙의 핵심이다. 각 서비스가 자체 DB(또는 최소 자체 스키마)를 가져야, 한 서비스의 DB 장애가 다른 서비스로 퍼지지 않는다. 데이터 일관성 문제는 saga 패턴·outbox 패턴으로 푼다 — 02에서 본 eventual consistency가 여기서 다시 등장한다.

shuffle-sharding — 고객마다 무작위로 선택된 인프라 조합에 할당하는 기법. AWS가 내부적으로 쓴다. 예를 들어 4개 AZ × 5개 DB = 20개 조합이 있으면, 고객 N명을 20개 조합에 무작위 분배한다. 한 조합이 죽어도 전체 고객의 5%만 영향. 단일 장애 도메인이 아니라 여러 장애 도메인으로 흩뿌리는 기법이다.

지연 로드와 비동기 호출 — 동기 호출을 줄이면 연쇄 장애 전파도 줄어든다. 서비스 간 통신을 메시지 큐(Kafka·RabbitMQ·SQS)로 비동기화하면, 한 서비스가 느려도 다른 서비스가 즉시 죽지 않는다. 큐에 적체되긴 하지만, blast radius가 훨씬 작다.

지역 분산(active-active vs pilot-light) — 멀티 리전의 두 가지 패턴. active-active는 두 리전이 모두 트래픽을 받는다 — 한 리전이 죽어도 다른 리전이 트래픽의 절반을 이미 처리하고 있어, DNS를 돌리기만 하면 된다. pilot-light는 한 리전(active)만 트래픽을 받고 다른 리전(standby)은 데이터만 복제해 둔다 — 장애 시 트래픽을 옮기는 데 시간이 걸린다. active-active가 blast radius를 작게 만들지만 비용이 2배. pilot-light는 비용이 적게 들지만 RTO(복구 시간 목표)가 길다.

패턴 blast radius 비용 복잡도 적용 사례
단일 인스턴스 최대 (전체) 최소 최소 개발 환경, 소규모 서비스
다중 인스턴스 (같은 AZ) 큼 (AZ 장애 시 전체) 낮음 낮음 일반 웹 서비스
다중 AZ 중간 (AZ 장애에 견딤) 중간 중간 상용 서비스 표준
다중 리전 active-active 작음 높음 (2배) 높음 결제, 글로벌 서비스
셀 기반 아키텍처 매우 작음 매우 높음 매우 높음 AWS, Stripe, Slack 규모
Shuffle-sharding 매우 작음 높음 높음 AWS 내부 서비스

chaos engineering — 장애 도메인을 테스트하는 방법

장애 도메인을 설계했다고 끝이 아니다 — 실제로 장애를 내보면서 설계가 맞는지 검증해야 한다. 이걸 체계적으로 하는 게 chaos engineering이다. foundations 01에서 잠깐 언급했지만, 여기서는 fault domain 관점에서 다시 본다.

넷플릭스는 2011년 "Simian Army"라는 도구 모음을 공개했다. 그중 chaos engineering과 관련된 것들이 있다.

  • Chaos Monkey — 매일 영업 시간에 무작위 프로덕션 인스턴스를 죽인다. 단일 인스턴스 장애에 서비스가 견디는지 검증.
  • Chaos Gorilla — 무작위 AZ 전체를 죽인다. AZ 단위 장애 도메인 설계가 맞는지 검증.
  • Chaos Kong — 무작위 리전 전체를 죽인다. 리전 단위 장애 도메인 설계(멀티 리전 active-active)가 맞는지 검증.

이 도구들의 핵심 철학은 — 장애는 일어난다. 가정은 틀릴 수 있다. 검증하지 않으면 장애 도메인 설계도 종이 위의 그림일 뿐이다. chaos engineering은 가정을 실험으로 바꾼다. "우리 서비스는 AZ 한 개가 죽어도 견딜 거야"라는 가정을 Chaos Gorilla로 테스트해서, 실제로 견디는지 확인한다. 안 견디면 — 버그이고, 진짜 장애가 오기 전에 고친다.

모든 팀이 Netflix 규모의 chaos engineering을 할 수 있는 건 아니다. 하지만 작게 시작할 수 있다. 단일 서버 재시작, 단일 컨테이너 죽이기, 네트워크 지연 주입(테스트 환경에서), DNS 일시 정지 — 이 정도의 "게임데이(game day)" 훈련만으로도 장애 도메인 가정의 허점을 발견할 수 있다. SRE 팀이 정기적으로 게임데이를 하는 이유다.

실습 — 장애 도메인 직접 확인하기

이 글의 개념을 직접 확인하는 기본 명령들이다. 단일 리눅스 서버에서 실행 가능한 것부터 Kubernetes 클러스터에서 실행 가능한 것까지 있다.

1. 시스템의 부팅 의존성 그래프 보기

# systemd 부팅 시 의존성 트리를 본다. 한 유닛이 죽었을 때 무엇이 같이 죽는지 보는 기본 도구.
systemd-analyze critical-chain

확인할 것: 부팅 시간의 대부분을 차지하는 "임계 경로(critical chain)". 한 유닛이 느려지면 전체 부팅이 느려지는 — blast radius를 가진 의존성을 식별.

# 예상 출력 (Ubuntu 24.04)
graphical.target @4.201s
└─multi-user.target @4.201s
  └─nginx.service @4.198s +12ms
    └─network-online.target @4.105s
      └─network.target @4.098s
        └─systemd-networkd-wait-online.service @2.103s +1.995s

2. Kubernetes Pod 분산 확인 (클러스터가 있는 경우)

# Pod가 여러 노드에 분산돼 있는지 확인. 한 노드에 몰려 있으면 blast radius가 크다.
kubectl get pods -o wide -n <namespace>

확인할 것: NODE 열. 같은 디플로이먼트의 Pod들이 서로 다른 노드에 분산돼야 blast radius가 작다.

# 예상 출력 (3 replicas, 다중 노드 분산)
NAME                     READY   STATUS    NODE              AGE
app-7b4f9c6d4-abc12      1/1     Running   node-us-east-1a   5m
app-7b4f9c6d4-def34      1/1     Running   node-us-east-1b   5m
app-7b4f9c6d4-ghi56      1/1     Running   node-us-east-1c   5m
# Pod 안티어피니티가 설정돼 있는지 확인 (같은 노드에 안 몰리게 강제).
kubectl get pod -n <namespace> -o jsonpath='{.items[0].spec.affinity}' | jq .

확인할 것: podAntiAffinity 필드가 있어야 같은 노드에 Pod가 몰리지 않는다. 이게 없으면 스케줄러 마음대로 배치 — 한 노드가 죽을 때 Pod N개가 같이 죽을 수 있다.

3. 데이터베이스 복제 토폴로지 (PostgreSQL 예시)

# PostgreSQL의 복제 상태를 본다. 스탠바이가 몇 개인지, 어디에 있는지.
sudo -u postgres psql -c "SELECT application_name, client_addr, state, sync_state FROM pg_stat_replication;"

확인할 것: state = streaming, sync_state = sync(동기) 또는 async(비동기). 동기 복제는 마스터 장애 시 데이터 손실 0을 보장하지만 느리고, 비동기는 빠르지만 데이터 손실 가능. 스탠바이가 같은 랙에 있으면 의미 없다 — AZ 분산이 돼 있는지 확인해야 한다.

# 예상 출력 (2개 standby, 다른 AZ)
 application_name |  client_addr   |   state   | sync_state
------------------+----------------+-----------+------------
 walreceiver_1    | 10.0.1.12      | streaming | sync
 walreceiver_2    | 10.0.2.45      | streaming | async

4. DNS 의존성 — 단일 장애점 테스트

# 현재 사용 중인 DNS 서버 확인. 단일 서버이면 SPOF.
cat /etc/resolv.conf
resolvectl status 2>/dev/null | head -10

확인할 것: nameserver 행이 하나뿐이면 SPOF. 둘 이상이고 서로 다른 네트워크 대역이어야 진짜 다중화.

# 단일 SPOF 예시 (위험)
nameserver 10.0.0.2

# 다중화 예시 (안전)
nameserver 10.0.0.2
nameserver 10.0.1.2
nameserver 1.1.1.1

5. 장애 주입 테스트 (테스트 환경에서만)

# chaos-mesh나 litmus chaos 같은 도구가 없으면, 단순히 프로세스를 죽여본다.
# nginx 프로세스를 죽이고 얼마나 빨리 복구되는지 본다.
sudo systemctl kill nginx
sleep 30
sudo systemctl status nginx

확인할 것: systemd가 Restart=always 또는 Restart=on-failure로 설정돼 있으면 자동 복구. 아니면 죽은 채로 있음 — blast radius가 "수동 개입 필요" 단계까지 간다는 뜻. 복구 시간(MTTR)을 재는 간단한 게임데이.

미검증: 위 출력의 정확한 형태는 환경·버전에 따라 다르다. Kubernetes 명령은 클러스터가 있어야 하고, PostgreSQL 명령은 postgres 계정 권한이 필요하다. 단일 Linux VM만으로는 systemd-analyze/etc/resolv.conf 점검 정도가 현실적이다.

SPOF 감지 체크리스트 — 코드와 다이어그램에서 찾는 법

인프라 코드를 보고 SPOF를 찾는 기본 체크리스트다. Terraform, Kubernetes manifest, 시스템 아키텍처 다이어그램에서 점검할 수 있는 항목들이다.

Terraform / IaC 관점:

  • count = 1인 리소스가 있나? (단일 EC2, 단일 RDS, 단일 NLB) — SPOF 후보
  • 리소스에 multi_az = true가 빠져 있나? (RDS, ELB)
  • 단일 NAT Gateway인가? — multi-AZ 배포 시 AZ 장애에 죽음
  • 단일 ElasticIP에 의존하나?
  • 단일 Route 53 hosted zone인가? — DNS 장애 시 전체 도메인 영향

Kubernetes manifest 관점:

  • replicas: 1인 Deployment가 있나? — SPOF
  • podAntiAffinity가 설정돼 있나? — 같은 노드에 Pod가 안 몰리게
  • 단일 Deployment가 같은 Service를 지원하는데 topologySpreadConstraints가 없나? — AZ 분산 보장 안 됨
  • StatefulSet이 단일 볼륨(PV)에 의존하나? — AZ 장애 시 볼륨이 따라 죽을 수 있음
  • Ingress Controller가 단일 Pod인가? — 클러스터 입구가 SPOF

아키텍처 다이어그램 관점:

  • 모든 박스를 한 번씩 "이게 죽으면?" 이라는 질문으로 스캔했나?
  • 단일 화살표가 들어가는 박스가 있나? (단일 의존 = SPOF)
  • "공유"라고 표시된 컴포넌트가 있나? (공유 DB, 공유 캐시 — 분산 모놀리스 후보)
  • AZ 장애·리전 장애 시나리오를 그려봤나?
  • DNS·인증·설정 저장소 같은 "숨겨진 의존성"이 다이어그램에 있는가?

운영 관점:

  • 장애 복구에 필요한 CI/CD 시스템이 프로덕션과 같은 장애 도메인에 있나? — 2차 장애 위험
  • 모니터링 시스템이 프로덕션과 같은 장애 도메인에 있나? — 장애 가시성 상실
  • On-call 엔지니어가 접근할 수 있는 "백업 접근 경로"가 있나? — 주 접근 경로가 죽었을 때 대안

판단 표 — 장애 도메인 설계 결정

장애 도메인을 어디까지 쪼갤지 결정할 때 참고하는 기준이다. 정답이 하나가 아니라 서비스 요구사항(02의 품질 속성 시나리오)에서 역산된다.

서비스 특성 권장 장애 도메인 크기 이유
개발/스테이징 환경 단일 AZ, 단일 인스턴스 비용 효율, 다운돼도 영향 작음
일반 웹 서비스 (낮은 SLO) 다중 인스턴스, 단일 AZ 장애 빈도 낮고 RTO 여유
일반 웹 서비스 (99.9% SLO) 다중 인스턴스, 다중 AZ AZ 장애 허용
결제·인증 시스템 다중 AZ, 다중 리전 active-active RTO 0에 가까운 목표
규제 대상 (금융·의료) 다중 리전 + 규제별 별도 셀 규제 요구사항 + 재해 복구
게임 백엔드 셀 기반 아키텍처 (게임별 격리) 한 게임 장애가 다른 게임으로 안 퍼짐
글로벌 B2B SaaS shuffle-sharding + 다중 리전 고객별 격리 + 지리적 분산

fault domain이 곧 신뢰성의 단위

도입의 GitHub 사건으로 돌아가자. 2018년 그 장애의 본질은 "네트워크 분단"이 아니라 — 두 리전이 MySQL 일관성이라는 하나의 장애 도메인 안에 있었다는 점이다. 분단은 트리거였을 뿐이다. 진짜 문제는 두 리전의 논리적 결합이었다. GitHub 팀은 postmortem에서 이를 명시하고, 복구 뒤 MySQL 토폴로지를 재설계했다 — failover 절차를 단순화하고, 리전 간 복제 방식을 재검토했다. 24시간의 장애가 43초 분단보다 훨씬 길었던 이유는, fault domain이 너무 컸기 때문이다.

AWS CTO Werner Vogels는 "Everything fails all the time"(모든 것은 항상 실패한다)이라고 자주 말한다. 이 말을 비관주의로 듣지 않으려면, 그 다음 문장까지 들어야 한다 — "따라서 설계는 failure를 전제로 해야 한다." failure를 전제로 한 설계의 핵심이, blast radius를 작게 만드는 것이다. 장애는 피할 수 없다. 다만 한 점에서 시작된 장애가 한 묶음만 죽이고 끝나게 만들 수는 있다. 그게 fault domain을 다루는 이유다.

다음 글(04-redundancy-ha.md)은 이 fault domain을 바탕으로 중복(redundancy)과 고가용성(HA)의 구체 패턴을 다룬다 — active-active, N+1, quorum 같은 패턴이 어떻게 장애 도메인 안에서 작동하는지. 그 다음 글에서 보겠지만, redundancy는 "얼마나"뿐 아니라 "어디에"가 중요하다 — 같은 장애 도메인 안의 중복은 중복이 아니다.


참고

  • GitHub incident report "October 21 post-incident analysis", GitHub Blog, 2018-10-22 — MySQL split-brain 24시간 장애의 공식 사고 보고서. 접근 2026-07-21. URL: https://github.blog/2018-10-30-oct21-post-incident-analysis/
  • Schroeder, B., Gibson, G. A. "Disk Failures in the Real World: What Does an MTTF of 1,000,000 Hours Mean to You?", FAST 2007 — 디스크 고장의 비독립성(시간적 군집) 연구. 접근 2026-07-21
  • Beyer, B., Jones, C., Petoff, J., Murphy, N. R. (eds.) , O'Reilly, 2016, Ch.6·Ch.11 — cascading failure, distributed systems. 무료 공개. 접근 2026-07-21
  • AWS Well-Architected Framework, Reliability Pillar — fault isolation, blast radius, 단일 장애점. 접근 2026-07-21. URL: https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/
  • Vogels, W. "A Conversation with Werner Vogels", ACM Queue, 2015 — "Everything fails all the time" 명언의 맥락. 접근 2026-07-21
  • Netflix Tech Blog, "The Netflix Simian Army", 2011-07 — Chaos Monkey·Chaos Gorilla·Chaos Kong 공개. 접근 2026-07-21. URL: https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116
  • Hollander, S. "Distributed monolith" — 마이크로서비스를 쪼갰지만 공유 자원으로 결합된 패턴. martinfowler.com 용어 참조. 접근 2026-07-21
  • Facebook Inc. "October 4 Outage Update", 2021-10-05 — BGP 장애로 DNS가 안 풀려 6시간 다운된 사건. 접근 2026-07-21. URL: https://engineering.fb.com/2021/10/04/networking-traffic/outage/
  • Kleppmann, M. , O'Reilly, 2017, Ch.5·Ch.6 — 복제·파티셔닝에서 독립성 가정의 함의. 접근 2026-07-21