데이터 중심 애플리케이션 설계 Ch 1. 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션

작성일: 2021-09-23 23:29

# 데이터 중심 애플리케이션?

데이터 양, 데이터 복잡성, 데이터가 변하는 속도 등 데이터가 주요 도전 과제인 애플리케이션을 데이터 중심적(data-intensive)이라고 한다. 반대로 CPU 사이클이 병목인 경우 계산 중심적(compute-intensive)이라고 한다.

오늘날 많은 애플리케이션은 데이터 중심적으로 DataBase(DB), Cache, Search Index, Stream Processing, Batch Processing 등을 필요로 한다.

대부분의 데이터 중심적 소프트웨어 시스템에서 중요하게 여기는 세 가지 관심사에 신뢰성, 확장성, 유지보수성이 있다.

# 신뢰성(Reliability)

하드웨어 결함, 소프트웨어 결함, 휴먼 에러와 같은 상황에서도 시스템은 지속적으로 올바르게 동작해야 한다.

# 결함(fault)과 장애(failure)

  • 잘못된 수 있는 일을 결함이라고 부른다. 결함을 예측하고 대처할 수 있는 시스템을 내결함성(fault-tolerant) 또는 탄력성(resilient)을 지녔다고 말한다.
  • 결함은 장애와 동일하지 않다. 결함은 사양에서 벗어난 시스템의 한 구성요소로 정의되지만, 장애는 사용자에게 필요한 서비스를 제공하기 못하고 시스템 전체가 멈춘 경우다.
    • 결함 확률을 0으로 줄이는 것은 불가능하므로, 결함으로 인해 장애가 발생하지 않게 내결함성 구조를 설계하는 것이 가장 좋다.

# 확장성(Scalability)

시스템의 부하(데이터 양, 트래픽 양, 복잡도 등)가 증가할 때 대처할 수 있는 시스템의 능력이다.

# 부하 기술하기

  • 현재 시스템의 부하를 간결하게 기술할 수 있어야 한다. 부하는 부하 매개변수(초당 요청 수, DB Read and Write Ratio, Active User, Cache Hit Ratio 등)로 나타낼 수 있다.

# 트위터 사례

  • 트위터의 주요 두 가지 동작은 트윗 작성(팔로워에게 새로운 메시지 게시)과 홈 타임라인(팔로우한 사람이 작성한 트윗 확인)이다.
  • 트위터에서 확장성 문제는 주로 트윗 작성양이 아니라 해당 트윗을 팔로워에게 전파하는 것이다.
  • 이를 구현하기 위한 두 가지 방법이 존재한다.
    1. 사용자가 홈 타임라인을 요청할 때 팔로우하는 사람들 찾아 트윗을 가져오는 쿼리를 작성한다.
    2. 각 사용자별 홈 타임라인 캐시를 유지하고, 트윗을 작성하면 팔로워들의 홈 타임라인 캐시에 추가한다.
  • 평균적으로 트윗 작성보다 홈 타임라인 읽기 요청량이 훨씬 많기때문에 쓰기에 많은 일을하고, 읽기에 적은일을 하는 2번째 방식이 더 효율적이다.
  • 팔로워가 매우 많다면?
    • 팔로워가 3천만명이라면 2번째 방식은 트윗 작성 시 3천만건의 쓰기 요청이 필요할지도 모른다. 이런 경우엔 1번째 방식을 사용할 수 있게 하는게 효율적일 것이다.
  • 트위터 사례에서 사용자당 팔로워의 분포가 부하에 큰 영항을 미치기 때문에 확장성을 논의할 때 핵심 부하 매개변수가 된다.

# 성능 기술하기

  • 시스템의 부하를 기술하고나면 부하가 증가했을 때 어떤 일이 일어나는지 조사할 수 있다.
    • 부하 매개변수를 증가시키고 시스템 자원을 유지하면 성능은 어떻게 될까?
    • 부하 매개변수를 증가시켰을 때 성능을 유지하기 위해선 시스템 자원을 얼마나 늘려야 될까?

위 질문을 답변하기 위해선 성능을 판단할 수 있는 수치가 필요하다.

  • 보통 온라인 시스템에서는 응답 시간(response time)이 더 중요하다.
  • 보통 일괄 처리 시스템에서는 처리량(throughput)이 더 중요하다.

# 지연 시간(latency)과 응답 시간(response time)

  • 지연 시간은 요청이 처리되길 기다리는 시간으로 서비스를 기다리며 휴지 상태인 시간을 말한다.
  • 응답 시간은 클라이언트 관점에서 본 시간으로, 요청을 처리하는 실제 시간외에도 네트워크 지연과 큐 지연도 포함한다.

# 응답 시간은 백분위(percentile)를 사용하자

  • 응답 시간은 단순히 평균 값이 아니라 백분위를 활용하여 상황에 따라 기준에 맞게 사용하는 것이 좋다.
  • 중앙값을 위해선 p50을 사용할 수 있을 것이다. 상위 백분위는 주로 p95, p99, p999가 일반적으로 사용된다.
    • p95의 응답 시간이 1.5초라면 100개 요청 중 95개의 응답시간은 1.5초 미만, 5개는 1.5초 이상을 의미한다.
  • 아마존은 p999를 주로 사용하는데 이는 1000개 요청 중 가장 느린 1건을 포착한다. 이를 사용하는 이유는 보통 응답 시간이 가장 느린 요청을 경험한 고객들이 많은 구매를 해서 고객 중에 가장 많은 데이터를 보유한 소중한 고객이기 때문이다.

# 부하 대응 접근 방식(scale up vs scale out)

  • 확장성은 보통 용량 확장(scale up, 수직 확장)과 규모 확장(scale out, 수평 확장)으로 구분한다.
    • 가능하면 낮은 사양의 장비를 여러대로 분산할 수 있는 수평 확장이 효율적이지만 수평 확장이 어려운 서비스도 존재한다.
  • stateless 서비스는 수평 확장이 용이하지만 데이터 베이스와 같은 stateful 서비스의 경우엔 수평 확장을 하는데 많은 복잡성이 요구된다.

# 확장성을 갖춘 아키텍처

  • 모든 상황에 맞는 확장성을 보유한 아키텍처는 없다. 아키텍처를 결정하는 요소는 읽기의 양, 쓰기의 양, 저장할 데이터의 양, 응답 시간 요구사항, 접근 패턴등 다양하다.
  • 1kB크기의 데이터 100,000건의 요청을 처리하는 시스템과 2GB크기의 데이터 3건의 요청을 처리하는 시스템은 서로 같은 처리량이라해도 아키텍처는 매우 다르다.

특정 애플리케이션에 적합한 확장성을 갖춘 아키텍처는 주요 동작이 무엇이고, 잘 하지 않는 동작이 무엇인지에 대한 가정으로 구축된다. 이 가정은 부하 매개변수다.

  • 이 가정이 잘못되면 확장에 대한 엔지니어링 노력은 헛수고가 된다. 검증되지 않은 제품의 경우 미래를 대비하기보단 빠르게 반복해서 제품 기능을 개선하는 작업이 좀 더 중요하다.

# 유지보수성(Maintainbility)

소프트웨어 비용의 대부분은 초기 개발이 아니라 지속해서 이어지는 유지보수에 들어간다. 이런 유지보수에는 버그 수정, 시스템 운영, 장애 조사, 새로운 플래폼 적응, 기술 부채 상환, 새로운 기능 추가등 다양하다.

  • 유지보수성은 시스템에서 작업하는 엔지니어와 운영 팀의 삶을 개선하는데 있다.

유지보수성을 위한 소프트웨어 시스템 설계 원칙은 다음과 같다.

# 운용성

  • 운영팀이 시스템을 원할하게 운영할 수 있게 쉽게 만들어라.

# 단순성

  • 시스템에서 복잡도를 최대한 제거해 새로운 엔지니어가 이해하기 쉽게 만들어라.
  • 복잡도는 다양한 증상으로 나타난다.
    • 모듈 간 강한 커플링
    • 복잡한 의존성
    • 일관성 없는 네이밍
    • 성능 문제 해결을 위한 해키한 방식으로 특별한 케이스를 해결
  • 이러한 복잡도를 줄이면 유지보수성이 크게 향상된다. 단순성이 시스템의 핵심 목표여야 한다.
  • 복잡도를 제거하기 위한 최상의 도구는 추상화이다.
    • 좋은 추상화는 깔끔하고 직관적인 외관 아래로 많은 세부 구현을 숨길 수 있다.
    • 고수준 프로그래밍 언어는 기계 언어, CPU 레지스터, 시스템 호출을 숨긴 추상화다.

# 발전성

  • 엔지니어가 이후에 시스템을 쉽게 변경할 수 있게 하라.
  • 서비스가 살아있는 한 시스템은 반드시 변경된다.
  • 시스템 변경을 쉽게 하고 변화된 요구사항에 시스템을 맞추는 방법은 시스템의 간단함과 추상화에 밀접하게 관련된다.