1과. 신뢰 할 수 있고 확장 가능 하며 유지 보수 하기 쉬운 애플리케이션
1. 데이터 시스템
어플리케이션은 계산 중심(compute-intensive)보다 데이터 중심(data-intensive)이 많다.
계산에 쓰이는 CPU보다는 데이터 양, 데이터 복잡도 등이 더 문제다.
데이터 중심 어플리케이션이 공통으로 필요로 할수 있는 기능들 : 데이터 시스템
- 데이터베이스 : 영속성 스토리지
- 캐시 : 읽기 속도 향상, 값 비싼 수행 결과 기억
- 검색 색인 : 사용자가 키워드로 데이터를 검색하거나 다양한 방법으로 필터링할 수 있게 제공
- 스트림 처리 : 비동기 처리를 위해 다른 프로세스로 메세지 보내기
- 일괄(batch) 처리 : 대량의 누적된 데이터를 주기 분석
1) 데이터 시스템의 범주들
데이터베이스, 메세지큐, 캐시 등
최근에는 이 경계들이 허물어지고있다. ex) redis를 MQ로도 쓸 수 있음. kafka 도 지속성을 지원함
단일 시스템으로는 어플리케이션 니즈를 충족시킬 수 없음. 복합 데이터 시스템으로 흘러감
개발자는 애플리케이션 개발자뿐 아니라 데이터 시스템 설계자이기도 하다.
2) 개발자의 고민들
데이터를 정확하고 완전하게 유지하려면 어떻게 해야할까?
시스템의 일부 성능이 저하되더라도 클라이언트에 일관되게 좋은 성능을 어떻게 제공할 수 있을까?
부하 증가를 다루기 위해 어떻게 규모를 확장할까?
서비스를 위해 좋은 API 는 어떤 건가?
2. 소프트웨어 엔지니어링의 목표
- 신뢰성(Reliability) : SW결함, 휴먼 에러, 재난 같은 상황에서도 시스템은 올바르게 동작
- 확장성(Scalability) : 트래픽이 증가해도 대응 가능
- 유지보수성(Maintainability) : 모든 개발,운영자가 생산적으로 작업할 수 있어야 함
3. 신뢰성
결함(fault)을 예측하고 대처할 수 있는 시스템을 내결함성(fault-tolerant) 또는 탄력성(resilient) 있다고 함.
- 결함 : 사양에 벗어난 시스템의 구성 요소
- 장애 : 유저에게 서비스를 제공하지 못하고 시스템이 멈춤
결함으로 인해 장애가 발생하지 않도록 내결함성 구조로 설계 해야 함.
결함을 0으로 줄일 수는 없다.
카오스 몽키(Chaos Monkey) : 고의적으로 결함을 유도하고 내결함성 시스템을 지속적으로 훈련하고 테스트
https://github.com/netflix/chaosmonkey
cf) 내결함성을 쉽게 테스트할 수 있는 spring 지원 구현체 : https://codecentric.github.io/chaos-monkey-spring-boot
3-1. 하드웨어 결함
- 시스템 장애의 대부분의 원인 : 디스크, 램 고장. 대규모 정전, 담당자가 장비를 잘못 다룸
- 대응 방안 : 하드웨어 구성에 중복(redundancy)을 추가
- 디스크 : RAID
- 서버 : 이중 전원 디바이스, hot-swap CPU
- IDC : 예비용 전원 발전기
애초부터 내결함성과 탄력성을 잘 고려해서 설계해라.
ex) aws 의 가상 장비들은 언제나 죽는 걸 가정하고 설게했다.
이렇게 고려된 시스템은 재부팅도 쉽다.
3-2. 소프트웨어 결함
systematic error 는 더 예상하기 어렵고 오류를 더욱 많이 유발한다.
ex) 리눅스 커널 윤초 버그로 레딧, 링크드인 등이 전부 장애남
https://engineering.vcnc.co.kr/2016/12/struggling-with-the-leap-second/
특정 상황에 의해 발생하기 전까지 나타나지 않을수도 있다.
명확한 해결책은 없다. 빈틈없는 테스트, 프로세스 격리, 모니터링, 알람체계를 잘 갖춰라.
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, 리팩토링 등
작고 로컬규모에 초점을 맞춰라
참조문서
'DB' 카테고리의 다른 글
스플릿 브레인의 정의와 발생 시나리오 정리 (0) | 2025.08.02 |
---|---|
[데이터 중심 어플리케이션 설계하기] 2과. 데이터 모델과 질의 언어 (0) | 2025.07.13 |
6/26 DB Connection Pool 고갈 장애 분석 및 해결 방안 리포트 (0) | 2025.06.27 |
[Tool] pgAdmin4 자주 사용하는 키보드 단축키 모음 (0) | 2025.05.21 |
7가지 핵심 질문으로 알아보는 개발자를 위한 Redis 활용법 (0) | 2025.03.14 |
댓글