운영자를 위한 분산 시스템 판단 기준
운영 판단은 이론 이름을 외우는 일이 아니다
분산 시스템을 운영할 때 가장 위험한 질문은 “이 시스템은 CP인가 AP인가?”처럼 너무 큰 이름으로 시작하는 질문이다. 실제 장애실에서는 그렇게 단순하게 갈리지 않는다. 어떤 요청은 강한 일관성이 필요하고, 어떤 요청은 늦게 보여도 된다. 어떤 장애에서는 쓰기를 멈추는 편이 안전하고, 어떤 장애에서는 읽기만 degrade해서라도 서비스를 유지하는 편이 낫다.
운영자의 질문은 더 구체적이어야 한다.
- 지금 지켜야 하는 것은 가용성인가, 데이터 안전성인가, 지연 시간인가?
- 이 기능은 stale read를 허용할 수 있는가?
- 쓰기 성공을 사용자에게 보여준 뒤 나중에 되돌릴 수 있는가?
- 자동 failover가 RTO를 줄이는 대신 split-brain이나 데이터 손상을 만들 가능성은 없는가?
- 더 높은 신뢰성을 위해 드는 비용이 실제 사용자 가치와 맞는가?
분산 시스템 이론은 이 질문에 답하기 위한 언어다. 운영자는 정리된 정답을 고르는 사람이 아니라, 보장 수준과 장애 시 행동을 명시적으로 선택하는 사람이다.
판단 축 1: 먼저 불변식(invariant)을 정한다
운영 판단의 출발점은 기술이 아니라 깨지면 안 되는 조건이다. 같은 분산 저장소라도 기능별로 불변식은 다르다.
| 기능 | 깨지면 안 되는 것 | 허용 가능한 것 | 보통 필요한 보장 |
|---|---|---|---|
| 결제 승인 | 중복 승인, 잔액 음수, 승인 후 기록 유실 | 몇 초 지연, 일시적 실패 | 강한 쓰기 일관성, idempotency, fencing |
| 재고 차감 | 초과 판매 한도 초과 | 제한된 oversell, 예약 만료 | 조건부 쓰기, compare-and-set, 보상 트랜잭션 |
| 사용자 프로필 표시 | 다른 사용자의 데이터 노출 | 최근 수정사항 지연 반영 | read-your-writes 또는 session consistency |
| 추천 피드 | 잘못된 순위, 오래된 추천 | stale result, fallback | eventual consistency, cache fallback |
| 분석 대시보드 | 숫자 해석 오류, 중복 집계 | 지연 표시, 재처리 | watermark, lineage, 재계산 가능성 |
여기서 중요한 점은 “모든 데이터를 linearizable하게 만들자”가 아니라, 어떤 불변식이 어떤 경로에서 깨질 수 있는지를 먼저 쓰는 것이다. Jepsen의 consistency model 설명처럼 강한 모델은 더 적은 실행 history를 허용한다. 그만큼 추론은 쉬워지지만, 비동기 네트워크에서는 가용성과 성능 비용을 치른다.
깨지면 안 되는 조건 → 보장 선택
일관성·격리·내구성 → 장애 시 행동
fail closed / degrade → 운영 검증
SLO·drill·alert
판단 축 2: CAP보다 정확한 문장으로 말한다
CAP 정리는 유용한 경고를 준다. 네트워크가 갈라진 상황에서 모든 노드가 항상 응답하면서 동시에 linearizability를 보장할 수는 없다. 하지만 운영 문서에서 “우리 DB는 CP”라고만 쓰면 거의 도움이 되지 않는다.
Martin Kleppmann이 지적하듯 CAP의 consistency는 일반적인 “데이터가 맞다”가 아니라 linearizability에 가깝고, availability도 “사용자 입장에서 대체로 살아 있다”가 아니라 모든 non-failing node가 non-error response를 반환한다는 좁은 의미다. 실제 제품은 이보다 복잡하다. timeout, leader lease, quorum, stale replica read, degraded mode, queue buffering, manual approval 같은 정책이 섞인다.
그래서 운영 문서는 다음처럼 써야 한다.
나쁜 문장:
- 이 저장소는 AP라서 장애에도 안전하다.
- 이 클러스터는 CP라서 데이터가 항상 맞다.
좋은 문장:
- 결제 승인 write는 quorum commit 전 성공을 반환하지 않는다.
- primary 선출 중에는 신규 write를 fail closed한다.
- 상품 목록 read는 최대 60초 stale cache를 허용한다.
- 사용자 본인의 설정 변경 직후 read는 primary 또는 session token 기반 경로로 보낸다.이렇게 쓰면 장애 대응도 명확해진다. 예를 들어 네트워크 파티션이 생겼을 때 “AP니까 계속 받자”가 아니라 “어떤 write를 계속 받을 수 있고, 어떤 write는 멈춰야 하는가?”로 나뉜다.
판단 축 3: 사용자 영향 기준으로 reliability target을 잡는다
Google SRE의 Embracing Risk 장은 100% 신뢰성을 목표로 하지 말라고 말한다. 어느 지점 이후의 신뢰성 증가는 사용자 가치보다 비용이 더 커질 수 있다. 운영자는 “더 안정적으로”라는 추상 목표를 SLO와 error budget으로 바꿔야 한다.
예를 들어 같은 1분 장애라도 영향은 다르다.
- 결제 승인 API 1분 장애: 직접 매출과 신뢰에 영향이 크다.
- 관리자용 리포트 1분 지연: 공지와 재처리로 흡수 가능할 수 있다.
- 추천 피드 fallback 1분: 정확도는 떨어져도 핵심 거래는 유지된다.
따라서 분산 시스템 판단은 항상 사용자 경로별로 나눠야 한다.
| 경로 | 실패 시 사용자 영향 | 권장 판단 |
|---|---|---|
| 돈, 권한, 재고처럼 되돌리기 어려운 write | 데이터 손상, 금전 손실, 보안 사고 | fail closed, 강한 검증, 수동 승인 가능 |
| 읽기 중심 탐색 경로 | 불편, stale 정보, UX 저하 | stale cache, replica read, degrade 허용 |
| 비동기 처리 경로 | 처리 지연, 알림 지연 | queue buffering, 재시도, DLQ, 재처리 |
| 분석·리포팅 경로 | 의사결정 지연, 숫자 불일치 | freshness 표시, 재계산, lineage 검증 |
SLO가 없으면 운영자는 장애 중에 “빨리 열 것인가, 안전하게 닫을 것인가”를 감으로 결정하게 된다. 반대로 SLO와 error budget이 있으면, 특정 기간에 얼마나 risk를 쓸 수 있는지 팀과 제품 조직이 같은 언어로 말할 수 있다.
판단 축 4: fail open과 fail closed를 기능별로 정한다
장애가 났을 때 모든 것을 닫으면 안전하지만 서비스는 멈춘다. 모든 것을 열면 가용성은 좋아 보이지만 데이터 손상이나 보안 사고가 날 수 있다. 운영자는 기능별로 fail open, fail closed, degrade 중 하나를 미리 정해야 한다.
| 선택 | 의미 | 적합한 경우 | 위험 |
|---|---|---|---|
| fail closed | 조건을 확인할 수 없으면 요청을 거부 | 결제, 권한 변경, 관리자 작업, primary 선출 | 과도한 거부로 가용성 저하 |
| fail open | 확인 시스템이 죽어도 요청을 허용 | 낮은 위험의 읽기, 내부 실험 기능 | 권한 우회, 잘못된 쓰기 가능성 |
| degrade | 핵심 기능만 유지하고 품질을 낮춤 | 추천, 검색 정렬, 통계, 이미지 변환 | fallback이 정상처럼 굳어짐 |
예를 들어 권한 확인 서비스가 장애라고 하자. 관리자 권한 변경 API를 fail open하면 보안 사고가 된다. 반면 공개 상품 목록 조회는 캐시된 결과로 degrade할 수 있다. 같은 의존성 장애라도 호출 경로의 위험도가 다르면 다른 판단이 맞다.
중요한 것은 이 정책을 코드, runbook, 대시보드에 모두 드러내는 것이다. 장애 중에 처음 토론하면 늦다.
판단 축 5: 자동 복구에는 fencing과 검증을 붙인다
분산 시스템 운영에서 자동화는 양날의 검이다. 자동 failover는 RTO를 줄이지만, 잘못된 조건에서 실행되면 split-brain을 만든다. 자동 retry는 transient failure를 숨기지만, overload 상황에서는 retry storm을 만든다. 자동 scale-out은 capacity를 늘리지만, shared DB connection limit을 넘기면 더 큰 장애가 된다.
자동 복구를 설계할 때는 최소한 네 가지 질문을 붙인다.
- Trigger: 어떤 관측 신호가 자동화를 시작하는가?
- Precondition: 실행 전에 반드시 확인해야 할 안전 조건은 무엇인가?
- Fencing: 이전 owner, primary, worker가 같은 일을 계속하지 못하게 막았는가?
- Post-check: 자동화 후 실제 사용자 경로와 데이터 상태가 정상인가?
데이터베이스 primary failover라면 heartbeat timeout만으로 충분하지 않다. old primary의 쓰기 차단, replication position 확인, VIP/LB 전환, application connection 재수립, 복구 후 쓰기 검증이 함께 있어야 한다. Kafka consumer 재처리라면 offset reset 전에 idempotent sink와 중복 처리 기준을 확인해야 한다.
AWS Well-Architected Reliability Pillar가 강조하는 fault isolation도 같은 맥락이다. 장애의 폭발 반경을 fault isolation boundary 안에 가두고, 여러 위치나 bulkhead로 나눠 한 컴포넌트 실패가 전체 workload를 끌고 가지 않게 해야 한다.
판단 축 6: 관측성은 평균이 아니라 경계 조건을 보여야 한다
분산 시스템 장애는 평균 latency보다 tail latency, 전체 성공률보다 특정 shard, 전체 lag보다 특정 partition에서 먼저 드러난다. 운영 대시보드는 “대체로 정상”이 아니라 어느 경계가 위험한지를 보여야 한다.
운영자가 반드시 봐야 하는 지표는 다음과 같다.
| 영역 | 지표 | 판단에 쓰는 방식 |
|---|---|---|
| 일관성 | replication lag, stale read ratio, conflict count | 읽기 경로 전환, write 제한 판단 |
| 합의·리더십 | leader change count, election duration, quorum availability | 자동 failover 안전성 판단 |
| 요청 품질 | p95/p99 latency, timeout rate, retry count | overload와 dependency 장애 구분 |
| 격리 | pool saturation, queue depth, bulkhead reject count | 장애 전파 범위 확인 |
| 복구 | RTO/RPO 측정, restore drill 결과, replay duration | 문서상 목표와 실제 복구 능력 비교 |
| 데이터 품질 | reconciliation diff, duplicate count, missing count | 장애 후 데이터 보정 필요성 판단 |
특히 분산 시스템에서는 “복구됐다”의 기준을 조심해야 한다. 프로세스가 떠 있고 HTTP 200이 나온다고 끝이 아니다. lag가 줄었는지, replay가 완료됐는지, 중복 쓰기가 없는지, downstream materialized view가 따라잡았는지까지 봐야 한다.
운영 의사결정 체크리스트
분산 시스템 설계를 리뷰하거나 장애 중 의사결정을 할 때는 아래 순서로 짧게 확인한다.
- 이 경로의 불변식은 무엇인가?
- stale read, 중복 처리, 지연 처리, 보상 처리를 각각 허용하는가?
- 성공 응답을 반환한 뒤 되돌릴 수 없는 부작용이 있는가?
- 네트워크 파티션에서 어느 쪽이 write를 받을 수 있는가?
- leader/failover 자동화에 fencing이 있는가?
- retry가 downstream 회복을 돕는가, 부하를 키우는가?
- fallback이 사용자에게 명확히 보이는가, 조용히 오류를 숨기는가?
- RTO/RPO는 문서가 아니라 drill로 검증됐는가?
- 장애 후 데이터 검증 쿼리나 reconciliation 절차가 있는가?
- 이 선택의 비용이 사용자 가치와 SLO에 맞는가?
이 체크리스트의 목적은 회의를 길게 만드는 것이 아니다. 반대로 장애 중 토론을 줄이는 것이다. 평소에 이 답을 정리해 두면, 실제 파티션·지연·leader churn 상황에서 팀은 “무엇을 지켜야 하는지”부터 합의할 수 있다.
정리: 운영자는 보장과 비용을 번역하는 사람이다
분산 시스템은 완벽한 선택지를 주지 않는다. 강한 일관성은 추론을 쉽게 하지만 latency와 availability 비용이 생긴다. 높은 가용성은 사용자 경험을 지키지만 stale data와 conflict 처리 비용을 만든다. 자동 복구는 빠르지만 잘못 설계하면 데이터 손상을 자동화한다.
운영자의 역할은 이 tradeoff를 숨기지 않는 것이다.
- CAP 같은 큰 이름보다 실제 보장 문장으로 쓴다.
- 기능별 불변식과 장애 시 정책을 나눈다.
- SLO와 error budget으로 “얼마나 안정적이어야 하는가”를 정한다.
- 자동화에는 fencing, post-check, rollback 기준을 붙인다.
- 평균 지표가 아니라 shard, partition, dependency별 경계 조건을 본다.
좋은 분산 시스템 운영 문서는 “우리 시스템은 안전하다”라고 말하지 않는다. 대신 “어떤 조건에서 무엇을 멈추고, 무엇을 계속하며, 무엇을 나중에 검증하는지”를 분명히 적는다. 그 문장이 실제 장애에서 팀을 지켜준다.
References
- Google SRE Book, Chapter 3: Embracing Risk. https://sre.google/sre-book/embracing-risk/
- Jepsen, Consistency Models. https://jepsen.io/consistency/models
- Jepsen, Linearizability. https://jepsen.io/consistency/models/linearizable
- Martin Kleppmann, “Please stop calling databases CP or AP.” https://martin.kleppmann.com/2015/05/11/please-stop-calling-databases-cp-or-ap.html
- AWS Well-Architected Framework, Reliability Pillar. https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/welcome.html
- AWS Well-Architected Framework, “Use fault isolation to protect your workload.” https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/use-fault-isolation-to-protect-your-workload.html