Rain Lag

아날로그 장애 스토리 도미노 정원: 작은 ‘종이 실패’를 세워놓고 무엇이 진짜 붕괴를 시작하는지 보기

아날로그 ‘도미노 정원’, 더미 부하, 부분 시뮬레이터를 활용해 복잡한 시스템에서 연쇄 장애를 촉발하는 진짜 단일 장애 지점을 찾아내는 방법을 다룹니다.

아날로그 장애 스토리 도미노 정원: 작은 ‘종이 실패’를 세워놓고 무엇이 진짜 붕괴를 시작하는지 보기

복잡한 시스템은 좀처럼 단순한 방식으로 망가지지 않습니다.

분산 서비스에서 과부하가 걸린 전력망에 이르기까지, 대형 장애는 종종 아주 사소한 것에서 시작됩니다. 느려진 의존성, 잘못 설정된 재시도 정책, 모두가 공유되는지 몰랐던 단일 핫 패스 등. 장애 알림이 울릴 때쯤이면, 이미 전체 시스템에 스파이크가 발생해 실제로 얼마나 강하게 결합되어 있었는지가 드러난 뒤인 경우가 많습니다.

여기서 **아날로그 장애 스토리 도미노 정원(Analog Incident Story Domino Garden)**이라는 아이디어가 등장합니다. 프로덕션이 시스템이 어떻게 망가지는지 “직접” 보여줄 때까지 기다리는 대신, 아키텍처를 저렴한 물리/아날로그 “도미노” 시뮬레이션으로 만들어봅니다. 그리고 거기에 수많은 **작은 종이 실패(paper failures)**를 세워놓고, 무엇이 실제로 붕괴를 시작하는지 지켜봅니다.

이 글에서는 다음과 같은 내용을 다룹니다.

  • 아날로그·물리 모델로 연쇄 장애를 탐색하는 방법
  • 수학적 모델과 시뮬레이터 스타일 실험을 결합하는 방법
  • 진짜 단일 장애 지점을 드러내는 “도미노 정원”과 더미 부하를 설계하는 방법
  • L1/L2/L3 영향도를 사용해 블라스트 레이디언스(영향 범위)를 시각화하는 방법
  • 실제 코드 기반 툴링으로 진짜 의존성 그래프를 매핑하는 방법

단일 장애 지점은 ‘과부하’ 속에 숨어 있다

시스템은 보통 정상 부하에서는 멀쩡해 보입니다. 다이어그램은 단정하고, 컴포넌트들은 느슨하게 결합된 것처럼 보이며, 모두가 아키텍처 문서를 보며 고개를 끄덕입니다. 하지만 과부하 상태가 되면 모든 것이 달라집니다.

  • 백그라운드 잡이 같은 데이터베이스를 놓고 경쟁하기 시작합니다.
  • “옵셔널”이라고 여겨졌던 의존성이 동기 호출 때문에 사실상 필수 컴포넌트가 됩니다.
  • 재시도와 타임아웃이 특정 패턴으로 겹치면서, 일종의 ‘떼 지어 몰리는(thundering herd)’ 현상이 발생합니다.

어느 한 컴포넌트가 임계점을 넘는 순간, 갑작스러운 시스템 전반의 스파이크가 관측될 수 있습니다.

  • 관련 없어 보이는 서비스들 전체에서 레이턴시가 폭증합니다.
  • 문제 서비스에 직접적으로는 의존하지 않는 엔드포인트에서도 에러율이 치솟습니다.
  • CPU, 메모리, I/O 같은 자원 사용량이 전혀 예상치 못한 지점에서 튀어 오릅니다.

이런 패턴은 **숨겨진 결합(hidden coupling)**이 있다는 신호입니다. 문서 상 아키텍처는 “loosely coupled(느슨한 결합)”이라고 말하지만, 런타임 동작은 전혀 다른 이야기를 들려줍니다. 문제는 프로덕션이 대신 보여주기 전에, 이 숨겨진 연결 고리들을 미리 찾아내는 것입니다.


왜 ‘도미노 정원’을 장애 분석에 쓰는가?

**도미노 정원(domino garden)**은 다음과 같은 방식으로 구성하는 실험 환경입니다.

  1. 컴포넌트를 물리적·아날로그 “도미노”(카드, 블록, 시뮬레이션된 노드 등)로 표현합니다.
  2. 각 도미노가 언제, 어떤 조건에서 다른 도미노를 넘어뜨릴 수 있는지 규칙을 부여합니다.
  3. 도미노 배치를 바꾸고, 다양한 시작점을 눕혀보며 어떤 배치에서 연쇄 붕괴가 일어나는지 실험합니다.

엔지니어링 관점에서 다시 말하면, 다음을 의미합니다.

  • 서비스와 의존성을 모델링합니다.
  • 작고 비용이 싼 장애(바로 이 **“종이 실패”**들)를 주입합니다.
  • 어떤 실패가 연쇄 영향으로 이어지는지 관찰합니다.

목표는 완벽한 ‘디지털 트윈’을 만드는 것이 아닙니다. 대신, 다음을 알고 싶습니다.

  • 어느 단일 실패가 가장 큰 연쇄 반응을 일으키는지
  • 어떤 인접 컴포넌트들이 장애를 증폭시키는지
  • 백프레셔, 재시도, 공유 인프라가 상황을 어떻게 더 악화시키는지

이런 아날로그적 사고가 강력한 이유는, 머릿속에만 있던 멘탈 모델을 바깥으로 끄집어내도록 강제하기 때문입니다. 도미노들이 탁자 위에(물리적으로든, 비주얼 도구 속이든) 올라가는 순간, 그동안 암묵적이었던 가정, 서로 다른 이해관계자들의 관점 차이가 빠르게 드러납니다.


더미 부하와 제약된 시뮬레이션: 안전한 실패 탐침

실제 연쇄 장애를 프로덕션에서 그대로 재현하고 싶을 일은 거의 없습니다. 우리가 원하는 것은 “충분히”만 건드려서, 시스템의 약한 고리를 알아내는 것입니다.

여기서 핵심 도구 두 가지가 있습니다.

1. 더미 부하(Dummy Load)

**더미 부하(dummy load)**는 실제 트래픽이나 실제 작업을 대신하는 대역입니다.

  • 특정 엔드포인트 하나만 일정한 패턴으로 두드리는 가짜 클라이언트
  • 특정 데이터베이스 파티션만 집중적으로 압박하는 합성 워크로드
  • 느린 응답 혹은 특정 에러 코드를 반환하도록 설계된 스텁 서비스

더미 부하는 시스템의 한 부분이 부하를 **흡수하는지, 증폭하는지(‘임피던스’ 관점)**를 전체를 터뜨리지 않고도 관찰할 수 있게 해 줍니다.

예를 들면 다음과 같습니다.

  • 단일 의존성에서 느린 응답을 일정량만 흘려보내고, 호출하는 쪽이 부분적 성능 저하를 어떻게 처리하는지 관찰합니다.
  • 특정 노드에 적당한 수준의 CPU 스파이크를 인위적으로 만들고, 오토스케일러가 어떻게 반응하는지 봅니다.

2. 제약된 시뮬레이션(Constrained Simulation)

**제약된 시뮬레이션(constrained simulation)**은 장애가 퍼져나갈 수 있는 범위를 의도적으로 제한하는 방식의 실험입니다.

  • 스테이징 환경에서 일부 서비스만 띄워 놓고 테스트합니다.
  • 기능 플래그나 라우팅 규칙으로 특정 경로를 아예 막아둡니다.
  • 위험한 의존성은 안전한 목(mock)·스텁으로 교체합니다.

목표는 전체 시스템을 똑같이 복제하는 것이 아닙니다. 대신, 다음과 같은 구체적인 질문에 답하려는 것입니다.

  • “서비스 A는 의존성 B의 레이턴시가 100ms 늘어나면 어떻게 반응하는가?”
  • “이 경로에서 재시도가 어느 지점부터 ‘도움’이 아니라 ‘해악’이 되는가?”

이게 바로 **종이 실패(paper failures)**입니다. 싸게 만들고, 쉽게 되돌릴 수 있으며, 진짜 도미노들이 어디 서 있는지 밝혀주는 실험들입니다.


하이브리드 접근: 수학 + 아날로그 + 코드

많은 엔지니어링 문제는 애매한 중간 지점에 놓여 있습니다.

  • 깔끔한 닫힌 형태의 수학 모델로는 다 표현하기 어렵고,
  • 프로덕션과 똑같은 규모로 완전 시뮬레이션하기엔 너무 크고 위험합니다.

그래서 하이브리드 접근법이 가장 잘 작동합니다.

  1. 수학적 모델링으로 핵심 동역학을 잡습니다.

    • 큐잉 이론으로 요청 백로그를 모델링
    • 리소스 소비/회복을 단순 미분방정식으로 근사
    • 에러율과 재시도 패턴에 대한 확률 모델
  2. 아날로그·물리 모델로 구조와 직관을 얻습니다.

    • 종이·화이트보드 위 도미노 배치
    • 카드 기반 의존 관계 매핑 워크숍
    • 임계값이 연쇄적으로 넘어가는 상황을 단순 스프레드시트로 시뮬레이션
  3. 코드 레벨 시뮬레이터로, 정말 중요한 부분에 현실감을 더합니다.

    • 주요 핫 패스를 둘러싼 부분 테스트 하니스
    • 핵심 서비스 몇 개만 컨테이너·로컬 환경에 띄우는 미니 클러스터
    • 네트워크 파티션, 느린 디스크, 타임아웃 등을 흉내 내는 장애 주입(fault injection) 도구

여기서 핵심은 “올 오어 낫싱(all or nothing)” 리얼리즘의 함정을 피하는 것입니다. 이런 질문에 답하려고 프로덕션 전체 복제본이 필요하지는 않습니다.

“어떤 단일 컴포넌트 장애가 가장 중요한 사용자 여정을 가장 많이 망가뜨리는가?”

필요한 것은 **진짜 중요한 지점에만 정밀함을 몰아주는 것(타깃형 정밀도)**입니다.


단순화된·부분 시뮬레이터의 힘

시스템의 일부만 다루는 시뮬레이터도, 그 일부가 장애를 시작하거나 증폭할 가능성이 높은 핵심 동작에 집중한다면 상당히 유용할 수 있습니다.

좋은 ‘부분 시뮬레이터’는 보통 이런 특성을 가집니다.

  • 범위가 명확히 제한됨: 예를 들어 메인 결제 플로우만, 혹은 핵심 검색 경로만 다룸
  • 높은 설정 가능성: 레이턴시, 에러율, 용량을 쉽게 올리고 내릴 수 있음
  • 빠른 피드백: 실행 시간이 수 초~수 분 수준 (몇 시간씩 걸리지 않음)

이런 시뮬레이터로 배울 수 있는 것들:

  • 재시도 폭풍이 어디서 시작되는지
  • 어떤 재시도는 안전하고, 어떤 재시도는 파괴적인지
  • 백프레셔가 언제 동작하기 시작하는지, 그리고 의도대로 동작하는지

당신의 “도미노 정원” 안에서 이런 시뮬레이터는, 전체 필드를 다시 깔 필요 없이 조밀하게 모인 도미노 구간 몇 개만 떼어내 자유롭게 재배치하고 스트레스 테스트하는 구역 역할을 합니다.


블라스트 레이디언스 시각화: L1, L2, L3

위험한 도미노를 하나 찾아냈다고 해도, 그 중요성을 팀에 설득력 있게 전달하기란 쉽지 않습니다. 이때 유용한 것이 바로 블라스트 레이디언스(blast radius, 영향 범위) 시각화입니다.

실용적인 방법 중 하나는 영향도를 레벨로 나눠 라벨링하는 것입니다.

  • L1 (Local / 국소): 영향이 실패한 컴포넌트와 그 즉시 호출자 범위 안에만 머무는 경우
  • L2 (Adjacent / 인접): 영향이 관련 서비스나 일부 사용자 여정까지 번지지만, 시스템 전체 성능이 심각하게 떨어지는 수준은 아님
  • L3 (Systemic / 전사적): 시스템 전체에 광범위한 영향 – 대규모 장애, 비즈니스 핵심 기능 중단

이를 시각적으로 표현할 수 있습니다.

  • 서비스를 노드로 그린 뒤, 특정 장애 시나리오에서 받는 영향 레벨(L1/L2/L3)에 따라 색을 다르게 칠합니다.
  • 주요 사용자 여정을 오버레이하고, 실패 경로와 교차하는 지점을 강조합니다.
  • “이 엣지에서 L1 장애가 발생하면 30초 안에 L3로 확대”처럼 위험도가 높은 엣지에 주석을 답니다.

이렇게 하면 추상적인 문장인

“캐싱 레이어 X가 다운되면, 몇몇 내부 API의 성능이 저하됩니다.”

가 다음처럼 구체적인 그림으로 바뀝니다.

“X가 실패하면, 백프레셔 Y가 제때 동작하지 않을 경우 2분 안에 체크아웃 플로우에서 L3 장애가 발생합니다.”

이제 팀은 이론상의 심각도가 아니라 실제 블라스트 레이디언스를 기준으로 개선 우선순위를 정할 수 있습니다.


실제 코드와 실제 그래프를 이해하는 툴링

지금까지 이야기한 것들은, 사용하는 도구가 아키텍처 다이어그램에 그려진 이상적인 모습이 아니라, 코드가 실제로 얽혀 있는 그대로의 시스템을 볼 수 있을 때 훨씬 쉬워집니다.

좋은 의존성 매핑·시뮬레이션 도구는 다음을 지원해야 합니다.

  • 리포지토리 전반에 걸쳐 실제 import 경로를 파싱할 수 있어야 합니다.
  • 백엔드, 프론트엔드, 스크립트 등 여러 언어를 이해해야 합니다.
  • 배럴 파일(barrel file), 인디렉션 레이어 같은 우회 레이어가 있어도 거기서 멈추지 않고 추적을 이어갈 수 있어야 합니다.

도구가 다음과 같은 사실을 알고 있을 때를 상상해 봅시다.

  • 서비스 A는 라이브러리 B에 의존하고,
  • 라이브러리 B는 배럴 파일 뒤에 숨겨진 클라이언트 C를 사용하며,
  • 클라이언트 C는 실제로 서비스 D와 E를 호출한다.

이런 맥락이 잡혀 있을 때, 당신의 “도미노 정원”은 추측이 아니라 현실에 근거한 모델이 됩니다.

이를 바탕으로 다음과 같은 실험을 할 수 있습니다.

  • “이 공용 라이브러리에 닿는 모든 L2/L3 도미노를 보여줘.”
  • “이 내부 API 레이턴시가 50% 늘어날 때의 영향을 시뮬레이션하고, 그래프로 시각화해줘.”

이런 정밀도가 없다면, 실제 그래프와는 닮기만 한 골판지 모형을 밀고 쓰러뜨리는 것에 불과합니다.


전체 그림: 어떻게 엮어서 활용할 것인가

아날로그 장애 스토리 도미노 정원의 목적은 완벽한 시스템 모델을 만드는 것이 아닙니다. 그 목적은 다음과 같습니다.

  • 숨겨진 결합 구조를 눈에 보이게 만들기
  • **종이 실패(paper failures)**와 **더미 부하(dummy loads)**로 위험 지점을 안전하게 탐침하기
  • 수학, 아날로그 모델링, 코드 레벨 시뮬레이션을 조합해 실용적인 인사이트를 얻기
  • 가장 중요한, 연쇄 반응의 첫 몇 개 도미노에 정밀도를 집중하기
  • L1/L2/L3 블라스트 레이디언스로 리스크를 명확하게 소통하기
  • 이 모든 것을 실제 코드 기반 의존성 그래프에 단단히 연결하기

장애를 ‘스토리’라고 생각해 보면, 우리의 역할은 마지막 챕터(장애 해결)만 고치는 데 그치지 않습니다. 조용히 모든 것을 움직이기 시작한 “첫 문장”, 즉 나머지 조각들을 연달아 움직이게 만든 작은 실패를 찾아내는 것이 목표입니다.

프로덕션이 대신 도미노 정원을 만들어주기를 기다리지 마십시오. 직접 도미노 정원을 만들고, 종이 실패들을 세워 놓고, 무엇이 실제로 쓰러지는지 확인하십시오.

그다음에는, 무언가가 언젠가 반드시 쓰러지더라도 이야기가 L3까지 번지지 않고, L1에서 끝나도록 판을 다시 설계하면 됩니다.

아날로그 장애 스토리 도미노 정원: 작은 ‘종이 실패’를 세워놓고 무엇이 진짜 붕괴를 시작하는지 보기 | Rain Lag