Infra Architecture/Foundations

Foundations - 02. 인프라 품질 속성

가용성 99.9%는 1년에 9시간 다운되겠다는 약속이다

AWS S3의 공식 문서에는 "11개의 9"라는 숫자가 등장한다. 99.999999999% 내구성(durability). 읽기 쉽게 풀면, 1,000만 개의 객체를 1만 년 동안 저장해도 1개가 손실될까 말까 한다는 뜻이다. 이 숫자가 의미 있으려면 "11개의 9"가 정확히 무엇을 재는지, 가용성(availability)과 내구성(durability)이 어떻게 다른지, 그리고 이런 수치를 SLO(Service Level Objective)로 채택했을 때 무엇을 약속한 건지 각각 명확해야 한다.

흔히 "품질 속성"이라 부르는 "~성"들은 인프라 결정을 갈라놓는 힘이다. 두 인프라가 똑같이 "웹 서비스를 호스팅한다"는 기능을 수행해도 구조가 전혀 달라지는 건 이 속성들 때문이라는 건 foundations 첫 글에서 봤다. 이 글은 한 단계 더 들어간다 — 각 속성을 측정 가능한 숫자로 번역하는 법, 흔히 빠지는 함정, 그리고 속성들끼리 충돌할 때 일어나는 일을 다룬다. 인프라를 공학으로 다룬다는 건, 결국 이 속성들을 숫자로 말할 수 있다는 뜻이다.

가용성 — "안 죽는다"가 아니라 "이 만큼 산다"

가용성(availability)은 정해진 시간 동안 서비스가 정상 동작한 비율이다. 수학적으로는 가용성 = 정상 동작 시간 / (정상 동작 시간 + 장애 시간)이고, 보통 퍼센트로 표현한다. 한 해를 8,760시간(윤년은 8,784)이라 할 때 99.9% 가용성은 연간 8.76시간의 장애를 허용한다.

이 단순한 정의에 두 가지 함정이 숨어 있다. 첫째, 가용성은 평균이 아니라 비율이다. 1년에 한 번 9시간 다운되는 것과, 매일 1분 26초씩 다운되는 게 둘 다 99.9%다. 매일 1분대 장애가 나는 서비스가 사용자 경험에 미치는 타격이 훨씬 크다 — 하지만 가용성 숫자로는 구분이 안 된다. 그래서 가용성만 보고 "신뢰성있다"고 판단하면 위험하다. 신뢰성(reliability)은 장애 빈도까지 포함하는 더 넓은 개념이고, 가용성은 그 부분 집합이다.

둘째, 가용성 숫자는 MTBF(Mean Time Between Failures, 평균 장애 간격)와 MTTR(Mean Time To Recovery, 평균 복구 시간)의 함수로 분해할 수 있다. 수식으로는 가용성 = MTBF / (MTBF + MTTR). 이 분해가 중요한 이유는 가용성을 올리는 길이 두 개라는 걸 보여주기 때문이다 — 장애를 덜 내게(MTBF ↑), 또는 장애가 나도 빨리 복구하게(MTTR ↓). 두 접근은 서로 다른 엔지니어링을 요구한다. MTBF를 올리려면 더 견고한 하드웨어, 더 많은 중복, 더 꼼꼼한 테스트가 필요하고, MTTR을 줄이려면 자동 복구·관측성(observability)·runbook 자동화가 필요하다. SRE가 MTTR에 집중하는 이유다 — MTBF를 0으로 만들 수는 없지만, MTTR을 수 분으로 줄이면 9의 개수를 늘릴 수 있다.

직렬과 병렬 — 가용성을 계산하는 두 가지 방식

시스템 전체 가용성은 컴포넌트들이 어떻게 연결됐는지에 따라 다르게 계산된다. 직렬 구성(한 컴포넌트가 죽으면 전체가 죽는 구조)에서는 전체 가용성이 컴포넌트 가용성의 곱이다. 두 개의 99%짜리 컴포넌트가 직렬로 연결되면 전체는 0.99 × 0.99 = 0.9801, 즉 98.01%다. 직렬이 길어질수록 가용성은 떨어진다.

병렬 구성(둘 중 하나만 살아 있으면 되는 구조, redundancy)은 반대다. 단순한 독립 병렬 두 개일 때 전체 가용성은 1 - (1 - p1) × (1 - p2). 99%짜리 두 개를 병렬로 두면 1 - 0.01 × 0.01 = 0.9999, 즉 99.99%다. 중복(redundancy)이 가용성을 기하급수적으로 올리는 이유가 이 수식에 있다. 다만 "독립"이라는 가정이 핵심 — 두 컴포넌트가 같은 전원·같은 스위치·같은 AZ에 있으면 동시에 죽을 수 있고, 병렬 계산은 무너진다. 이게 멀티 AZ가 단순한 "서버 두 대"가 아니라 "물리적으로 독립된 환경 두 개"를 요구하는 이유다.

flowchart LR
    subgraph SR["직렬 (직렬 가용성 = p1 × p2)"]
        direction LR
        A1["app<br/>99%"] --> B1["DB<br/>99%"]
    end
    subgraph PR["병렬 (병렬 가용성 = 1-(1-p1)(1-p2))"]
        direction TB
        A2["app-1<br/>99%"] --> B2["DB<br/>99%"]
        A3["app-2<br/>99%"] --> B2
    end

직렬 시스템에서 전체 가용성은 가장 약한 고리보다 낮아진다. 반대로 병렬 시스템에서는 중복 컴포넌트가 늘어날수록 가용성이 극적으로 올라간다. 인프라 설계의 많은 결정이 "이 컴포넌트를 직렬로 둘 것인가 병렬로 둘 것인가"라는 질문으로 귀결된다.

9의 법칙과 비용 폭발

01 글에서 맛보기로 다룬 "9의 법칙(nines rule)"을 다시 보자. 9가 하나 추가될 때마다 허용 다운타임은 1/10로 줄고, 필요한 엔지니어링 비용은 대략 10배로 늘어난다. 이게 추상적 경고가 아니라는 건 S3의 11개의 9가 어떻게 달성되는지를 보면 알 수 있다 — 객체를 세 개 이상의 AZ에 걸쳐 최소 11개의 9를 달성하도록 복제하고, erasure coding을 추가하고, 백그라운드 무결성 검사를 돌린다. "11개의 9"는 마케팅 수사가 아니라 저 엔지니어링 투자의 결과 숫자다.

가용성 연간 허용 다운타임 일반적 달성 방법
99% ("두 개의 9") 3.65일 단일 서버 + 정기 재시작
99.9% ("세 개") 8.76시간 중복 컴포넌트 + 자동 복구
99.99% ("네 개") 52.5분 멀티 AZ + 로드 밸런서 + 헬스체크
99.999% ("다섯 개") 5.25분 멀티 리전 active-active + 자동 failover
99.9999% ("여섯 개") 31.5초 우주항공·통신망 수준, 사실상 인간 개입 불가

5개의 9부터는 인간이 개입할 시간이 없다 — 자동화가 모든 복구를 해야 한다. 6개의 9는 "인간의 반응 시간(보통 수십 초에서 수 분)"보다 허용 다운타임이 짧다. 그래서 6개의 9를 목표로 하는 시스템(전통 전화망·항공 관제)은 설계부터 "인간은 장애 원인"이라고 가정하고 전부 자동화한다.

내구성 — 가용성과 다른 축

가용성이 "지금 동작하느냐"라면, 내구성(durability)은 "데이터가 살아 있느냐"다. 서비스가 1년간 단 1초도 안 멈췄어도(가용성 100%), 한 번의 디스크 고장으로 사용자 데이터를 잃으면 내구성은 0에 가까워진다. S3가 "11개의 9 내구성"을 광고하면서 가용성은 "4개의 9(99.99%)"만 발표하는 건 이 때문이다 — 두 속성은 독립적으로 측정되고 독립적으로 달성된다.

디스크 한 대의 연간 고장률을 AFR(Annualized Failure Rate)이라 부른다. CMU와 Google이 2007년에 발표한 대규모 디스크 고장 연구에 따르면 실제 데이터센터 디스크의 AFR은 보통 2 ~ 3%, 일부 모델은 6%까지 올라간다. 디스크 한 대에만 데이터를 저장하면 1년에 2 ~ 3% 확률로 데이터를 잃는다 — 이건 "100개 고객의 데이터를 저장하면 매년 2 ~ 3명 데이터 날아간다"는 뜻이다. 상용 서비스에선 받아들일 수 없는 숫자.

내구성을 올리는 두 가지 전략이 있다. 복제(replication)는 데이터를 N개의 디스크에 복사하는 것이다. 3-way 복제에서 세 디스크가 동시에 고장나지 않는 한 데이터는 살아 있다. 세 디스크가 독립적으로 2% AFR을 가질 때 연간 손실 확률은 0.02^3 = 0.000008, 즉 0.0008% — 디스크 한 대의 25배 안전하다. 단, "독립적"이라는 가정이 또 등장한다 — 같은 배치(bad batch)의 디스크들이 연쇄 고장을 일으키는 현상은 3-way 복제도 뚫을 수 있다.

Erasure coding은 복제보다 저장 공간 효율적인 방법이다. 데이터를 k개 조각으로 쪼개고 m개의 패리티(parity) 조각을 추가해, k+m 중 임의의 k개만 있으면 원본을 복원할 수 있게 만든다. 예를 들어 10+4 erasure coding은 14개 노드 중 10개만 살아 있으면 데이터가 살아 있다. 3-way 복제 대비 저장 효율은 14/10 = 1.4배로, 3-way 복제의 3배보다 훨씬 적다. 대신 CPU·네트워크 오버헤드가 크고 복원 시간이 길다. S3·GCS·Azure Blob이 대용량 객체에 erasure coding을 쓰는 이유가 이 효율 때문이다.

전략 저장 효율 장애 허용 복잡도
단일 디스크 1x 0개 (디스크 1개 고장=데이터 손실) 낮음
2-way 복제 (mirroring) 0.5x 1개 낮음
3-way 복제 0.33x 2개 낮음
RAID 5 (k=4, m=1) 0.75x 1개 중간
RAID 6 (k=4, m=2) 0.67x 2개 중간
Erasure coding 10+4 0.71x 4개 높음
Erasure coding 10+6 0.625x 6개 높음

"내구성 99.999999999%"라는 숫자는 보통 이런 erasure coding이나 대규모 복제를 깔았을 때 계산으로 나오는 값이다. 단순한 마케팅이 아니라 저장 아키텍처가 결정하는 수학적 결과다.

지연 — "평균"이 숨기는 꼬리

지연(latency)은 요청을 보낸 시각부터 응답을 받은 시각까지의 시간이다. 초(ms) 단위로 잰다. 흔히 "평균 응답 시간 100ms"라는 식으로 표현하는데, 인프라 엔지니어링에선 평균을 거의 쓰지 않는다. 이유는 평균이 꼬리 지연(tail latency)을 숨기기 때문이다.

지연 분포를 이해하려면 백분위수(percentile)를 봐야 한다. p50은 중간값(요청의 50%가 이보다 빠르다), p95는 95%가 이보다 빠르고 5%는 느리다는 뜻, p99는 99%가 이보다 빠르고 1%는 느리다는 뜻이다. 하루 100만 건 요청을 받는 서비스에서 p99가 500ms라면, 매일 1만 건의 요청이 500ms 이상 걸린다.

평균이 왜 함정인지 예로 보자. 100개 요청 중 99개가 10ms, 1개가 1,000ms 걸렸다고 치자. 평균은 (99 × 10 + 1 × 1000) / 100 = 19.9ms. "평균 20ms"라는 숫자는 대부분의 사용자 경험(10ms)도, 극단적으로 느린 사용자 경험(1,000ms)도 반영하지 못한다. p50은 10ms, p99는 1,000ms — 이게 진짜 그림이다. Google의 Jeff Dean이 2013년 WSDC 발표에서 강조한 "tail at scale" 문제는 이것이다 — 분산 시스템에서는 한 대의 느린 서버가 전체 요청의 지연을 결정한다.

지연을 말할 땐 어떤 백분위수를 말하는지 명시해야 한다. "p99 500ms 이하"는 의미 있는 SLO지만 "평균 100ms 이하"는 시스템 상태를 거의 알려주지 않는다.

지연과 처리량은 다르다

흔히 섞는 두 속성이 지연과 처리량이다. 지연이 "하나의 요청이 얼마나 빨리 끝나나"라면, 처리량(throughput)은 "단위 시간당 몇 개의 요청을 처리하나"다. 두 값은 연관돼 있지만 독립적이다 — 지연이 10ms인 서비스가 초당 100만 건을 처리할 수도 있고, 지연이 1ms인 서비스가 초당 10건밖에 못 할 수도 있다. Little's Law(L = λW)가 둘을 잇는 공식이지만, 실무에서는 보통 각각 따로 잰다.

지연이 낮으면 처리량이 높아지는 경향이 있지만, 그 관계는 선형이 아니다. 동시성(concurrency)이 개입하기 때문이다. 같은 지연이라도 동시에 처리할 수 있는 요청 수(concurrency)가 크면 처리량이 그만큼 커진다. 이게 비동기 I/O·논블로킹 서버가 높은 처리량을 내는 원리 — 지연 자체는 줄지 않아도, 한 서버가 동시에 처리하는 요청 수가 늘어난다.

처리량 — 단위가 다르면 의미가 다르다

처리량은 측정 단위에 따라 완전히 다른 이야기를 한다. 인프라에서 자주 쓰는 단위 네 개가 있다.

  • RPS(Requests Per Second) — HTTP 요청 수. 웹 서비스의 표준 단위. nginx 로그·APM 도구가 기본으로 잰다.
  • IOPS(Input/Output Operations Per Second) — 블록 장치(디스크)의 읽기/쓰기 작업 수. 데이터베이스 성능의 병목이 자주 여기에 있다.
  • pps(packets per second) — 네트워크 패킷 수. 방화벽·로드 밸런서·가상 스위치 성능의 병목.
  • bps(bits per second) — 초당 비트 수. 대역폭(bandwidth). 보통 Gbps, 10Gbps, 40Gbps 단위로 표현.

단위가 다른 처리량은 단순 비교가 안 된다. 10Gbps NIC이 곧 1,000만 pps를 의미하지 않는다 — 패킷 크기가 작으면 pps가 병목이고, 패킷 크기가 크면 bps가 병목이다. 64바이트 패킷으로 10Gbps를 채우려면 초당 약 1,488만 패킷이 필요한데, 대부분의 일반 NIC은 그 pps를 못 버틴다. 이게 네트워크 성능 이야기에서 "대역폭"과 "pps"를 항상 함께 말해야 하는 이유다.

데이터베이스 성능도 마찬가지다. SSD의 "읽기 100,000 IOPS"라는 스펙은 보통 4KB 블록을 기준으로 한다. 64KB 블록으로 읽으면 IOPS는 줄고, 처리량(MB/s)은 다른 병목에 걸린다. 인프라 결정에서 "이 디스크가 우리 워크로드를 감당하나"라는 질문은 단순한 IOPS 비교로 답이 안 나온다 — 워크로드의 블록 크기 분포·읽기/쓰기 비율·랜덤/순차 패턴까지 봐야 한다.

확장성 — 수직과 수평이 지시하는 다른 길

확장성(scalability)은 부하가 늘어날 때 처리 능력을 어떻게 키우는가를 다룬다. 두 가지 기본 전략이 있고, 이 둘은 인프라의 거의 모든 결정을 갈라놓는다.

수직 확장(scale-up)은 한 서버를 더 크게 만드는 것이다. CPU를 추가하고, 메모리를 늘리고, 더 빠른 디스크를 단다. 장점은 단순함 — 코드를 안 바꿔도 된다. 단점은 한계가 빨리 온다 — 가장 큰 서버의 크기가 물리적 한계(소켓 수·DIMM 슬롯·PCIe 레인)에 부딪힌다. 또한 비용이 비선형으로 폭발한다 — 가장 큰 서버는 두 번째로 큰 서버보다 2 ~ 3배 비싸다.

수평 확장(scale-out)은 서버 대수를 늘리는 것이다. 서버 10대 → 100대 → 1,000대. 장점은 이론상 한계가 없고, 비용이 선형적이다. 단점은 코드가 수평 확장을 지원해야 한다 — 상태(state)를 서버 안에 두면 안 되고, 세션·캐시·파일을 외부화해야 한다. 데이터베이스를 여러 서버에 쪼개는 샤딩(sharding)은 수평 확장의 가장 어려운 형태다.

flowchart TD
    subgraph UP["수직 확장 (scale-up)"]
        direction LR
        S1["서버 (CPU 4, RAM 16GB)"] --> S2["서버 (CPU 32, RAM 256GB)"]
    end
    subgraph OUT["수평 확장 (scale-out)"]
        direction TB
        W1["서버 1 (CPU 4)"]
        W2["서버 2 (CPU 4)"]
        W3["서버 N (CPU 4)"]
    end

Gene Amdahl이 1967년에 정리한 Amdahl의 법칙은 병렬화(수평 확장)가 줄 수 있는 가속에 상한이 있음을 보인다. 작업 중 병렬화 가능한 부분이 p, 순차적(직렬)으로 처리해야 하는 부분이 1-p라면, N개 자원으로 얻을 수 있는 최대 가속은 1 / ((1-p) + p/N)다. N이 무한대로 가면 가속은 1 / (1-p)에 수렴한다 — 즉 순차적 부분이 10%만 있어도(p=0.9), 수평 확장으로 얻을 수 있는 최대 가속은 10배다. 이게 "수평 확장하면 무한히 빨라진다"는 통념을 깨는 정리다. 진짜 병목은 항상 직렬 부분에 있다.

확장성을 논할 때 빠지는 흔한 함정이 "선형 확장"이라는 말이다. "우리 시스템은 선형 확장한다"는 "서버 대수를 두 배로 늘리면 처리량이 두 배로 늘어난다"는 뜻인데, 이는 거의 항상 부분적으로만 참이다. 공유 자원(DB·캐시·네트워크)이 병목이 되고, 조정 비용(coordination cost)이 늘어난다. 실제 시스템의 확장 곡선은 보통 선형으로 시작하다가 S자로 꺾인다.

일관성 — CAP와 PACELC가 말하는 트레이드오프

일관성(consistency)은 인프라에서 두 가지 다른 의미로 쓰인다. 혼동하기 쉬운 부분이라 명확히 짚고 가야 한다.

첫째, 데이터 일관성은 "여러 복제본이 같은 값을 보느냐"다. strong consistency(강한 일관성)는 모든 읽기가 가장 최근 쓰기를 반영하는 것을 보장하고, eventual consistency(최종 일관성)는 시간이 지나면 결국 같아지지만 그 전에는 일시적으로 다를 수 있다. 데이터베이스 복제·분산 저장소의 핵심 축이다.

둘째, 트랜잭션 일관성은 ACID의 C로, 트랜잭션 전후에 데이터 무결성 제약(외래키·유니크 등)이 유지되는 것을 말한다. 이건 데이터베이스 내부 이야기이고 인프라 레벨에선 거의 다루지 않는다. 같은 단어를 쓰지만 축이 다르다.

인프라에서 "일관성"이라 하면 보통 첫째 의미(복제 일관성)를 가리킨다. 그리고 이 일관성은 가용성·지연과 강하게 충돌한다. 그 충돌을 정리한 정리가 Brewer가 2000년에 발표하고 Gilbert-Lynch가 2002년에 형식화한 CAP 정리다. CAP는 일관성(C, Consistency)·가용성(A, Availability)·분단 내성(P, Partition tolerance)의 세 속성 중 분단(P)이 발생했을 때 나머지 둘(C, A) 중 하나를 포기해야 한다는 정리다. 대중적 오해가 하나 있는데, "3개 중 2개 고른다"가 아니다 — 분단은 네트워크 속성이라 "선택"하는 게 아니라 일어나는 것이다. 따라서 실제 선택지는 "분단이 났을 때 C와 A 중 어느 쪽을 포기할 것인가"뿐이다.

Abadi가 2010년에 제안한 PACELC는 CAP를 보완한다. 분단(P)이 발생하면(then) C와 A 중 택일하고, 분단이 없을 때(else)에는 지연(L, Latency)과 C 중 택일해야 한다는 정리다. 즉 분단이 없는 정상 상황에서도 일관성과 지연은 트레이드오프 관계다 — strong consistency를 강제하려면 모든 복제본에 동기 쓰기를 해야 해서 지연이 늘어난다. 이게 왜 eventual consistency 시스템(DynamoDB·Cassandra)이 strong consistency 시스템(Spanner·etcd)보다 기본적으로 빠른지 설명한다.

상황 시스템 예 PACELC 선택 함의
분단 발생 (PA), 정상 (EL) Cassandra, DynamoDB 가용성 + 지연 우선 분단 시 일관성 양보, 정상 시 지연 최소화
분단 발생 (PC), 정상 (EL) MongoDB (기본) 일관성 + 지연 우선 분단 시 거부, 정상 시 지연 최소화
분단 발생 (PA), 정상 (EC) 베이스라인 eventual 가용성 + 일관성 우선 분단 시 일관성 양보, 정상 시 동기 복제로 지연 증가
분단 발생 (PC), 정상 (EC) Spanner, etcd 일관성 + 일관성 우선 항상 strong consistency, 지연 비쌈

"어느 일관성 모델을 고를 것인가"는 비즈니스 요구사항에서 역산된다. 결제 시스템은 strong consistency가 필요 — 잔액이 일시적으로라도 다르면 안 된다. 소셜 피드의 좋아요 수는 eventual consistency로 충분 — 몇 초 늦게 동기화돼도 사용자가 모른다. 강한 일관성을 모든 곳에 요구하면 비용·지연이 올라가고, eventual consistency를 모든 곳에 쓰면 사용자가 직면하는 불일치가 늘어난다. 인프라 아키텍처의 일관성 결정은 결국 "이 데이터는 얼마나 정확해야 하는가"라는 질문에 답하는 일이다.

비용 — 0번 품질 속성

비용(cost)은 다른 품질 속성들과 별도로 다뤄야 할 게 아니라 그 자체로 품질 속성이다. "최고의 가용성"을 비용 무시하고 설계하면 회사가 망한다. 비용을 인프라 결정에서 0번 품질 속성으로 두는 건 그래서 합리적이다.

비용의 큰 축 두 개가 있다. Capex(Capital Expenditure, 자본 지출)는 서버·네트워크 장비·데이터센터 장비를 살 때 한 번에 지출하는 돈이다. 감가상정(depreciation)이 일어나지만, 한 번 사면 수년간 쓴다. Opex(Operational Expenditure, 운영 지출)는 매월·매시간마다 내는 돈이다. 클라우드 과금이 대표적 — 서버 한 대를 한 시간 쓰면 그만큼 낸다.

온프레미스는 capex 중심, 클라우드는 opex 중심이다. 둘은 단순히 "싸냐 비싸냐"로 비교하면 안 된다 — 자본의 기회비용, 예측 가능성, 유연성이 다르다. 트래픽이 안정적이고 장기적으로 예측 가능하면 capex(온프레미스)가 보통 싸다. 트래픽이 불규칙하거나 글로벌 확장 가능성이 높으면 opex(클라우드)가 유리하다. 같은 인프라라도 사용 패턴에 따라 정답이 다르다.

비용을 측정할 때 단위당 비용(unit economics)이 중요하다. 저장소는 GB당 월 비용($/GB/month), 컴퓨트는 vCPU-시간당 비용($/vCPU-hour), 네트워크는 송신 GB당 비원($/GB egress), 데이터베이스는 IOPS당·요청당 비용(RU, Request Units). 이 단위당 비용이 다르면 "같아 보이는" 두 서비스가 실제로는 완전히 다른 비용 구조를 갖는다. AWS S3는 GB당 싸지만 요청당 비용이 있어서 작은 객체 많이 저장하면 비용이 폭발하고, AWS EBS는 GB당 비싸지만 IOPS가 포함이라 큰 블록 장치에 유리하다. 워크로드에 따라 정답이 다르다.

품질 속성 시나리오 — 측정 가능하게 만드는 틀

지금까지 본 "~성"은 그 자체로는 SLO가 될 수 없다. "높은 가용성", "낮은 지연", "강한 일관성"은 측정 불가능한 추상이다. Bass·Clements·Kazman이 에서 제안한 품질 속성 시나리오(quality attribute scenario)는 이 추상을 측정 가능한 명세로 번역하는 틀이다. 네 요소로 구성된다.

  1. 자극(stimulus) — 무엇이 일어나는가. ("결제 노드 한 대가 장애를 일으킨다")
  2. 환경(environment) — 그것이 어떤 상황에서 일어나는가. ("정상 영업 시간에, 피크 시간대에")
  3. 응답(response) — 시스템이 어떻게 반응해야 하는가. ("다른 노드로 요청을 우회시킨다")
  4. 측정(response measure) — 그 응답이 얼마나 잘 됐는지 어떻게 알 수 있는가. ("30초 이내 복구, 연간 가동률 99.95% 유지")

"높은 가용성"이라는 추상은 위 네 요소로 채워져야 비로소 엔지니어가 구현할 수 있고 운영자가 측정할 수 있는 목표가 된다. 품질 속성 시나리오가 없으면 "가용성이 중요하다"는 합의는 있되 "어떤 장애 상황에서 어떤 수치로 측정할 것인가"는 합의가 없는 상태가 되고, 그건 장애가 났을 때 누구도 같은 페이지에 없다는 뜻이다.

이 시나리오는 인프라 설계에 즉시 도움이 된다. "결제 노드 1대 장애 시 30초 이내 복구"라는 SLO가 있으면, 그 아래 설계는 자동으로 도출된다 — 헬스체크 간격은 30초보다 짧아야 하고, 로드 밸런서의 헬스체크 실패 임계값은 수 초 이내여야 하고, 백업 노드가 항상 준비돼 있어야 한다. 반대로 "높은 가용성"이라는 추상은 아무 설계도 이끌어내지 못한다.

측정의 함정 — Goodhart의 법칙

품질 속성을 측정 가능하게 만들면 생기는 새로운 문제가 있다. 영국 경제학자 Charles Goodhart가 1975년에 정리한 Goodhart의 법칙은 이렇게 말한다 — "측정 지표가 목표가 되면, 그 지표는 더 이상 좋은 지표가 아니다." 인프라 세계에서도 똑같이 성립한다.

p99 지연을 SLO로 채택하면, 엔지니어는 p99 안에 들게 하는 데 집중한다. 그 과정에서 p99.9(훨씬 느린 0.1%)이 어떻게 되는지는 추적을 안 하게 된다 — 그럼 p99.9가 폭발해도 p99 SLO는 "달성"이다. 가용성을 SLO로 채택하면, 엔지니어는 "장애 시간"의 정의를 유리하게 바꾸려는 유혹을 받는다 — "예약된 유지보수 시간은 장애에서 제외", "부분 장애는 전체 장애에서 제외". SLO는 달성되는 것처럼 보이지만 실제 사용자 경험은 개선되지 않는다.

이 함정을 피하는 유일한 방법은 측정 지표를 여러 개 두고 서로 검증하게 하는 것이다. 가용성만 보지 말고 지연 분포도 보고, p99만 보지 말고 p99.9도 보고, "시스템이 정상"이라는 판단을 단일 지표에 의존하지 않는다. Marilyn Strathern이 1997년에 인류학 맥락에서 정리한 형태("When a measure becomes a target, it ceases to be a good measure")가 현대 인프라에도 그대로 적용된다. 측정은 필수지만, 측정에 대한 건강한 회의도 같이 필수다.

실습 — 직접 확인하기

품질 속성을 손으로 만져보는 기본 명령들이다. 리눅스 서버(또는 WSL2)에서 실행 가능하다.

1. 지연 측정 — ping과 curl

# 로컬 RTT(Round Trip Time)를 잰다. 같은 호스트면 ~0.02ms가 나와야 정상.
ping -c 5 127.0.0.1

확인할 것: time 필드. 네트워크 지연의 기본 단위. 단일 홉의 지연이 1ms 미만인 게 정상. 10ms 이상이면 무언가 잘못됐다.

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.022 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.019 ms
...
rtt min/avg/max/mdev = 0.017/0.020/0.026/0.005 ms
# HTTP 요청의 단계별 지연을 분해한다.
curl -s -o /dev/null -w "dns:%{time_namelookup}s  connect:%{time_connect}s  ttfb:%{time_starttransfer}s  total:%{time_total}s\n" https://example.com

확인할 것: dns(DNS 질의), connect(TCP 연결), ttfb(Time To First Byte, 서버 처리 + 응답 시작), total(전체). 어느 단계가 병목인지 한 줄로 보인다. ttfb가 connect보다 훨씬 크면 서버 처리가 느린 거고, connect가 크면 네트워크 RTT가 느린 것이다.

dns:0.045s  connect:0.062s  ttfb:0.125s  total:0.130s

2. 처리량 측정 — iperf3 (네트워크), dd (디스크)

# iperf3 서버를 먼저 띄운다 (다른 터미널이나 다른 호스트)
iperf3 -s
# 클라이언트에서 처리량 측정
iperf3 -c 127.0.0.1 -t 10

확인할 것: bitrate 필드. 루프백이면 수십 Gbps가 나온다. 물리 NIC에선 1Gbps·10Gbps 등 회선 대역폭에 가까운 값이 나와야 한다.

[  5]   0.00-10.00  sec  37.5 GBytes  32.2 Gbits/sec    0             receiver
# 디스크 순차 쓰기 처리량 측정. 1GB를 1MB 블록으로 쓴다.
dd if=/dev/zero of=/tmp/testfile bs=1M count=1024 oflag=direct
rm /tmp/testfile  # 측정 뒤 정리

확인할 것: 초당 쓰기 속도. NVMe SSD는 1 ~ 3GB/s, SATA SSD는 500MB/s 전후, HDD는 100 ~ 200MB/s. oflag=direct가 커널 페이지 캐시를 우회해 실제 디스크 속도를 잰다.

1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.42 s, 2.5 GB/s

3. IOPS 측정 — fio

# fio가 설치돼 있으면 랜덤 읽기 IOPS를 잰다. 4KB 블록, 깊이 32.
fio --name=randread --ioengine=libaio --iodepth=32 --rw=randread --bs=4k --direct=1 --size=1G --runtime=20 --time_based --filename=/tmp/fiotest
rm /tmp/fiotest

확인할 것: IOPS 필드. NVMe SSD는 100,000 ~ 500,000 IOPS, SATA SSD는 30,000 ~ 100,000. HDD는 100 ~ 200 IOPS. 데이터베이스 성능 예측의 기본 숫자.

randread: (groupid=0, jobs=1): err= 0: pid=12345: Mon Jul 21 10:00:00 2026
  read: IOPS=342k, BW=1337MiB/s (1402MB/s)(26.1GiB/20002msec)

4. 가용성 — uptime과 systemctl

# 시스템이 얼마나 오래 켜져 있었는지 본다. 단순 uptime은 SLO가 될 수 없다 (참고용).
uptime

확인할 것: load averageup 시간. 이건 단일 서버의 uptime이지 서비스 가용성이 아니다 — 실제 가용성은 외부 모니터링(pingdom·Datadog synthetics)에서 잰다. 단일 서버 uptime이 99%여도 서비스는 99.99%일 수 있다(장애 시 자동 failover). 가용성 SLO는 단일 컴포넌트가 아니라 사용자가 겪는 서비스 단위로 정의해야 한다.

# systemd 서비스가 몇 번이나 재시작됐는지 본다. (N restarts = MTBF 데이터)
systemctl show nginx --property=NRestarts,ActiveEnterTimestamp

미검증: 위 출력의 정확한 숫자는 하드웨어·커널·배포판에 따라 다르다. fio는 apt install fio 또는 dnf install fio로 설치. iperf3도 마찬가지.

비교·감지 표 — 흔한 오해와 진단 체크리스트

지연 SLO 비교 — 무엇을 측정하느냐에 따라 다른 답

지표 의미 언제 쓰나 함정
avg (평균) 모든 요청의 산술평균 절대 SLO로 쓰면 안 됨 꼬리를 숨긴다
p50 (중간값) 절반의 요청이 이보다 빠름 일반 사용자 경험 느린 사용자를 무시
p95 95%가 이보다 빠름 대다수 사용자 경험 상위 5%는 여전히 안 보임
p99 99%가 이보다 빠름 일반적 SLO 단위 1%는 여전히 안 보임
p99.9 99.9%가 이보다 빠름 엄격한 SLO (큰 서비스) 측정 비용 큼
max (최대) 가장 느린 요청 거의 쓰면 안 됨 이상치 하나에 좌우

품질 속성 충돌 매트릭스 — 한 속성을 올리면 다른 속성이 내려가는 경우

올리면 이 속성이 흔들린다 이유
가용성 (9 추가) 비용 (≈10배) 중복·자동화 필요
가용성 (redundancy) 복잡도 더 많은 컴포넌트 관리
내구성 (복제 수) 비용, 쓰기 지연 더 많은 디스크·동기화
Strong consistency 가용성(CAP), 지연(PACELC) 동기 합의 필요
지연 (낮추기) 비용 더 가까운 리전·더 빠른 하드웨어
처리량 복잡도, 비용 더 큰 서버 또는 샤딩
수평 확장성 일관성 분산 시 합의 비용

잘못된 SLO 감지 체크리스트

  • "높은 가용성"이라는 추상 표현이 SLO 문서에 있다 → 측정 가능한 숫자(99.X%, 연간 Y시간)로 바꿀 것
  • "평균 응답 시간"을 SLO로 쓰고 있다 → p99나 p99.9로 바꿀 것
  • "예약된 유지보수 시간"을 장애 시간에서 제외하고 있다 → 사용자가 겪은 다운타임이 진짜 SLO. 자료에 포함할 것
  • 단일 컴포넌트 가용성만 SLO로 있다 → 사용자가 겪는 end-to-end 서비스 가용성을 SLO로 둘 것
  • 9를 추가하면 비용이 10배 든다는 걸 SLO 설계에 반영 안 했다 → 비즈니스가 감당할 수 있는 다운타임 비용에서 역산할 것
  • 일관성 요구사항을 명시하지 않았다 → strong vs eventual 중 어느 쪽인지 각 데이터마다 명시할 것

품질 속성을 숫자로 말한다는 것

도입의 S3 예로 돌아가자. "11개의 9"가 의미하는 것을 이 글의 언어로 번역하면 — S3 객체 복제는 erasure coding으로 99.999999999% 내구성을 달성하고, 가용성은 별개의 속성으로 99.99%(4개의 9)를 발표하며, 두 수치는 서로 다른 아키텍처 결정의 결과라는 뜻이다. "S3는 신뢰성있다"는 통념적 평가는 이 수학적 분해 뒤에 있다. 통념이 나쁜 건 아니지만, 인프라를 설계하고 운영하는 사람은 통념의 자리에 숫자를 놓아야 한다.

품질 속성을 숫자로 말할 수 있어야 하는 이유는 거기에 있다. "높은 가용성"이라는 합의는 아무것도 이끌어내지 못하지만, "결제 노드 장애 시 30초 이내 failover, 연간 99.95%"라는 시나리오는 헬스체크 주기·백업 노드 수·failover 자동화 여부를 즉시 도출한다. "낮은 지연"은 측정 불가능하지만 "p99 200ms"는 측정 가능하고 알림을 설정할 수 있다. 인프라 공학이 직감에서 측정으로 옮아간다는 건, 이렇게 모든 "~성"을 숫자와 시나리오로 번역할 수 있다는 뜻이다.

다음 글(03-fault-domains.md)은 이 품질 속성을 깨는 사건이 어떻게 퍼지는지를 다룬다 — 장애 도메인과 blast radius. 가용성 수학은 "독립된 복제본"을 가정하지만, 현실의 인프라에선 복제본이 얼마나 독립적인지가 전부라는 걸 보게 될 것이다.


참고

  • Bass, L., Clements, P., Kazman, R. 4th ed, Addison-Wesley, 2021, Ch.2·Ch.4 — 품질 속성·architectural drivers·품질 속성 시나리오(자극·환경·응답·측정). 접근 2026-07-21
  • 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 — 대규모 데이터센터 디스크 고장률(AFR) 실측 연구. 접근 2026-07-21
  • Pinheiro, E., Weber, W.-D., Barroso, L. A. "Failure Trends in a Large Disk Drive Population", FAST 2007 — Google의 디스크 고장률 연구. 접근 2026-07-21
  • Dean, J. "The Tail at Scale", Communications of the ACM, Vol. 56, 2013 — 분산 시스템의 꼬리 지연 문제. 접근 2026-07-21
  • Brewer, E. "Towards Robust Distributed Systems", PODC 2000 keynote — CAP 직관. 접근 2026-07-21
  • Gilbert, S., Lynch, N. "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services", SIGACT News 2002 — CAP 형식화. 접근 2026-07-21
  • Abadi, D. "Consistency Tradeoffs in Modern Distributed Database System Design", IEEE Computer 2012 — PACELC 정리. 접근 2026-07-21
  • Amdahl, G. M. "Validity of the Single Processor Approach to Achieving Large Scale Computing Capabilities", AFIPS 1967 — Amdahl의 법칙. 접근 2026-07-21
  • AWS S3 Service Level Agreement — 공식 가용성·내구성 수치. 접근 2026-07-21. URL: https://aws.amazon.com/s3/sla/
  • Strathern, M. "Improving Ratings": Audit in the British University System", European Review, Vol. 5, 1997 — Goodhart의 법칙의 인류학적 공식화. 접근 2026-07-21
  • Kleppmann, M. , O'Reilly, 2017, Ch.5 — 복제·일관성 모델. 접근 2026-07-21