[데이터 중심 애플리케이션 설계] 1. 신뢰성, 확장성, 유지보수성

succeeding·2024년 10월 4일
0
post-thumbnail

4장까지는 데이터 시스템이 단일 장비이거나 여러 클러스터 장비에 분산됐거나 상관없이 모든 상황에서 적용되는 데이터 시스템의 기초 개념에 대해 알아본다.

오늘날 많은 애플리케이션은 계산 중심(compute-intensive) 이라기 보단 데이터 중심적(data-intensive)이다.

데이터 중심적인 애플리케이션의 성능을 제한하는 것은 CPU 성능보단 데이터의 양, 복잡도, 변화 속도다.

신뢰성

결함이 발생해도 시스템이 올바르게 동작하는 성질

결함(fault) 은 사양에서 벗어난 시스템의 한 구성 요소로 정의된다.

결함을 예측하고 대처할 수 있는 시스템을 내결함성(fault-tolerant) 또는 탄력성(resilient)을 지녔다고 한다.

장애(failure) 는 사용자에게 필요한 서비스를 제공하지 못하고 시스템 전체가 멈춘 상황을 의미한다. 결함과는 다른 의미다.

결함으로 인해 장애가 발생하지 않게끔 내결함성 구조를 설계하는 것이 좋다.

결함 유형

아래 결함들에 대한 해결책을 다소 추상적으로 제공하고 있어 현재 장에서는 크게 도움이 되지 않는다고 판단된다.

하드웨어 결함

10,000 개의 디스크로 구성된 저장 클러스터는 평균적으로 하루에 한 개의 디스크가 죽는다고 예상된다.

하드웨어 구성 요소에 중복(redundancy)를 추가하는 방법으로 대응한다.

소프트웨어 오류

신속한 해결책이 없다.
숙고하기, 빈틈없는 테스트, 프로세스 격리, 죽은 프로세스의 재시작 허용, 시스템 동작의 측정, 모니터링, 분석과 같은 노력이 필요하다.

인적 오류

한 연구에 따르면 인적 오류가 하드웨어 에러보다 더 많이 발생한다고 한다.

어떻게 해결하겠는가?

  • 오류의 가능성을 최소화하도록 시스템 설계하기
  • sandbox 환경 제공하기
  • 단위/통합/시스템 테스트 철저히
  • 빠른 복구 도구 제공하기
  • 텔레메트리 활용한 모니터링
  • 교육과 실습

확장성

부하가 증가해도 좋은 성능을 유지하기 위한 전략

확장성은 증가된 부하에 대처하는 시스템 능력을 설명하는 데 사용하는 용어다. 그러나, "X는 확장 가능하다" 와 같은 일차원적인 표식은 의미가 없다. 오히려 "시스템이 특정 방식으로 커지면 대처하기 위한 선택은 무엇인가?", "추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까?" 같은 질문을 고려해야 한다.

부하 기술하기

시스템의 현재 부하를 간결하게 기술해야 한다. 그래야 부하 성장 질문("부하가 두 배로 되면 어떻게 될까?")을 논의할 수 있다.
부하는 부하 매개변수(load parameter) 라 부르는 몇 개의 숫자로 나타낼 수 있다. 가령 다음의 것들이 있다.

  • 초당 요청 수
  • 데이터베이스의 읽기 대 쓰기 비율
  • 활성 사용자(active user)
  • 캐시 적중률

평균적인 경우가 중요할 수도 있고, 소수의 극단적인 경우가 병목 현상의 원인일 수 있다.

트위터의 예시

트위터는 팔로워의 분포가 핵심 부하 매개변수이다.

트위터의 주요 두 가지 동작은 다음과 같다.

  • 트윗 작성
    • 사용자는 팔로워에게 새로운 메시지를 게시할 수 있다.
    • 평균 초당 4.6k 요청. 피크일 때 초당 12k 요청 이상
  • 홈 타임라인
    • 사용자는 팔로우한 사람이 작성한 트윗을 볼 수 있다.
    • 초당 300K 이상

트위터의 확장성 문제의 주요 원인은 트윗 양이 아닌 팬 아웃 때문이다. 개별 사용자가 많은 사람을 팔로우하고, 다시 많은 사람들이 개별 사용자를 팔로우한다.
트위터를 구현하기 위한 방법은 크게 두 가지가 있다.

  1. 관계형 데이터베이스를 사용하는 방식
  • 트윗 작성은 간단히 tweets 테이블에 삽입한다.
  • 홈 타임라인 요청은 매번 관계형 테이블을 질의 한다.
  1. 데이터 파이프라인을 사용하는 방식
  • 팔로워가 각자 홈 타임라인 캐시를 유지한다.
  • 트윗을 작성하면 팔로워들의 타임라인 캐시에 새로운 트윗을 삽입한다.

트위터의 첫 번째 버전은 첫 번째 방법을 사용하다가, 홈 타임라인의 질의 부하를 버텨내기 어려워 두 번째 방법으로 전환했다.
평균적으로 트윗 작성보다 홈 타임라인 요청이 수백 배 많기 때문에, 두 번째 방법이 훨씬 더 잘 동작한다.
이런 경우엔 쓰기 시점에 더 많은 일을 하고(트윗 작성시 팔로워들을 찾고 그들의 타임라인 캐시에 새로운 트윗을 삽입하는 일), 읽기 시점에는 적은 일(단순 캐시 읽기)을 하는 것이 바람직 하다.

반면 두 번 째 방법은 트윗 작성이 많은 부가 작업을 필요로 한다는 것이다. 평균적으로 트윗이 약 75명의 팔로워에게 전달되므로, 초당 4.6k 트윗 요청은 홈 타임라인 캐시에 345k 쓰기를 유발한다. 또한, 어떤 사용자는 3천만 명의 팔로워가 있어서, 단일 트윗 작성이 홈 타임라인에 3천만 건 이상의 쓰기 요청을 유발할 수도 있다. 따라서, 적시에 트윗을 전송하는 작업이 (트위터는 5초 이내에 팔로워에게 트윗을 전송하려고 노력한다) 중요한 과제이다.
트위터 사례에서 사용자당 팔로워의 분포는 팬아웃 부하를 결정하기 때문에 확장성을 논의할 때 핵심 부하 매개변수가 된다.

트위터는 최종적으로, 두 가지 방식을 혼합해서 사용한다. 대부분의 사용자의 트윗은 두 번째 방법처럼 팬아웃 캐시 쓰기를 사용한다. 반면, 유명인의 트윗은 팬아웃하지 않고, 타임 라임 읽기 요청이 발생한 경우에 사용자의 홈 타임라인에 합친다.

성능 기술하기

부하 매개변수를 선택하여 부하를 기술한 뒤, 부하가 증가할 때 어떤 일이 일어나는지 조사할 수 있다.
부하 매개변수를 증가시키면서 목적에 따라 자원(CPU, 메모리, 네트워크 대역폭 등)과 성능 중 하나를 고정시킬 수 있다.

  • 자원을 유지하기 위해 발생하는 성능 저하의 정도
  • 성능을 유지학 위해 발생하는 추가적인 자원의 양

두 방법 모두 성능 수치가 필요하다. 따라서 시스템 성능에 대해 간단히 살펴본다.

시스템 성능

처리량(throughput): 초당 처리할 수 있는 레코드 수나 일정 크기의 데이터 집합으로 작업을 수행할 때 걸리는 전체 시간

응답 시간(response time): 클라이언트가 요청을 보내고 응답을 받는 사이의 시간. 매번 다를 수 있으므로, 응답 시간은 측정 가능한 값의 분포로 생각해야 한다.

응답시간은 평균보다는 중앙값(median)과 같은 백분위(percentile)를 사용하는 편이 좋다. 평균은 얼마나 많은 사용자가 실제로 지연을 경험했는지 알려주지 않기 때문이다.

부하 대응 접근 방식

비공유(shared-noting) 아키텍처: 다수의 장비에 부하를 분산하는 아키텍처.

각 장비끼리 공유하는 자원이 없음을 의미하는 것 같다.

탄력적(elastic) 시스템: 부하 증가를 감지하면 컴퓨팅 자원을 자동으로 추가하는 시스템
탄력적인 시스템은 부하를 예측할 수 없을 만큼 높은 경우 유용하지만, 수동으로 확장하는 시스템이 더 간단하고 운영상 예상치 못한 일이 더 적다.

대개 대규모로 동작하는 시스템의 아키텍처는 해당 시스템을 사용하는 애플리케이션에 특화돼 있다. 범용적이고 모든 상황에 맞는(one-size-fits-all) 마법의 확장 아키텍처(magic scaling source)는 없다.

특정 애플리케이션에 적합한 확장성을 갖춘 아키텍처는 주요 동작이 무엇이고 잘 하지 않는 동작이 무엇인지에 대한 가정을 바탕으로 구축한다. 이 가정은 곧 부하 매개변수가 된다.이 가정이 잘못되면 확장에 대한 헛수고가 되거나 최악의 경우 역효과를 낳는다. 초기 단계나 검증되지 않은 제품의 경우 미래를 가정한 부하에 대비해 확장하기보다는 빠르게 반복해서 제품 기능을 개선하는 작업이 좀 더 중요하다.

유지보수성

소프트웨어의 비용은 대부분 초기 개발보다 유지보수에 들어간다.

유지보수 비용을 줄이기 위한 소프트웨어 시스템 설계 원칙 세 가지

  • 운용성(operability): 운영하기 쉽게 만들라
  • 단순성(simplicity): 시스템의 복잡도를 최대한 제거하여 새로운 엔지니어가 시스템을 이해하기 쉽게 만들라
  • 발전성(evolvability): 엔지니어가 이후에 시스템을 쉽게 변경할 수 있게 하라.

신뢰성, 확장성을 달성하기 위한 쉬운 해결책은 없다. 그보다 유지보수성(운용성, 단순성, 발전성)을 염두에 두고 시스템을 생각하려 노력해야 한다.

운용성: 운영의 편리함 만들기

좋은 운영성이란 동일하게 반복되는 태스크를 쉽게 수행하게끔 만들어 운영팀이 고부가가치 활동에 노력을 집중한다는 의미.

단순성: 복잡도 관리

우발적 복잡도(accidental complexity) 를 제거하기 위한 최상의 도구는 추상화다.

발전성: 변화를 쉽게 만들기

애자일(agile) 작업 패턴은 변화에 적응하기 위한 프레임워크를 제공한다. 애자일 커뮤니티는 테스트 주도 개발(test-driven development, TDD)과 리팩토링 같이 자주 변화하는 환경에서 소프트웨어에를 개발할 때 도움이 되는 기술 도구와 패턴을 개발하고 있다.

0개의 댓글