빨간색코딩

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

database

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

빨간색소년 2022. 4. 18. 03:06

초심자의 눈으로 이해한 내용을 정리해보았다. 책에 있는 내용을 기반으로 썼지만, 책에 없는 내용도 조금씩 적어보았다. 책은 꼭 사서 보시길 바랍니다..

1. 데이터 시스템

  • 어플리케이션은 계산 중심(compute-intensive)보다 데이터 중심(data-intensive)이 많다.
    • 계산에 쓰이는 CPU보다는 데이터 양, 데이터 복잡도 등이 더 문제다.
  • 데이터 중심 어플리케이션이 공통으로 필요로 할수 있는 기능들 : 데이터 시스템
    • 데이터베이스 : 영속성 스토리지
    • 캐시 : 읽기 속도 향상, 값비싼 수행 결과 기억
    • 검색 색인 : 사용자가 키워드로 데이터를 검색하거나 다양한 방법으로 필터링할 수 있게 제공
    • 스트림 처리 : 비동기 처리를 위해 다른 프로세스로 메세지 보내기
    • 일괄(batch) 처리 : 대량의 누적된 데이터를 주기 분석
  • 데이터 시스템의 범주들
    • 데이터베이스, 메세지큐, 캐시 등
    • 최근에는 이 경계들이 허물어지고있다. ex) redis를 MQ로도 쓸 수 있음. kafka 도 지속성을 지원함
    • 단일 시스템으로는 어플리케이션 니즈를 충족시킬 수 없음. 복합 데이터 시스템으로 흘러감
  • 개발자는 애플리케이션 개발자뿐 아니라 데이터 시스템 설계자이기도 하다.
  • 개발자의 고민들
    • 데이터를 정확하고 완전하게 유지하려면 어떻게 해야할까?
    • 시스템의 일부 성능이 저하되더라도 클라이언트에 일관되게 좋은 성능을 어떻게 제공할 수 있을까?
    • 부하 증가를 다루기 위해 어떻게 규모를 확장할까?
    • 서비스를 위해 좋은 API 는 어떤 건가?

2. 소프트웨어 엔지니어링의 목표

  • 신뢰성(Reliability) : sw결함, 휴먼에러, 재난같은 상황에서도 시스템은 올바르게 동작
  • 확장성(Scalability) : 트래픽이 증가해도 대응가능
  • 유지보수성(Maintainability) : 모든 개발,운영자가 생산적으로 작업할 수 있어야 함

3. 신뢰성

  • 결함(fault)을 예측하고 대처할 수 있는 시스템을 내결함성(fault-tolerant) 또는 탄력성(resilient) 있다고 함
    • 결함 : 사양에 벗어난 시스템의 구성 요소
    • 장애 : 유저에게 서비스를 제공하지 못하고 시스템이 멈춤
    • 결함으로 인해 장애가 발생하지 않도록 내결함성 구조로 설계해야함
    • 결함을 0으로 줄일 수는 없다.
  • 카오스 몽키(Chaos Monkey) : 고의적으로 결함을 유도하고 내결함성 시스템을 지속적으로 훈련하고 테스트

3-1. 하드웨어 결함

  • 시스템 장애의 대부분의 원인 : 디스크, 램 고장. 대규모 정전, 담당자가 장비를 잘못 다룸
  • 대응 방안 : 하드웨어 구성에 중복(redundancy)을 추가
    • 디스크 : RAID
    • 서버 : 이중 전원 디바이스, hot-swap CPU
    • IDC : 예비용 전원 발전기
  • 애초부터 내결함성과 탄력성을 잘 고려해서 설계해라
    • ex) aws 의 가상장비들은 언제나 죽는걸 가정하고 설게했다.
    • 이렇게 고려된 시스템은 재부팅도 쉽다.

3-2. 소프트웨어 결함

  • systematic error 는 더 예상하기 어렵고 오류를 더욱 많이 유발한다.
  • 특정 상황에 의해 발생하기 전까지 나타나지 않을수도 있다.
  • 명확한 해결책은 없다. 빈틈없는 테스트, 프로세스 격리, 모니터링, 알람체계를 잘 갖춰라

3-3. 인적 오류(human error)

  • 대응방안
    • 잘 설계된 추상화 API, 인터페이스를 사용하라. 옳은 일은 쉽게하고 잘못된 일을 막을 수 있다.
    • 실수가 잦게 발생하는 곳은 실제 데이터로 안전하게 실험해볼 수 있는 sandbox 를 제공하라 (ex. stage환경)
    • 단위테스트, 통합테스트, 수동테스트를 철저히 해라. 특히 코너케이스에 유의
    • 장애를 빠르게 복구 할 수 있게 도구를 만들어라
    • 모니터링이 중요하다. 지표를 분석해라

4. 확장성

4-1. 부하 매개변수

  • 부하 성장 질문
    • 시스템이 특정 방식으로 커지면 어떤 선택지들이 있는가?
    • 추가 부하를 다루기 위해 계산 자원을 어떻게 투입할까?
  • 부하 매개변수 : 이걸 정의해야 부하 성장 질문을 논의할수 있다. 기준이 생기기 때문이다.
    • 각 시스템 설계에 따라 달라진다.
    • ex) rps, qps, active user, cache hit ratio 등
  • 예시) 트위터 타임라인 아키텍처
    • 버전1 : 트윗을 쓰면 테이블에 전역적으로 insert. 각 유저는 타임라인 접근 시점에 join
      • SELECT tweets.*, users.* FROM tweets JOIN users ON tweets.sender_id = users.id JOIN follows ON follows.followee_id = users.id WHERE follows.follower_id = current_user
      • 쉽고 빠른 쓰기 + 쉽지만 느린 읽기
    • 버전2 : 각 유저별 트윗 우편함에 insert. 각 유저는 타임라인 접근 시, 자신의 트윗 우편함만 읽으면 됨
      • 어려운 쓰기 + 쉽고 빠른 읽기
      • 한계 : 인플루언서가 수천만 팔로워를 갖고있다면 쓰기가 너무 비대해지고 어려워짐
    • 버전3 : 인플루언서는 2방식에서 제외. 1방식으로 전환
    • 트위터의 부하 매개변수 : 팬아웃 -> 사용자당 팔로워의 분포, 빈도

4-2. 성능 기술하기

  • 하둡같은 일괄 처리 시스템은 처리량에 관심이 있지만, 서비스들은 응답시간에 관심이 있다.
  • 응답시간은 요청마다 매번 다르다. 따라서, 분포로 생각해야한다.
  • 대부분의 요청은 빠르지만, 가끔 오래걸리는 특이점(outlier)이 있다.
    • 컨텍스트 스위칭, 네트워크 패킷손실 - TCP재전송, GC, page fault 등 다양한 이유가 있겠다.
  • 응답시간 분석에 백분위를 사용하고, 중앙값(p50)을 지표로 삼아라
    • 특이점 = p95, p99, p999
    • p9999부터는 최적화 비용이 너무 많이들어서 이점이 없다.
  • 특이점도 무시할 순 없다. 보통 데이터가 많기 때문에, 발생할 수 있는데, 중요한 고객이기 때문이다.
  • SLO(서비스 수준목표. Service Level Objective)와 SLA(서비스 수준 협약서. Service Level Agreement) 가 이럴때 자주 등장함
    • ex) 환불의 기준
  • 큐 대기 지연은 응답시간의 상당 부분을 차지한다.
    • 선두 차단(head-of-line blocking) : 서버는 병렬로 소수 작업만 처리할 수 있기때문에, 첫 요청을 처리하는데 느려진다면 후속 요청이 모두 영향받는다.
  • 시스템에 인위적으로 부하를 생성하는 경우, 클라이언트는 응답시간과 독립적으로 요청을 계속 보내야한다. 응답을 기다리고 다음 요청을 보내면 평가가 의미없다.
    • cf) nGrinder 같은 전문 부하테스트기 대신 개발자가 부하 시뮬레이터를 만들 경우 고려해야한다.
  • 꼬리 지연 증폭(tail latency amplification) : 여러 API를 호출했을 때, 적은 API만 느려도 전체가 느린게 된다.

4-3. 대응 방안

  • 용량확장=수직확장=scaling up 과 규모확장=수평확장=scaling out 사이에서 실용적인 조합을 찾아야한다.
  • 스케일아웃이 항상 좋지는 않다. 적절히 스케일업된 장비들로 구성된게 더 좋을 때도 있다.
  • 탄력적(elastic) : 부하 증가를 감지하면 자동으로 자원 추가
    • 이것도 항상 좋지는 않다. 수동 확장이 더 간단하고 예상치 못한 운영이슈가 적다.
  • 분산환경이 필요해질때 까진 스케일업하는게 보통이지만, 요새는 분산시스템과 추상화가 워낙 좋아져서, 처음부터 고려해볼만도 하다.

5. 유지보수성

  • 많은 사람들은 레거시 시스템을 유지보수하는 것을 좋아하지 않는다.
  • 처음부터 레거시를 만들지 않게 끔 설계해야한다.
  • 유지보수성 원칙
    • 운용성 : 운영팀이 원활하게 운영할 수 있도록 쉽게 만들어라
    • 단순성 : 복잡도를 최대한 제거해 새로운 팀원도 잘 이해할 수 있게 만들어라
    • 발전성 : 이후 변경에 열려있어야 한다. 요구사항 변경같은 새로운 사용사례를 쉽게 적용할 수 있어야한다.
      • 유연성, 수정가능성, 적응성

5-1. 운용성

  • 운영 중 일부 측면은 자동화할 수 있고, 자동화해야한다.
    • 하지만 자동화를 처음 설정하고 제대로 동작하는 지 확인하는 것도 여전히 사람이 해야한다.
  • 좋은 운영성 : 반복되는 태스크들을 쉽게 수행할 수 있도록 하는 것
    • 런타임 동작과 시스템 내부에 대한 가시성 제공
    • 좋은 문서
    • 이해하기 쉬운 운영모델 : X를 하면 Y가 발생
    • 어드민 등으로 기본값을 재정의할 수 있는 권한 부여
    • 자기 회복이 가능할 뿐아니라 관리자가 상태를 수동으로 제어할 수 있어야 함

5-2. 단순성

  • 시스템이 복잡해지면 모든 사람의 진행을 느리게하고, 유지보수 비용이 증가한다.
  • 복잡성의 사례들
    • 상태 공간의 금증
    • 모듈 간 강결합성
    • 일관성 없는 네이밍
    • 임시방편 코드들
  • 우발적 복잡도(accidental complexity)를 제거하기 위한 최상의 도구는 '추상화' 다
    • 좋은 추상화는 깔끔하고 직관적인 외관 아래로 세부 구현을 숨길 수 있다.
    • 재사용성이 좋아진다.
    • ex) 기계어, cpu레지스터, 시스템콜들을 추상화한 것이 고수준 프로그래밍언어다.

5-3. 발전성

  • 시스템 요구사항은 계속 바뀐다.
  • 애자일(agile) 작업 패턴은 변화에 적응하기 위한 프로세스이다.
    • TDD, 리팩토링 등
    • 작고 로컬규모에 초점을 맞춰라
Comments