Infra Architecture/Foundations
Foundations - 04. redundancy와 고가용성 패턴
다섯 개의 백업이 전부 실패할 수 있다 — redundancy의 진짜 조건
2017년 1월 31일, GitLab의 한 엔지니어가 피로한 상태에서 데이터베이스 복구 작업을 하다가 잘못된 디렉터리에서 rm -rf를 실행했다. 프로덕션 데이터베이스가 삭제됐다. 놀란 팀이 백업을 확인했다. 백업은 다섯 군데에 있었다 — 디스크 백업, S3 동기화, Azure 동기화, staging 복제, LVM(논리 볼륨 관리자) 스냅샷. 이 중 정상적으로 복구 가능한 건 단 하나, LVM 스냅샷뿐이었다. 디스크 백업은 오래 전부터 실패하고 있었고, S3·Azure 동기화는 설정 오류로 오랫동안 돌지 않았고, staging 복제는 잘못된 데이터를 받고 있었다. 다섯 중 하나만 살아 있었다. 팀은 그 하나로 복구했지만, 문제의 본질은 "다섯 개의 백업이 안전한가"가 아니라 — 다섯 개가 서로 독립적이었는가였다. 그들은 독립적이지 않았다. 대부분이 같은 원인(설정·운영 오류)에 동시에 실패했다.
이 사건이 보여주는 것은 redundancy(중복)의 역설이다. 중복을 두면 안전할 거라는 통념은, 중복이 어떤 조건에서 의미가 있는지 놓친다. foundations 03에서 본 것처럼, 같은 장애 도메인 안의 중복은 중복이 아니다. 이 글은 그 다음 이야기를 다룬다 — fault domain 밖에서 중복을 어떻게 배치하느냐에 따라 중복이 진짜 안전망이 되기도 하고 GitLab처럼 환상으로 끝나기도 한다. 구체 패턴(active-active·N+1·quorum), 동기/비동기 복제의 트레이드오프, RTO/RPO의 의미, DR(재해 복구) 전략까지 — "왜 중복이 안전하지 않을 수 있는가"라는 질문에서 시작해 "진짜 안전한 중복을 어떻게 설계하는가"로 나아간다.
중복의 본질 — "더 많다"가 아니라 "독립적이다"
중복(redundancy)은 한 시스템이 동일한 기능을 수행하는 컴포넌트를 여러 개 갖는 것이다. 하지만 "여러 개 갖는 것"은 중복의 정의일 뿐, 중복의 가치는 아니다. 중복의 가치는 장애 시 다른 컴포넌트가 그 역할을 대신할 수 있다는 데 있다. 이 가치가 발휘되려면 하나의 전제가 있다 — 컴포넌트들이 서로 독립적으로 장애나야 한다.
03에서 본 직렬·병렬 가용성 공식을 다시 떠올려보자. 병렬 가용성 1 - (1-p1)(1-p2)가 성립하려면 p1과 p2가 독립적이어야 한다. p1이 장애날 확률과 p2가 장애날 확률이 서로 영향을 주지 않아야 한다. 이 가정이 깨지면 공식도 깨진다. 두 컴포넌트가 같은 전원·같은 랙·같은 AZ·같은 배치(batch)에 있으면, 같은 원인에 동시에 장애나고, "2-way 중복"은 사실상 "1-way"가 된다.
GitLab 사건은 독립성의 또 다른 형태를 보여준다. 다섯 백업은 모두 다른 위치에 있었다 — 디스크·S3·Azure·staging·LVM. 물리적 독립은 충분했다. 그런데 다섯 중 네 개가 같은 운영적 원인(백업 스크립트 설정 오류, 동기화 지속 실패)에 의해 조용히 실패하고 있었다. 논리적 독립이 부족했던 것이다. 중복의 조건은 물리적만이 아니다 — 백업 스크립트, 백업 주기, 접근 권한, 검증 절차도 각각 독립적이어야 진짜 중복이다. 한 스크립트가 다섯 백업을 다루면, 그 스크립트가 실패하면 다섯이 동시에 실패한다.
이 관점에서 중복은 "개수"가 아니라 "독립성의 정도"로 재야 한다. 진짜 질문은 "몇 개의 백업이 있나"가 아니라 "이 백업들이 서로 독립적인가"다. GitLab 팀이 postmortem에서 명시한 교훈도 이것이었다 — "다섯 백업이 있었지만, 그들이 독립적이지 않았다는 걸 인지하지 못했다."
중복 패턴 — 네 가지 기본 형태
중복을 배치하는 기본 패턴은 네 가지다. 각각 다른 가용성·비용·복잡도를 갖는다.
1. active-passive (주종)
하나의 컴포넌트(active)만 트래픽을 받고, 다른 컴포넌트(passive)는 대기하다가 active가 죽으면 교체된다.
flowchart LR
USER[사용자] --> LB[로드 밸런서/VIP]
LB --> A["active<br/>(실제 트래픽)"]
LB -. failover .-> P["passive<br/>(대기)"]
A -. 복제 .-> P
장점은 단순함과 비용 — passive는 active보다 작은 규격이어도 된다. 단점은 failover 시간 — passive가 active 역할을 인계받아 트래픽을 받기까지 수 초에서 수 분이 걸린다. 데이터베이스의 master-slave 복제, 로드 밸런서의 VRRP/keepalived, 클라우드의 RDS 다중 AZ가 전형적 사례다.
2. active-active (동시 구동)
여러 컴포넌트가 동시에 트래픽을 받는다. 한 컴포넌트가 죽어도 나머지가 트래픽을 분담한다.
flowchart LR
USER[사용자] --> LB[로드 밸런서]
LB --> A1["active-1<br/>(50% 트래픽)"]
LB --> A2["active-2<br/>(50% 트래픽)"]
A1 <-. 동기화 .-> A2
장점은 자원 활용과 빠른 장애 대응 — 한 쪽이 죽어도 나머지가 즉시 처리하므로 사용자가 느끼지 못한다. 단점은 일관성 복잡도 — 두 active가 같은 데이터를 쓰면 충돌이 발생하고, 이를 조율하는 계층(합의 알고리즘·분산 트랜잭션)이 필요하다. 비용은 active-passive보다 크다. 글로벌 서비스의 멀티 리전 active-active, 분산 데이터베이스(Cassandra·DynamoDB·Spanner)가 사례다.
3. N+1, N+M (여유 중복)
N개의 컴포넌트가 필요할 때 N+1개 또는 N+M개를 둔다. 한 개가 죽어도 N개는 유지된다.
flowchart LR
USER[사용자] --> LB[로드 밸런서]
LB --> W1["worker 1"]
LB --> W2["worker 2"]
LB --> W3["worker 3"]
LB --> W4["worker 4 (여유)"]
N+1은 한 번의 장애까지 견딘다. N+2는 두 번, N+M은 M번. 장점은 단순한 공식과 예측 가능한 비용. 단점은 "얼마나 큰 M이 충분한가"를 정하는 게 사실이 아니다 — 장애 빈도, 장애 지속 시간, 트래픽 피크가 함께 고려돼야 한다. 03에서 본 fault domain 관점에서 — N+1의 +1이 같은 랙·같은 AZ에 있으면 의미가 없다. 각 인스턴스가 서로 다른 장애 도메인에 있어야 한다.
4. quorum (다수결)
홀수 개(보통 3·5·7)의 노드가 다수결로 의사결정한다. 과반수 이상이 동의하면 커밋, 과반수 이상이 살아 있으면 서비스가 유지된다.
flowchart TD
CLI[클라이언트] --> L[리더]
L -->|복제| F1[팔로워 1]
L -->|복제| F2[팔로워 2]
L -.-> X[팔로워 3<br/>(죽어도 됨)]
3개 노드는 1개 장애까지, 5개는 2개 장애까지, 7개는 3개 장애까지 견딘다. 공식 N = 2f + 1(N은 노드 수, f는 허용 장애 수). 장점은 일관성 보장과 split-brain 방지 — 네트워크 분단 시 과반수가 없는 쪽은 멈춘다. 단점은 쓰기 성능 — 모든 쓰기가 과반수에 동기 복제돼야 해서 지연이 생긴다. etcd·Consul·ZooKeeper·PostgreSQL의 동기 복제가 사례다. 자세한 합의 알고리즘(Paxos·Raft)은 software-architecture/04-distributed-systems를 참조.
패턴 비교
| 패턴 | 가용성 | 쓰기 성능 | 비용 | 복잡도 | 전형 사례 |
|---|---|---|---|---|---|
| active-passive | 보통 (failover 시 일시 정지) | active만 쓰기, 빠름 | 낮음 | 낮음 | RDS multi-AZ, VRRP LB |
| active-active | 높음 (즉시 우회) | 양쪽 쓰기, 충돌 조율 필요 | 높음 | 높음 | DynamoDB, Cassandra, 글로벌 CDN |
| N+1 | 한 번 장애까지 견딤 | 빠름 | 중간 | 낮음 | Stateless 서비스 표준 |
| N+M | M번 장애까지 견딤 | 빠름 | 높음 | 낮음 | 고가용성 서비스 |
| quorum (3-노드) | 1노드 장애까지, 일관성 보장 | 과반수 동기화, 느림 | 중간 | 중간 | etcd, ZooKeeper |
| quorum (5-노드) | 2노드 장애까지, 일관성 보장 | 더 느림 | 높음 | 중간 | 프로덕션 etcd, Consul |
동기 복제 vs 비동기 복제 — RPO의 trade-off
중복은 컴포넌트 간 데이터 복제로 이어진다. 복제 방식에는 동기(sync) 와 비동기(async) 가 있고, 선택에 따라 장애 시 허용되는 데이터 손실량(RPO, Recovery Point Objective)이 결정된다.
동기 복제는 active가 쓰기를 수행할 때 passive(또는 과반수)에게도 쓰기가 완료된 뒤에 클라이언트에게 응답한다. 데이터 손실 0(RPO=0)을 보장하지만, passive까지의 왕복 지연이 매 쓰기에 추가된다. 멀티 AZ 동기 복제는 보통 1 ~ 5ms, 멀티 리전 동기 복제는 수십 ~ 수백 ms. 그래서 성능에 민감한 워크로드는 동기 복제를 피한다.
비동기 복제는 active가 쓰기를 클라이언트에게 바로 응답하고, passive에게는 나중에 전달한다. 빠르지만 — active가 죽을 때 passive에게 아직 도달하지 못한 쓰기는 손실된다. 이 손실량이 RPO다. 비동기 복제의 RPO는 보통 초 단위(밀리초~수 초)지만, 복제 지연이 쌓이면 분 단위까지 갈 수 있다.
| 복제 방식 | RPO | 쓰기 지연 추가 | 장애 시 데이터 손실 | 전형 사례 |
|---|---|---|---|---|
| 동기 (sync) | 0 | 큼 (왕복 지연) | 없음 | PostgreSQL sync rep, Spanner |
| 준동기 (quorum) | 0 (과반수 한정) | 중간 | 과반수가 살면 없음 | etcd, Raft 기반 |
| 비동기 (async) | 수 초 ~ 수 분 | 작음 | 허용 | MySQL async rep, Kafka cross-region |
| 리전 간 비동기 | 수 초 ~ 수 분 | 작음 | 허용 | 멀티 리전 DR |
이게 왜 중요한가? 모든 시스템이 동기 복제를 쓰면 안전하겠지만, 비용(지연·처리량 저하)이 감당이 안 된다. 결제 시스템은 RPO=0이 필요하므로 동기 복제로 지연을 감수하고, 일반 로깅 시스템은 RPO=수 초도 허용되므로 비동기 복제로 성능을 잡는다. RPO는 서비스의 성격이 결정하는 값이지, 엔지니어가 마음대로 정하는 값이 아니다.
RTO/RPO — 장애 복구의 두 축
DR(재해 복구) 설계에서 핵심이 되는 두 수치가 RTO 와 RPO 다. 둘 다 시간 단위이지만, 의미는 완전히 다르다.
- RTO (Recovery Time Objective) — 장애 발생 후 서비스가 복구될 때까지 허용되는 시간. "장애 뒤 얼마나 빨리 다시 정상이어야 하나"의 목표.
- RPO (Recovery Point Objective) — 장애 발생 시 허용되는 데이터 손실 시간. "장애 직전 몇 분/초의 데이터까지는 잃어도 되나"의 목표.
이해를 돕는 비유가 있다. 은행 ATM이 장애났다고 치자. RTO는 "ATM이 다시 정상 동작할 때까지 1시간 허용"이다 — 고객이 1시간 동안 ATM을 못 쓴다. RPO는 "직전 5분간의 거래 내역은 손실 허용"이다 — 마지막 5분 동안 입금된 돈이 기록에서 사라질 수 있다. 은행은 보통 RPO=0을 요구한다(돈이 사라지면 안 되니까), RTO는 15분 정도를 허용한다.
flowchart LR
T0[정상 동적] --> T1[장애 발생]
T1 -->|RPO: 데이터 손실 허용 구간| T2[복구 시점]
T2 -->|RTO: 서비스 중단 허용 구간| T3[정상 복구]
RTO와 RPO가 짧을수록 인프라 비용은 기하급증한다. RPO=0은 동기 복제를 의미하고, RTO=수분은 자동 failover + 헬스체크를 의미한다. 반대로 RPO=1시간, RTO=4시간이면 백업에서 복구하는 수준으로 충분하다. 모든 시스템을 RPO=0, RTO=0으로 만들 수는 없다. 각 서비스가 감당할 수 있는 RPO/RTO를 정하고, 그 목표에 맞는 DR 전략을 선택한다.
| DR 전략 | RTO | RPO | 비용 | 적용 사례 |
|---|---|---|---|---|
| 백업만 (Backup-only) | 수 일 ~ 수 주 | 수 시간 ~ 수 일 | 최소 | 개발 환경, 비핵심 데이터 |
| Pilot Light | 수 십 분 ~ 수 시간 | 수 분 | 낮음 | 보조 서비스, 분석 데이터 |
| Warm Standby | 수 분 ~ 수 십 분 | 수 초 ~ 수 분 | 중간 | 일반 상용 서비스 |
| Multi-site Active-Active | 수 초 ~ 수 분 | 0 ~ 수 초 | 높음 (2배) | 결제, 핵심 서비스 |
| Multi-region Multi-site | 즉시 | 0 | 매우 높음 | 글로벌 결제, 규제 대상 |
이 표는 AWS Well-Architected Framework의 Disaster Recovery 전략을 요약한 것이다. RTO/RPO가 짧아질수록 비용이 증가하는 패턴이 명확하다. 핵심은 — RTO/RPO가 설계 목표라는 점이다. "최대한 빨리, 최대한 안 잃게"는 의미 없는 목표다. "RTO 5분, RPO 1분"처럼 구체적이고 측정 가능한 목표가 있어야 설계가 시작된다.
헬스체크와 failover — 중복을 살리는 메커니즘
중복을 둬도 — active가 죽었을 때 passive가 그걸 감지하고 교체하지 않으면, 중복은 의미가 없다. 이 감지와 교체를 failover라 부르고, 그 핵심 메커니즘이 헬스체크(health check)다.
헬스체크는 두 가지 축을 갖는다.
- 주기(interval) — 얼마나 자주 검사하나. 1초, 5초, 30초.
- 임계값(threshold) — 몇 번 연속 실패해야 장애로 판정하나. 보통 2 ~ 3회.
주기가 5초, 임계값이 3이면 — 헬스체크 실패 후 15초 뒤에 장애로 판정하고 failover가 시작된다. 즉 헬스체크 설정만으로 RTO의 하한이 15초로 고정된다. RTO 5초가 필요하면 주기 1초, 임계값 2(총 2초 + failover 지연)로 설정해야 한다. RTO는 헬스체크 주기와 임계값의 합보다 짧을 수 없다.
헬스체크의 종류도 중요하다.
- L4 헬스체크 — TCP 연결이 성공하면 OK. 빠르지만, 서비스가 진짜 살아 있는지는 모른다. 프로세스가 떠 있지만 DB 연결이 끊긴 경우를 잡지 못한다.
- L7 헬스체크 — HTTP 요청(
/health엔드포인트 등)을 보내고 응답을 검사. 더 정확하지만 더 느리고 서버 자원을 쓴다. - 종합 헬스체크 —
/health엔드포인트가 의존성(DB·캐시·외부 API)까지 검사. 가장 정확하지만 가장 느리다. 너무 엄격하면 일시적 외부 장애에도 장애 판정이 나서 "cascade failover"가 일어날 수 있다.
Knight Capital의 2012년 사건은 failover와 kill switch의 부재가 얼마나 치명적인지를 보여준다. 2012년 8월 1일, 잘못된 배포로 주문 거래 시스템이 폭주해 45분 만에 4억4000만 달러 손실을 냈고, 회사는 파산했다. 시스템은 살아 있었지만 — 비정상 동작을 멈추게 하는 kill switch가 비활성화돼 있었다. 헬스체크가 "프로세스가 떠 있음"만 봤지 "비정상 동작 중"은 안 본 것이다. 장애는 "완전히 죽은" 상태만이 아니다 — "살아 있지만 비정상"인 상태도 장애고, 그걸 감지하지 못하면 중복은 도움이 안 된다.
중복 설계의 함정 — 네 가지
중복이 항상 안전을 의미하지는 않는다. 흔한 네 가지 함정을 짚는다.
1. 같은 장애 도메인의 중복 (03에서 다룬 것)
두 컴포넌트가 같은 랙·같은 AZ·같은 배치에 있으면, 장애 도메인이 하나다. 03의 "진짜 독립 vs 가짜 독립" 매트릭스에서 봤듯이 — 진짜 중복은 서로 다른 AZ·리전·벤더에 있을 때만 성립한다.
2. 과잉 중복 (over-redundancy)
"많을수록 좋다"는 통념은 비용과 복잡도를 폭발시킨다. 모든 컴포넌트를 3-way 중복하면 비용이 3배, 그 3-way를 다중 AZ로 두면 9배, 다중 리전으로 두면 27배. 03의 9의 법칙이 다시 등장한다 — 9가 추가될 때마다 비용은 10배. 적정 수준의 중복은 품질 속성 시나리오(02)에서 역산된다. "완벽한 중복"은 비즈니스를 망친다.
3. 공유 자원으로 환상 중복
03에서 다룬 분산 모놀리스와 같은 맥락. 두 서비스를 중복 배포했어도 같은 DB·같은 캐시를 공유하면, 그 공유 자원이 죽을 때 두 서비스가 같이 죽는다. 서비스 중복이 아니라 서비스 안의 공유 자원 중복이 필요하다.
4. 검증되지 않은 중복
이게 GitLab의 함정이었다. 다섯 백업이 있었지만 실제로 복구 가능한지 정기적으로 테스트하지 않았다. 중복은 그냥 두면 썩는다 — 백업 스크립트가 조용히 실패하고, 동기화가 느려지고, 설정이 어긋난다. chaos engineering(03에서 본 것)의 한 축이 바로 이 검증이다. "우리 시스템은 3-way 중복이다"라고 말하려면, 그 3-way 중 하나를 정기적으로 죽여서 나머지가 정상 동작하는지 확인해야 한다.
클라우드별 중복 패턴 — 구체 사례
추상적 패턴을 클라우드 실천으로 번역해 본다. AWS를 중심으로, 다른 클라우드도 유사한 패턴을 제공한다.
AWS RDS 다중 AZ — active-passive 패턴. primary DB가 한 AZ에, standby가 다른 AZ에 동기 복제된다. primary 장애 시 standby가 승격(promote)된다. RPO=0, RTO = 보통 60 ~ 120초. primary와 standby가 서로 다른 AZ에 있으므로 AZ 장애에 견딘다.
AWS Aurora — quorum 기반. 데이터를 6개 복사본(3 AZ × 2 copy)으로 분산, 6개 중 4개 쓰기, 3개 읽기로 쿼럼을 잡는다. AZ 1개가 통째로 날아가도, 디스크 2개가 추가로 날아가도 서비스가 유지된다. 6-way 복제이지만 저장 효율은 erasure coding으로 2배 수준(3-way 복제와 비슷).
AWS S3 Cross-Region Replication — 비동기 복제로 다른 리전에 객체를 복제. RPO는 보통 수 초 ~ 수 분. 한 리전이 통째로 날아가도 다른 리전에 데이터가 살아 있다. 2017년 AWS S3 us-east-1 장애 당시 다중 리전 복제를 둔 고객은 영향을 피했다.
ELB (Elastic Load Balancer) — 다중 AZ에 걸친 active-active. 단일 AZ LB가 죽어도 다른 AZ LB가 트래픽을 받는다. 다만, 뒤의 인스턴스가 한 AZ에만 있으면 AZ 장애 시 전체가 죽는다 — LB의 중복 + 인스턴스의 중복이 둘 다 필요하다.
Auto Scaling Group — N+M 패턴을 자동화. 트래픽 증가 시 자동으로 인스턴스를 추가, 장애 인스턴스를 자동 교체. 단, AZ가 1개만 지정돼 있으면 AZ 장애에 취약하다. 다중 AZ 설정이 필수.
이런 클라우드 서비스의 중복 패턴은 이미 검증된 설계를 캡슐화한 것이다. 직접 구현하려면 Aurora의 6-way 쿼럼, RDS의 자동 failover 같은 걸 다시 만들어야 한다. 클라우드가 편리한 이유가 이거다 — 중복 패턴이 관리형 서비스로 제공된다.
실습 — 중복 설정 직접 확인하기
이 글의 개념을 직접 확인하는 기본 명령들이다.
1. PostgreSQL 동기 복제 상태 확인
# primary에서 standby로의 복제 상태를 본다.
sudo -u postgres psql -c "SELECT application_name, state, sync_state, sync_priority FROM pg_stat_replication;"
확인할 것: sync_state가 sync이면 동기 복제(RPO=0), async이면 비동기(RPO>0). sync_priority는 동기 복제 대기 순위.
# 예상 출력 (1 sync + 1 async standby)
application_name | state | sync_state | sync_priority
------------------+----------+------------+---------------
walreceiver_1 | streaming| sync | 1
walreceiver_2 | streaming| async | 0
2. AWS CLI로 다중 AZ 배포 상태 확인 (AWS 환경)
# RDS 인스턴스의 다중 AZ 상태를 본다.
aws rds describe-db-instances --query 'DBInstances[*].[DBInstanceIdentifier,MultiAZ,AvailabilityZone,SecondaryAvailabilityZone]' --output table
확인할 것: MultiAZ가 true이고 SecondaryAvailabilityZone이 primary와 다른 AZ여야 진짜 다중 AZ. 같은 AZ이거나 MultiAZ=false이면 SPOF.
# 예상 출력 (정상 다중 AZ)
-----------------------------------------------------------------------------------
| DescribeDBInstances |
+----------------------+-------+-----------------+--------------------------+
| mydb-prod | True | us-east-1a | us-east-1b |
+----------------------+-------+-----------------+--------------------------+
3. nginx upstream 헬스체크 확인
# nginx가 upstream 서버들의 헬스체크를 어떻게 설정했는지 본다.
grep -A 20 'upstream' /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf 2>/dev/null | head -30
확인할 것: max_fails와 fail_timeout 설정. max_fails=3 fail_timeout=30s이면 30초 동안 3번 실패 시 30초 동안 해당 서버를 우회.
# 예상 nginx 설정
upstream backend {
server 10.0.1.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.2.10:8080 max_fails=3 fail_timeout=30s;
server 10.0.3.10:8080 max_fails=3 fail_timeout=30s backup;
}
backup 표시가 있으면 active-passive 패턴, 없으면 active-active.
4. etcd 쿼럼 상태 확인 (Kubernetes 클러스터)
# etcd 클러스터의 노드 상태를 본다. (kubeadm로 설치한 경우)
sudo crictl exec $(sudo crictl ps --name etcd -q) etcdctl endpoint status --write-out=table
확인할 것: IS LEARNER가 false인 노드가 과반수여야 쿼럼이 유지된다. 3노드 클러스터에서 2개까지 장애 허용, 5노드에서 3개까지 장애 허용.
# 예상 출력 (3노드 클러스터)
+-----------------+------------------+---------+---------+-----------+
| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER |
+-----------------+------------------+---------+---------+-----------+
| 127.0.0.1:2379 | 8e9e05c52164694d | 3.5.10 | 4.3 MB | true |
| 10.0.1.21:2379 | 91bc3c398fb3c146 | 3.5.10 | 4.3 MB | false |
| 10.0.2.21:2379 | fd422379fda50e48 | 3.5.10 | 4.3 MB | false |
+-----------------+------------------+---------+---------+-----------+
미검증: 위 출력의 정확한 형태는 환경·버전에 따라 다르다. AWS CLI는 AWS 자격증명이 설정된 환경에서만 동작. etcdctl은 etcd 클러스터 인증서가 필요할 수 있다.
판단 표 — 중복 패턴 선택
서비스 특성에 따른 중복 패턴 선택 기준을 표로 정리한다.
| 서비스 특성 | 권장 패턴 | RTO | RPO | 비용 |
|---|---|---|---|---|
| 개발/테스트 환경 | 단일 인스턴스 | 수 시간 | 수 시간 | 최소 |
| 비핵심 서비스 (로깅, 분석) | N+1, 비동기 복제 | 수 십 분 | 수 분 | 낮음 |
| 일반 웹 서비스 (99.9% SLO) | N+2 다중 AZ, 비동기 | 수 분 | 수 초 | 중간 |
| 상용 서비스 (99.99% SLO) | 다중 AZ active-active, 동기 | 수 초 | 0 | 높음 |
| 결제·인증 (99.999% SLO) | 다중 리전 active-active, 동기 | 즉시 | 0 | 매우 높음 |
| 설정 저장소 (etcd) | 5노드 quorum, 다중 AZ | 자동 | 0 | 중간 |
| 객체 스토리지 | erasure coding + cross-region | 자동 | 수 초 | 중간 |
중복 검증 체크리스트
중복 설계가 "진짜"인지 확인하는 체크리스트다.
- 중복 컴포넌트가 서로 다른 장애 도메인(다른 랙, 다른 AZ, 다른 리전)에 있는가?
- active-passive인 경우, failover 절차가 자동화돼 있는가? 수동 개입이 필요하면 RTO가 수십 분을 넘는다.
- 헬스체크가 서비스 진짜 동작을 검사하는가? 단순 TCP 연결만으로는 "살아 있지만 비정상" 상태를 못 잡는다.
- 헬스체크 주기와 임계값의 합이 목표 RTO보다 짧은가? 그렇지 않으면 RTO를 달성할 수 없다.
- 백업이 정기적으로 복원 테스트되는가? GitLab 사건의 핵심 교훈 — 백업을 안 해봤으면 없는 거나 다름없다.
- 동기 복제인 경우, 지연이 허용 범위 안인가? 멀티 리전 동기 복제는 보통 수십 ms의 쓰기 지연을 만든다.
- 비동기 복제인 경우, 복제 지연을 모니터링하는가? 지연이 쌓이면 RPO가 목표치를 초과한다.
- failover가 실제로 일어나는지 게임데이로 검증했는가? "이론상 가능"과 "실제로 동작"은 다르다.
- failover 뒤에도 성능이 유지되는가? active-active에서 절반이 죽었을 때 나머지 절반이 트래픽을 감당하는가 (용량 계획과 연결).
- kill switch나 강제 failback 경로가 있는가? Knight Capital 사건의 교훈 — 비정상 동작을 멈출 수 있어야 한다.
중복은 강도가 아니라 독립성이다
도입의 GitLab 사건으로 돌아가자. 다섯 백업이 전부 실패한 이유는 "다섯이 부족해서"가 아니었다 — 다섯이 서로 독립적이지 않았기 때문이다. 한 명의 엔지니어가 하나의 도구로 다섯을 관리했고, 그 도구가 실패하면 다섯이 함께 실패했다. 중복은 개수로 평가하는 게 아니라 독립성의 정도로 평가해야 한다. 다섯 개의 종속 중복보다 두 개의 독립 중복이 더 안전할 수 있다.
이걸 기억하면 중복 설계의 원칙이 단순해진다. 첫째, 중복 컴포넌트는 서로 다른 장애 도메인에. 둘째, 복제 방식(동기/비동기)은 RPO 목표에서 역산. 셋째, 헬스체크와 failover는 RTO 목표에서 역산. 넷째, 중복은 정기적으로 검증. 마지막으로 — 비용과의 균형. "완벽한 중복"은 없다. 품질 속성 시나리오(02)에서 정해진 RTO/RPO를 달성하는 최소의 중복이 정답이다.
다음 글에서는 인프라 의사결정의 다른 축을 다룬다 — 인프라를 직접 구축할 것인가, 관리형 서비스를 쓸 것인가(build vs buy). redundancy와 DR이 "직접 구축"의 가장 큰 부담이라는 걸 알게 될 것이다. Aurora의 6-way 쿼럼을 직접 구현하려면 어떤 비용이 드는가, AWS에 맡기면 어떤 통제를 잃는가. 그 질문이 build vs buy의 핵심이다.
참고
- GitLab, "GitLab.com: Database incident impacting production and the GitLab.com service", 2017-02-10 — 다중 백업 실패의 공식 postmortem. 접근 2026-07-21. URL: https://about.gitlab.com/blog/2017/02/10/postmortem-of-database-outage-of-january-31/
- AWS Well-Architected Framework, Reliability Pillar — DR 전략(backup/pilot-light/warm-standby/multi-site). 접근 2026-07-21. URL: https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/disaster-recovery.html
- Aurora 팀 "Amazon Aurora: Design Considerations for High Throughput Cloud-Native Relational Databases", SIGMOD 2017 — 6-way 복제 쿼럼 설계. 접근 2026-07-21
- U.S. SEC, "In the Matter of Knight Capital Americas LLC", Release No. 70694, 2013-10-16 — Knight Capital 2012-08-01 사건의 공식 보고서. 접근 2026-07-21
- NIST SP 800-34 Rev 1 "Contingency Planning Guide for Federal Information Systems", 2010 — RTO/RPO의 공식 정의. 접근 2026-07-21
- Ongaro, D., Ousterhout, J. "In Search of an Understandable Consensus Algorithm (Raft)", USENIX ATC 2014 — quorum 기반 합의 알고리즘. 접근 2026-07-21
- Beyer, B., Jones, C., Petoff, J., Murphy, N. R. (eds.)
, O'Reilly, 2016, Ch.6 — cascading failure와 중복의 함정. 접근 2026-07-21 - Kleppmann, M.
, O'Reilly, 2017, Ch.5·Ch.9 — 복제(동기/비동기), 일관성과 합의. 접근 2026-07-21
'Infra Architecture > Foundations' 카테고리의 다른 글
| Foundations - 03. fault domain과 blast radius (1) | 2026.07.21 |
|---|---|
| Foundations - 02. 인프라 품질 속성 (0) | 2026.07.21 |
| Foundations - 01. 인프라 아키텍처란 무엇인가 (0) | 2026.07.21 |