Skip to content

필사 모드: 분산 시스템 완전 가이드 — 시계·Consensus·Event Sourcing·Saga·CRDT·장애 패턴 (Season 2 Ep 12, 2025)

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

들어가며 — 분산 시스템이 어려운 진짜 이유

Butler Lampson의 유명한 정의:

> "분산 시스템이란, 네가 존재하는지도 몰랐던 컴퓨터의 고장이 네 컴퓨터를 사용할 수 없게 만드는 것이다."

분산 시스템이 어려운 이유는 다음 3가지가 **동시에** 일어나기 때문:

1. **시계가 다르다** (Clock Drift)

2. **고장이 부분적이다** (Partial Failure)

3. **네트워크는 불신해야 한다** (Unreliable Network)

이 글은 이 3가지 문제에 대한 **이론적 도구(시계·합의·일관성)**와 **실전 패턴(Event Sourcing·Saga·CRDT)**을 한 번에 정리한다.

1부 — 8 Fallacies of Distributed Computing

Peter Deutsch가 1994년에 정리한 분산 시스템 8가지 거짓 가정:

1. **네트워크는 신뢰할 수 있다** ❌

2. **지연시간은 0이다** ❌

3. **대역폭은 무한하다** ❌

4. **네트워크는 안전하다** ❌

5. **토폴로지는 변하지 않는다** ❌

6. **관리자는 한 명이다** ❌

7. **전송 비용은 0이다** ❌

8. **네트워크는 균질하다** ❌

**이 8가지를 어기는 모든 가정은 언젠가 프로덕션에서 터진다.**

2부 — 시간의 3가지 모델

2.1 Physical Clock (물리 시계)

`time.Now()` — OS 시계. NTP로 동기화.

**문제:**

- Clock Drift (ppm 단위 오차)

- NTP 점프 (일정 조정 시 뛰어 넘음)

- Leap Second

- 데이터센터 간 ms~수십 ms 오차

2.2 Logical Clock (논리 시계)

**Lamport Timestamp (1978):**

이벤트 발생 시 counter++

메시지 송신: (event, counter) 전송

메시지 수신: counter = max(local, received) + 1

**장점**: 인과 관계 보존 (happens-before)

**단점**: 인과 관계를 가졌는지 **구분 불가** (concurrent vs causal)

2.3 Vector Clock

노드 수 N만큼의 벡터:

A: [2, 1, 0]

B: [1, 3, 0]

- A와 B가 concurrent인지, 누가 먼저인지 판별 가능

- **크기 문제**: 노드 수 많으면 메타데이터 커짐

2.4 HLC (Hybrid Logical Clock, 2014)

**물리 시간 + 논리 카운터** 조합:

HLC = (physical_time, logical_counter)

- 물리 시계 기준 정렬 + 논리 시계로 인과 보존

- CockroachDB, YugabyteDB 등 현대 분산 DB의 표준

2.5 TrueTime (Google Spanner)

**GPS·원자시계로 ±7ms 보장** → "이 시점 이후의 이벤트는 확실히 뒤"가 가능.

- Commit Wait: 불확실성 구간(ms)만큼 대기 후 완료

- 강력한 일관성 + 성능 (Linearizable)

- 일반 기업은 구현 불가 (하드웨어 투자)

3부 — 일관성 모델 스펙트럼

3.1 주요 일관성 모델

**강함 → 약함 순서:**

1. **Linearizable**: 모든 연산이 실시간 순서대로 보임

2. **Sequential**: 모든 프로세스가 같은 순서로 보되, 실시간은 아님

3. **Causal**: 인과 관계 있는 것만 순서 보장

4. **Eventual**: 결국 같아짐

3.2 CAP 재방문

- **Consistency**: Linearizability

- **Availability**: 모든 요청이 답을 받음

- **Partition Tolerance**: 네트워크 분할 시에도 동작

**네트워크 분할은 피할 수 없음** → CP or AP 선택.

3.3 PACELC (더 현실적)

- **P**artition: C or A

- **E**lse (정상 운영): L(atency) or C(onsistency)

대부분의 시스템은 **PA/EL** (분할 시 가용성 + 정상 시 저지연).

3.4 2025 DB 일관성 선택

| DB | 모델 |

|----|-----|

| PostgreSQL | 단일 노드 Serializable |

| MySQL | Read Committed (기본) |

| Spanner | Linearizable + External Consistency |

| CockroachDB | Serializable + Causal |

| Cassandra | Tunable (Quorum·Eventual) |

| DynamoDB | Eventual (옵션 Strong) |

| Redis Cluster | 단일 키 Linearizable, 다중 키 ❌ |

4부 — Consensus: 합의 알고리즘

4.1 왜 Consensus인가

분산된 여러 노드가 **같은 값에 동의** — 리더 선출, 상태 복제, 트랜잭션 커밋 등.

4.2 FLP Impossibility (1985)

**비동기 시스템에서 1개 노드 고장도 허용하는 deterministic consensus는 불가능.**

실제 시스템은 **timing 가정** (eventually synchronous) 또는 **확률적** 방법을 쓴다.

4.3 Paxos (1989)

Leslie Lamport가 만든 원조. 정확하지만 **이해·구현 어려움**으로 악명.

**역할:**

- Proposer (값 제안)

- Acceptor (투표)

- Learner (결정 학습)

**문제**: 실전에서 쓰기 어려워 Multi-Paxos, Fast Paxos, EPaxos 등 변형.

4.4 Raft (2014)

Stanford에서 "이해 가능한 Consensus"를 목표로 설계. 현재 사실상 표준.

**3가지 핵심:**

1. **Leader Election**: 리더 하나 뽑기

2. **Log Replication**: 리더가 Follower에게 로그 복제

3. **Safety**: Committed 로그는 뒤집히지 않음

**상태:**

- Follower → Candidate (Election Timeout) → Leader (과반 투표)

- Leader가 heartbeat 실패 시 → 새 선출

**사용처**: etcd, Consul, TiKV, CockroachDB, Kafka KRaft (2024~).

4.5 PBFT (Practical Byzantine Fault Tolerance, 1999)

악의적 노드 존재 시 **3f+1 노드 중 f개 고장 허용**. 블록체인에서 사용.

**단계:** Pre-prepare → Prepare → Commit. 메시지 복잡도 O(N²).

4.6 Tendermint·HotStuff (2018~)

BFT를 블록체인에 맞게 현대화. Libra/Diem의 HotStuff, Cosmos의 Tendermint.

4.7 Nakamoto Consensus

Bitcoin의 Proof-of-Work. 확률적 합의 — 충분히 많은 블록 뒤에 있으면 confirmed.

**단점**: 에너지, 느림. 그래서 Ethereum이 PoS로 전환.

5부 — Replication

5.1 복제 패턴 4가지

1. **Single-Leader**: 쓰기는 리더, 읽기는 어디서든. 단순. 대부분의 RDBMS.

2. **Multi-Leader**: 여러 리더가 쓰기 수락. 충돌 해결 필요. 드물게 사용.

3. **Leaderless**: 모든 노드가 동등. Dynamo-style. Cassandra, Riak, DynamoDB.

4. **Quorum-based**: R + W > N 이면 강한 일관성.

5.2 Replication Lag

Leader → Follower 전파 지연 → 다음 문제:

- **Read-your-writes**: 방금 쓴 걸 못 읽음

- **Monotonic reads**: 이전에 본 것이 사라짐

- **Consistent Prefix**: 원인 없는 결과를 봄

**해결:** Leader에서 읽기, Sticky Session, Timestamp 기반 일관성.

5.3 Conflict Resolution (Multi-Leader/Leaderless)

- **LWW (Last Writer Wins)**: 간단, 데이터 손실 위험

- **Version Vectors**: 정확, 복잡

- **Application-level Merge**: 사용자 정의 (CRDT)

6부 — CRDT (Conflict-free Replicated Data Type)

6.1 CRDT란

**수학적으로 충돌이 불가능한** 데이터 구조. 어느 순서로 합쳐도 같은 결과.

6.2 두 가지 유형

**State-based (CvRDT)**: 상태 자체를 전송 + merge

**Operation-based (CmRDT)**: 연산을 전송 + 재적용

6.3 대표 CRDT

| CRDT | 용도 |

|-----|-----|

| **G-Counter** | 증가만 (카운터) |

| **PN-Counter** | 증감 |

| **G-Set** | 추가만 |

| **OR-Set** | 추가·제거 |

| **LWW-Register** | 마지막 쓰기 승리 |

| **RGA** | 순서 있는 리스트 (텍스트 편집) |

| **JSON CRDT (Automerge)** | 중첩 구조 |

6.4 실전 사용처

- **Redis CRDT** (Enterprise): Active-Active Geo

- **Riak**: Dynamo + CRDT

- **Automerge** (Ink & Switch): 협업 에디터

- **Yjs**: Real-time collaboration (Notion, Linear 영감)

- **Figma**: 디자인 협업에 자체 CRDT

6.5 한계

- 임의 불변 규칙 표현 어려움 (예: 유일성)

- 메타데이터 크기 (Tombstone)

- 작업 순서 의미를 잃음 (이게 장점이자 단점)

7부 — Event Sourcing + CQRS

7.1 Event Sourcing

상태 대신 **이벤트를 저장**. 상태는 이벤트 재생으로 도출.

events = [

AccountCreated(id=1, balance=0),

MoneyDeposited(account=1, amount=100),

MoneyWithdrawn(account=1, amount=30),

]

balance = fold(events, 0, apply) // 70

**장점:**

- 완전한 히스토리 (감사·디버깅)

- 시간 여행

- 이벤트 재생으로 새 View 생성

- 분산 친화 (이벤트는 append-only)

**단점:**

- 복잡도 ↑

- 스키마 진화 문제 (구 이벤트 해석)

- Snapshot 없으면 재생 느림

7.2 CQRS (Command Query Responsibility Segregation)

**쓰기 모델(Command) ≠ 읽기 모델(Query)** 분리.

Write: events → EventStore

Projection

Read: Materialized View (최적화)

**장점**: 읽기·쓰기 독립 최적화·확장.

**단점**: 일관성 지연 (Eventual).

7.3 Event Sourcing + CQRS 스택

- **EventStoreDB**: 전용 DB

- **Kafka + Kafka Streams**: 이벤트 로그 + Projection

- **Axon Framework** (Java): 통합

- **MartenDB** (.NET): Postgres 위에 구현

7.4 언제 쓰고 언제 피할까

**좋을 때:**

- 금융·거래처럼 **감사 가능성**이 중요

- 복잡한 비즈니스 로직 + 과거 재구성

- 이벤트 중심 아키텍처

**피할 때:**

- 단순 CRUD

- 팀이 초보자 다수

- 간단한 일관성 요구

8부 — Saga 패턴

8.1 분산 트랜잭션 문제

2PC (Two-Phase Commit): 완벽하지만 **block**, Coordinator 실패에 취약. 현대에 거의 안 씀.

**대안: Saga — 로컬 트랜잭션 연속 + 실패 시 보상 트랜잭션.**

8.2 Choreography (안무)

각 서비스가 이벤트 발행 + 구독:

OrderCreated → InventoryReserved → PaymentCharged → OrderConfirmed

↓ 실패

InventoryReleased → OrderCancelled (보상)

**장점**: 서비스 간 결합도 낮음.

**단점**: 흐름 추적 어려움.

8.3 Orchestration (지휘)

중앙 Orchestrator가 명시적 흐름 관리:

Orchestrator:

1. ReserveInventory

2. ChargePayment

3. ShipOrder

실패 시 ← CompensateAll

**장점**: 흐름 명시적, 디버깅 쉬움.

**단점**: Orchestrator가 단일 장애 지점 가능.

8.4 구현 도구

- **Temporal**: 워크플로우 엔진. Saga의 사실상 표준.

- **AWS Step Functions**: 관리형

- **Camunda**: BPMN 기반

- **Cadence** (Uber, Temporal 전신): 오픈소스

8.5 Temporal 예시

func ProcessOrder(ctx workflow.Context, order Order) error {

err := workflow.ExecuteActivity(ctx, ReserveInventory, order).Get(ctx, nil)

if err != nil { return err }

err = workflow.ExecuteActivity(ctx, ChargePayment, order).Get(ctx, nil)

if err != nil {

workflow.ExecuteActivity(ctx, ReleaseInventory, order)

return err

}

// ...

}

Temporal이 **재시작 안전성·타이머·시각화**를 무료로 제공.

9부 — 장애 패턴 12

9.1 장애 전파 패턴

1. **Cascading Failure**: 한 서비스 느려짐 → 호출자 타임아웃 쌓임 → 전파

2. **Thundering Herd**: 캐시 만료 시 모든 요청이 Origin에 몰림

3. **Retry Storm**: 실패한 요청이 재시도로 증폭

4. **Metastable Failure**: 일시적 문제가 정상 복귀 후에도 지속

5. **Split-Brain**: 네트워크 분할로 두 리더 생김

6. **Clock Skew Bug**: 시계 차이로 타임스탬프 역전

7. **Gray Failure**: 죽지 않았지만 기능 저하

8. **Poison Pill**: 특정 메시지가 모든 Consumer 다운

9. **Queue Backlog**: Consumer 느려 무한 쌓임

10. **Hot Partition**: 특정 샤드에 트래픽 집중

11. **Fan-out Overload**: 하나의 요청이 100개 내부 요청

12. **Noisy Neighbor**: 공유 자원에서 한 테넌트가 전체 영향

9.2 방어 패턴

| 장애 | 방어 |

|-----|-----|

| Cascading | Circuit Breaker, Bulkhead, Timeout |

| Thundering Herd | Request Coalescing, Jittered refresh |

| Retry Storm | Exponential Backoff + Jitter |

| Metastable | Load Shedding, Queue Depth 제한 |

| Split-Brain | Fencing Token, Quorum |

| Clock Skew | HLC, NTP 감시 |

| Gray Failure | Health Check + 적극 폐기 |

| Poison Pill | Dead Letter Queue + Alert |

| Queue Backlog | Backpressure, Auto-scale |

| Hot Partition | Consistent Hashing + replication |

| Fan-out | Timeout 깊이 고려, Async |

| Noisy Neighbor | Resource Quota, QoS |

9.3 Chaos Engineering

의도적으로 장애 주입 → 시스템 검증.

- **Chaos Monkey** (Netflix 2011): 인스턴스 랜덤 킬

- **Chaos Mesh**: K8s 네이티브

- **LitmusChaos**: 오픈소스

- **Gremlin**: 상용

**원칙:**

- 프로덕션에서 (처음엔 스테이징)

- Blast Radius 제한

- 자동 롤백

- 정기 실행 (Game Day)

10부 — 분산 시스템 패턴 10

1. **Idempotency**: 같은 요청 N번 = 1번 효과

2. **Exactly-once is a lie**: At-least-once + Idempotent Consumer

3. **Outbox Pattern**: DB 트랜잭션 + 이벤트 발행 원자성

4. **Inbox Pattern**: 멱등 Consumer 구현

5. **Circuit Breaker**: 실패 감지 + 회피

6. **Bulkhead**: 스레드풀 격리로 연쇄 실패 방지

7. **Leader Election**: Lease 기반

8. **Sharding**: 데이터 분할

9. **Sidecar**: 공통 기능 (로깅, 보안)을 별도 컨테이너

10. **Service Mesh**: Sidecar를 네트워크 레이어로

11부 — 분산 시스템 로드맵 6개월

Month 1: 이론 기초

- DDIA (Designing Data-Intensive Applications) 정독

- Lamport Paper 2편

- 8 Fallacies 사고 실험

Month 2: Consensus 실습

- Raft 직접 구현 (MIT 6.824)

- etcd·Consul 내부 읽기

- Kafka KRaft 구조 이해

Month 3: Replication + Consistency

- 일관성 모델 구분 연습

- CockroachDB·Spanner 백서

- TrueTime·HLC 구현

Month 4: Event Sourcing + Saga

- 미니 뱅킹 시스템 Event Sourcing으로

- Temporal로 Saga 구현

- Outbox 패턴 실제 적용

Month 5: CRDT + 실시간

- Automerge·Yjs 학습

- 협업 에디터 프로토타입

- Figma 기술 블로그 정독

Month 6: 장애 + 운영

- Chaos Monkey 도입

- 주요 장애 패턴 방어 구현

- Incident Postmortem 분석 10건

12부 — 분산 시스템 체크리스트 12

1. **8 Fallacies**를 말할 수 있다

2. **Lamport vs Vector vs HLC** 차이를 안다

3. **CAP vs PACELC** 차이를 안다

4. **Raft의 3가지 핵심**을 설명할 수 있다

5. **FLP Impossibility**의 의미를 안다

6. **Single-Leader vs Leaderless** 트레이드오프를 안다

7. **CRDT가 해결하는 문제**를 안다

8. **Event Sourcing + CQRS** 장단점을 안다

9. **Saga Choreography vs Orchestration** 차이를 안다

10. **Cascading Failure 방어 3가지**를 말할 수 있다

11. **Outbox·Inbox 패턴**의 역할을 안다

12. **Chaos Engineering 원칙**을 안다

13부 — 분산 시스템 안티패턴 10

1. **"네트워크는 신뢰할 수 있다"**: 8 Fallacies 1번. Timeout·재시도 필수

2. **2PC를 마이크로서비스에**: Block 위험. Saga로

3. **이벤트 순서를 Kafka 파티션 간 가정**: 파티션별로만 보장

4. **Exactly-once를 DB 트랜잭션 없이**: 불가능. Outbox 또는 멱등

5. **Clock을 신뢰**: `time.Now()`로 정렬 → 버그. 논리 시계 필수

6. **무한 재시도**: Retry Storm 유발. Backoff + Circuit Breaker

7. **단일 노드 DB + 복제만으로 안전하다 가정**: Split-Brain 고려 필수

8. **Consumer 실패 시 NACK 없이 블록**: DLQ 설계 필수

9. **글로벌 트랜잭션**: 성능·가용성 ↓. 경계 재설계

10. **"Eventual Consistency는 결국 같아짐"**: 사용자 관점에선 문제. UX로 해결

마치며 — 분산 시스템은 "마음의 모델"이다

분산 시스템은 직관이 가장 자주 배신하는 영역이다. "직관이 강력한" 엔지니어가 가장 위험.

필요한 것:

- **의심하는 마음**: 네트워크·시계·노드 전부 의심

- **보상하는 마음**: 원자성 안 된다면 보상 설계

- **재현하는 마음**: 이벤트로 기록 → 언제든 재현

2025년 시니어 엔지니어의 분산 역량은 **연봉을 2배로 벌리는 축**이다. 이 영역은 그만큼 어렵고, 그래서 그만큼 값지다.

다음 글 예고 — "DB 완전 가이드: 내부 구조·인덱스·쿼리 플래너·파티셔닝·Vector DB"

Season 2 Ep 13은 데이터의 심장, **DB 심화**. 다음 글은:

- B-Tree·LSM-Tree·Hash Index 내부 구조

- 쿼리 플래너와 EXPLAIN 깊이 읽기

- 트랜잭션 격리 수준 (Read Uncommitted ~ Serializable)

- 샤딩·파티셔닝 전략

- PostgreSQL의 독주 (2025)

- Vector DB와 pgvector·Qdrant·Weaviate

DB가 어떻게 쿼리를 처리하는지, 다음 글에서.

현재 단락 (1/284)

Butler Lampson의 유명한 정의:

작성 글자: 0원문 글자: 8,826작성 단락: 0/284