본문 바로가기
정리/데이터 중심 애플리케이션 설계

데이터 중심 애플리케이션 설계 - 1(신뢰성, 확장성, 유지보수성)

by __EunQ 2022. 9. 12.

1장. 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션

오늘날 대부분의 애플리케이션은 계산 중심(compute-intensive)이 아닌 데이터 중심적(data-intensive)이다.

많은 애플리케이션은 다음을 필요로 한다.
- 데이터베이스: 구동 애플리케이션이나 다른 애플리케이션에서 나중에 다시 데이터를 찾을 수 있게 데이터 저장
- 캐시: 읽기 속도 향상을 위해 값비싼 수행 결과를 기억
- 검색 색인(search index): 사용자가 키워드로 데이터를 검색하거나 다양한 방법으로 필터링할 수 있도록 제공
- 스트림 처리: 비동기 처리를 위해 다른 프로세스로 메시지 보내기
- 일괄 처리(batch processing): 주기적으로 대량의 누적된 데이터 분석

데이터 시스템에 대한 생각

데이터베이스, 큐, 캐시 등을 데이터 시스템이라는 포괄적 용어로 묶는다.
첫번째 이유, 데이터 저장과 처리를 위한 새로운 도구는 최근에 만들어졌다.
새로운 도구는 다양한 사용 사례(use case)에 최적화되었기 때문에 전통적인 분류에 정확히 들어맞지 않는다. 
(ex. redis는 메시지 큐로 사용하는 데이터스토어, Apache Kafka는 DB처럼 지속성을 보장하는 메시지 큐)
두번째 이유, 단일 도구로는 데이터 처리와 저장을 모두 만족시킬 수 없는 과도하고 광범위한 요구사항을 갖고 있음.

대부분의 소프트웨어 시스템에서 중요하게 여기는 세 가지 관심사
1. 신뢰성(Reliability): 결함(fault)이 있더라도 시스템은 지속적으로 올바르게 동작
2. 확장성(Scalability): 시스템 데이터 양, 트래픽 양 복잡도가 증가하면서 이를 처리할 적절한 방법 존재
3. 유지보수성(Maintainability): 시간이 지나도 모든 사용자가 시스템 상에서 생산적으로 작업할 수 있어야 함.

신뢰성

"무엇인가 잘못되어도 지속적으로 올바르게 동작함." (대략적인 의미)

결함과 장애는 동일하지 않다.
- 결함(fault): 잘못될 수 있는 일
  -> 결함을 예측하고 대처할 수 있는 시스템을 결함성(fault-tolerant) 또는 탄력성(resilient)을 지녔다고 함.
- 장애(faliure): 사용자에게 서비스를 제공하지 못하고 시스템 전체가 멈춤

결함으로 인해 장애가 발생하지 않도록 내결함성 구조를 설계해야 함.

 ※ 넷플릭스의 카오스 몽키(Chaos Monkey): 고의적으로 결함을 유도하여 내결함성 시스템을 지속적으로 훈련

결함

1. 하드웨어 결함
  가. 원인: 하드디스크 고장, 램 결함, 정전 등
  나. 대응책: 디스크 RAID 구성, 서버 이중 전원 디바이스와 핫 스왑(hot-swap) 가능한 CPU설치, 예비 전원용 디젤 발전기 설치
    -> 구성요소 하나가 죽으면 고장난 구성 요소가 교체되는 동안 중복된 구성 요소를 대신 사용할 수 있음.

2. 소프트웨어 오류
  가. 원인: 시스템 내 체계적 오류(systematic error)
  나. 특징: 예상하기 어렵고 노드 간 상관관계 때문에 상관관계없는 하드웨어 결함보다 오히려 시스템 오류를 더욱 많이 유발하는 경향
  다. 해결책: 신속한 해결이 어렵다. 시스템에 가정과 상호작용에 대해 주의 깊게 생각하기, 빈틈없는 테스트, 프로세스 격리, 죽은 프로세스의 재시작 허용 등

3. 인적 오류
  가. 원인: 운영자의 설정 오류 등
  나. 방지책
    - 오류의 가능성을 최소화하는 방향으로 시스템 설계(ex. 잘 설계된 추상화, API, 관리 인터페이스 사용)
    - 실제 데이터를 확인할 수 있지만 사용자에겐 영향이 없는 비 프로덕션 샌드박스(sandbox) 제공
    - 단위 테스트부터 전체 시스템 통합 테스트와 수동 테스트까지 철저하게 테스트
    - 장애 발생 영향을 최소화하기 위해 오류를 빠르고 쉽게 복구할 수 있도록 함
    - 성능 지표와 오류율 같은 상세하고 명확한 모니터링 대책 마련
    - 조작 교육과 실습 시행

확장성

증가한 부하에 대처하는 시스템의 능력을 설명(ex. "시스템이 특정 방식으로 커지면 이에 대처하기 위한 선택은 무엇인가 또 추가 부하를 다루기 위해 계산 자원을 어떻게 투입하는가")

1. 부하 기술하기

먼저 시스템의 현재 부하를 간결하게 기술한다.

부하는 부하 매개변수(load parameter)라 부르는 몇 개의 숫자로 나타낼 수 있다.
  -> ex. 웹 서버의 초당 요청 수, DB의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자, 캐시 적중률 등

2. 성능 기술하기

시스템 부하를 기술하면 부하가 증가할 때 어떤 일이 일어나는지 조사할 수 있다.

시스템 부하 기술 방법
  - 부하 매개변수를 증가시키고 시스템 자원은 변경하지 않고 유지하면 시스템 성능은 어떻게 영향을 받는가?
  - 부하 매개변수를 증가시켰을 때 성능이 변하지 않고 유지되길 원한다면 자원을 얼마나 늘려야 하는가?

시스템 성능 측정에 필요한 수치
  - 일괄 처리 시스템 -> 처리량(throughput): 초당 처리할 수 있는 레코드 수나 데이터 집합으로 작업을 수행할 때 걸리는 전체 시간)
  - 온라인 시스템 -> 응답 시간(response time): 클라이언트가 요청을 보내고 응답을 받는 사이의 시간

※ 지연 시간(latency)과 응답 시간(response time): 지연 시간과 응답 시간은 다름.
  - 응답 시간은 클라이언트 관점에서 본 시간(요청을 처리하는 실제 시간 + 네트워크 지연 시간 + 큐 지연시간) 
  - 지연 시간은 요청이 처리되길 기다리는 시간(서비스를 기다리며 휴지(latent) 상태인 시간을 말함)

응답 시간은 동일한 요청에도 매번 응답 시간이 다르기 때문에 단일 숫자가 아니라 측정 가능한 값의 분포로 생각해야 한다.

서비스 평균 응답 시간을 살피는 것에 있어서 산술 평균을 사용하는 것보다 백분위를 사용하는 편이 더 좋다. 

응답 시간의 목록을 가지고 가장 빠른 시간부터 제일 느린 시간까지 정렬하면 중간 지점이 중앙값이 된다.
  - 사용자 요청의 절반은 중앙값 응답 시간 미만으로 제공되고 나머지 반은 중앙값보다 오래 걸린다. 중앙값은 50분위로서 p50으로 축약
  - 상위 백분위 응답시간(=꼬리 지연 시간(tail latency))으로 특이 값이 얼마나 좋지 않은지 파악할 수 있음(95p, 99p, 999p)
    -> 아마존은 99.9분위(999p)  사용
  - 큐 대기 지연은 높은 백분위에서 소수의 느린 요청 처리만으로도 후속 요청 처리가 지체된다.(=선두 차단(head-of-line blocking))
    -> 클라이언트 측 응답시간 측정이 중요하며, 응답 시간과 독립 적으로 요청을 계속 보내야 한다. 이전 요청 완료까지 기다리면 테스트에서 인위적으로 실제 대기 시간을 더 짧게 만들어 평가를 왜곡함.

3. 부하 대응 접근 방식

좋은 성능을 유지하는 방법

- 용량 확장(scailing up)(수직 확장(vertical scaling), 강력한 장비로 이동)
- 규모 확장(scailing out)(수평 확장(horizontal scaling), 다수의 낮은 사양 장비에 부하를 분산)

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

단일 노드 상태유지(stateful) 데이터 시스템은 분산 설치는 복잡도가 높아 확장 비용이나 데이터베이스를 분산으로 만들어야 하는 고가용성 요구가 있을 때까지 단일 노드에 데이터베이스를 유지하는 것(용량 확장(scailing up))
  -> 최근까지의 통념이었지만 분산 시스템을 위한 도구와 추상화가 좋아지면서 일부는 바뀌었음.

유지보수성

유지보수성을 위한 소프트웨어 시스템 설계 원칙 세 가지
1. 운용성: 운영의 편리함 만들기 -> 반복 task를 줄여 업무 생산성을 높임
  - 모니터링 제공하여 런타임 동작과 시스템 내부 가시성 제공
  - 표준 도구 이용하여 자동화와 통합을 위한 지원
  - 개별 장비 의존성 회피
  - 좋은 문서와 이해하기 쉬운 운영 모델 제공
  - 기본값을 다시 정의할 수 있는 자유를 관리자에게 부여
  - 예측 가능하게 하고 예기치 않은 상황 최소화

2. 단순성: 복잡도 관리 -> 복잡도를 줄여야 함
  - 우발적 복잡도를 제거하기 위한 최상의 도구는 추상화다.(세부 구현을 숨길 수 있고 재사용성이 높음)

3. 발전성: 변화를 쉽게 만들기
  - 요구사항은 끊임없이 변하기 때문
  - 애자일 작업 패턴 적용