이 글은 "가상 면접 사례로 배우는 대규모 시스템 설계 기초" 를 읽고 정리한 글입니다.
https://product.kyobobook.co.kr/detail/S000001033116
| 서버
웹 애플리케이션
- 비즈니스 로직 + 데이터 처리 등을 위한 서버 구현용 언어 사용(Java, Python)
- 프레젠테이션 용으로 클라이언트 구현용 언어(HTML, JavaScript) 사용
모바일 앱
- 모바일 앱 ↔ 웹 서버
- HTTP 프로토콜 이용 → 반환 응답 데이터는 JSON 형태
| 데이터베이스
- 웹 계층(웹/모바일 트래픽 처리)
- 데이터 계층(데이터베이스 서버)
어떤 데이터 베이스 사용?
⇒ 관계형 데이터베이스
- MySQL, Oracle, PostgreSQL
- 자료를 테이블, 열, 칼럼으로 표현
- 테이블 관계
JOIN가능
⇒ 비-관계형 데이터베이스(NoSQL)
- CouchDB, Neo4j, Cassandra, Amazon DynamoDB
- 일반적으로 JOIN 연산은 지원 x
분류
- Key-value 저장소
- 그래프 저장소
- 칼럼 저장소
- 문서 저장소
💡 아래의 경우 비-관계형 데이터베이스가 더 좋음
- 응답 지연 시간이 낮아야할 때
- 데이터가 비정형인 경우
- 데이터를 직렬화 하거나 역직렬화할 수 있기만 하면 됨
- 아주 많은 양의 데이터를 저장해야 함
💡데이터 직렬화
- 객체, 자료구조, 메모리에 있는 데이터를 하나의 바이트 스트림(문자열, JSON, BSON 등)으로 변환하는 과정
- ex) 자바 객체 → JSON 문자열
💡데이터 역직렬화
- 직렬화된 데이터를 다시 원래의 객체나 자료구조 형태로 복원
- ex) JSON 문자열 → 자바 객체
| 수직적 규모 확장 vs 수평적 규모 확장
수직적 규모 확장
- 서버에 고사양 자원(CPU, 더 많은 RAM)을 추가
- 서버에 들어오는 트래픽이 적을 때 좋은 선택
단점
- 한 대의 서버에 CPU나 메모리를 무한대로 증설할 수는 없음
- 장애 자동 복구나 다중화 방안을 제시하지 않음 → 서버 장애 시 웹/앱은 완전히 중단됨
수평적 규모 확장
- 더 많은 서버를 추가하여 성능 개선
- 대규모 애플리케이션 지원에 적절
- 대규모 사용자접속 → 부하 분산기, 로드 밸런서 도입이 최선
로드 밸런서
- 부하 분산 집합에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할
- 사용자가 로드밸런서의 공개 IP로 접속 → 웹 서버는 클라이언트 접속을 직접 처리하지 않음
- 서버간 통신에는 private IP 주소(같은 네트워크 내에 속한 서버 사이 통신에만 사용할 수 있는 IP 주소) 사용, 인터넷 접속 불가
- 웹 계층 가용성 향상
데이터베이스 다중화
- master-slave 구조
- MasterDB - 쓰기 연산만 지원 (
insert, delete, update 등)
- SlaveDB - 읽기 연산만 지원
- 대부분의 DB는 쓰기 연산보다 읽기 연산이 더 많음 → 일반적으로 SlaveDB가 MasterDB보다 수가 더 많음
- 성능 향상 / 안정성 - 서버 일부 파괴 시에도 데이터 보존 / 가용성 - 한 서버의 장애에도 서비스 지속 가능
| 캐시
- 값 비싼 연산 결과나 자주 참조되는 데이터를 메모리 안에 두고 빨리 처리될 수 있도록 하는 저장소
- 웹 페이지의 새로 고침 → 매 번 데이터베이스 호출이 발생 → 애플리케이션 성능은 데이터 베이스를 얼마나 자주 호출하는지에 따라 크게 좌우됨
캐시 계층
- 데이터가 잠시 보관되는 곳 - DB 보다 훨씬 빠름
- ex) 웹 서버 ↔ 캐시 ↔ 데이터베이스
- 요청 받은 웹 서버가 캐시에 응답을 찾음 → 저장되어 있으면 그대로 반환
- 없으면 DB에 가서 응답을 찾아서 반환
⇒ 읽기 주도형 캐시 전략(read-through caching strategy)
- 캐시 데이터 종류, 크기, 액세스 패턴에 맞는 캐시 전략을 선택해 사용하면 됨
캐시 사용 시 주의할 점
- 캐시 서버는 일반적으로 프로그래밍 언어로 API를 제공
- 데이터 갱신이 자주 일어나지 않지만 참조가 빈번하게 일어나는 경우
- 영속적으로 보관할 데이터는 캐시에 두지 않는 편이 나음. → 캐시는 휘발성 저장소이기 때문
- 캐시 데이터의 만료 정책도 마련해야 함. → 너무 짧으면 성능이 떨어지고 너무 길면 원본이랑 차이 나겠지
- 데이터 원본과 캐시 사본이 동일한지도 확인해야 함 → 여러 지역에 걸쳐 시스템을 확장할 경우 캐시와 저장소 사이 일관성 유지는 매우 어려움
- 캐시 서버의 장애에는 어떻게 대처할건지? → 서버가 한 대 있는 경우는 단일 장애 지점(Single Point of Failure, SPOF)가 될 수 있음
- 캐시 메모리를 얼마나 크게 잡을지? → 너무 작으면 데이터가 캐시에서 밀려날 수도 있음 → 캐시 메모리의 과할당하면 됨
⇒ 무조건적인 과할당은 운영 비용적인 문제나 캐시 만료에 있어 불필요한 오버헤드 발생 가능성이 있음
⇒ 예상되는 워크로드 + 캐시 정책 + 모니터링으로 안정적으로 커버될 정도로만 크면 됨
- 캐시가 가득 차면 기존 캐시 데이터를 방출해야 함 → 뭘 내보낼건지?
- LRU - 마지막으로 사용된 시점이 가장 오래된 데이터
- LFU - 사용 빈도가 가장 낮은 데이터
- FIFO - 가장 먼저 들어온 애 내보내기
| 컨텐츠 전송 네트워크(CDN)
- 정적 컨텐츠 전송에 쓰이는 지리적으로 분산된 서버의 네트워크
- 이미지, 비디오, CSS, JavaScript 파일 캐시 가능
동작 과정
- 사용자가 CDN이 제공하는 URL로 이미지에 접근
- 이미지가 CDN에 없으면 CDN이 원본 서버에 요청
- 원본 서버 응답의 HTTP 헤더에 TTL(만료 기한) 값이 들어 있음
- CDN 서버가 파일을 캐시하고 사용자에게 반환
CDN 사용 시 고려할 점
- 비용 → CDN으로 들오가고 나가는 데이터 전송 양에 따라 요금을 냄. 자주 사용되지 않는 컨텐츠는 캐싱하지 않는 편이 비용적으로 이득
- 적절한 만료 기간 설정
- 너무 짧으면 자주 원본 서버에 요청해야 함
- 너무 길면 컨텐츠 신선도 저하
- CDN 장애 발생
- CDN 서버 장애 발생 시 → 문제 감지 후 원본 서버로 직접 컨텐츠를 가져오도록 클라이언트 구성이 필요
- 컨텐츠 무효화
- CDN 사업자 제공 API를 이용한 컨텐츠 무효화
- 새로운 버전을 서비스 하도록 오브젝트 버저닝(Object Versioning) 이용
- 캐시 = 데이터 접근 속도를 올리기 위한 임시 저장소
- CDN = 지리적 분산을 통해 정적 컨텐츠를 빠르게 전달하는 시스템 (결과적으로 캐시를 활용함)
⇒ CDN도 캐시를 사용하지만, 단순 캐시보다 범위가 넓고 지리적 분산이 있다는 점이 핵심 차이
| 무상태 웹 계층
- 웹 계층의 수평적 확장 방법 → 더 많은 웹 서버 증설
- 상태 정보(사용자 세션 데이터)를 웹 계층에서 제거해야 함. → 상태 정보를 관계형 DB나 NoSQl 등의 지속성 저장소에 보관하고 필요할 때 가져오는 것
- 상태 정보가 한 서버에만 묶여 있으면 서버 증설 의미가 없음 → 한 서버에 묶여 있으면 서버를 늘려도 정보가 저장되어 있는 서버로만 접속해야 하잖아
- 사용자의 HTTP 요청은 어떤 웹 서버로도 전송 가능하게 만들고, → 이걸 공유 저장소에서 데이터를 가져오도록 설정함
- 세션 데이터를 noSQL이나 캐시 시스템 등으로 분리 → 트래픽 양에 따라 웹 서버를 자동으로 추가하거나 삭제
세션 = 사용자 정보 파일을 서버에서 관리
쿠키 = 클라이언트에 저장되는 Key-Value 데이터
| 데이터 센터
- 서버, 스토리지, 네트워크 장비, 보안 시스템 등
- IT 시스템을 집중적으로 배치하고 운영하는 시설
지리적 라우팅
- 장애가 없는 상황에서 가장 가까운 데이터 센터로 안내됨
- geoDNS - 사용자 위치에 따라 도메인명을 어떤 IP로 변환할지 결정하도록 돕는 DNS
- 데이터 센터 장애 발생 시 → 장애가 없는 다른 데이터 센터로 전송
다중 데이터 센터 아키텍처
- 트래픽 우회
- 올바른 데이터 센터로 트래픽을 전송하는 효과적인 방법을 찾아야 함
- geoDNS는 사용자의 가장 가까운 데이터 센터로 트래픽을 보낼 수 있도록 함
- 데이터 동기화
- 테스트와 배포
- 여러 데이터센터를 사용하도록 시스템이 구성되었다면 → 여러 위치에서 테스트해 보는 것이 중요
- 자동화 배포 도구는 모든 데이터 센터에 동일한 서비스가 설치되도록 해야함
→ 시스템의 확장을 위해서는 시스템을 컴포넌트로 분리 → 각각이 독립적으로 확장될 수 있도록 해야 함.
→ 메시지 큐는 실제 분산 시스템의 핵심 전략 중 하나
| 메시지 큐
- 비동기 통신을 지원하는 컴포넌트
- 메시지 무손실 - 메시지 큐에 보관된 메시지는 소비자가 꺼내기 전까지 안전함
- 비동기적으로 전송
- 기본 아키텍처
- 생산자(입력 서비스) → 메시지 생성, 메시지 큐에 발행
- 소비자(구독자) → 메시지를 받아 그에 맞는 동작을 수행
- 서비스 또는 서버 간 결합이 느슨해짐
- 생산자 다운 → 소비자는 메시지 수신 가능
- 소비자 다운 → 메시지 발행 가능
- 요청을 받는 웹서버, 받은 요청을 작업하는 작업 서버 두 개로 물리적으로 나누고, 그 사이에서 연결 역할을 함. 일종의 편지함/대기열
| 로그, 메트릭, 자동화
로그
- 에러 로그 모니터링
- 서버 단위 / 서비스 단위
메트릭
시스템 상태를 수치화한 데이터
- 호스트 단위 메트릭 - CPU, 메모리, 디스크 I/O에 관한 메트릭
- 종합 메트릭 - 데이터 베이스 계층 성능, 캐시 계층의 성능
- 핵심 비즈니스 메트릭 - 일별 능동 사용자, 수익, 재방문 등
자동화
- 생산성을 높이기 위한 자동화 도구 활용
- CI 도구 → 검증 절차 자동 / 빌드, 테스트, 배포 등 절차 자동화 가능
| 데이터 베이스 규모 확장
수직적 확장(Scale-up)
- 고성능 자원증설(CPU, RAM, 디스크 등)
약점
- 서버 HW는 한계가 있음 → CPU, RAM의 무한 증설 x
- SPOF로 인한 위험성 증가
- 비용이 많이 듦
수평적 확장(Sharding)
- 서버 증설
- 대규모 DB를 Shard라는 작은 단위로 분할하는 기술
샤드(Shard)
- 샤드는 같은 스키마를 사용하지만, 보관되는 데이터 사이에는 중복이 없음
- ex) user_id 를 4로 나눈 나머지가 0이면 0번 샤드에, 1이면 1번 샤드에 …
- 샤딩 키 전략(Sharding Key)
- = partition key
- 데이터 분산 전략을 정하는 하나 이상의 칼럼(ex. user_id 등)
샤딩 문제점
- 데이터 재 샤딩(Resharding)
- 하나의 샤드로 감당이 안될 때
- 샤드 간 데이터 불균형(샤드 소진 속도가 다른 경우)
- 유명 인사 문제(Celebrity, hotspot key)
- 특정샤드에 질의가 집중되어 서버에 과부하가 걸리는 문제
- 조인과 비정규화
- 한 DB를 여러 샤드로 나누면 여러 샤드에 걸친 데이터를 조인하기 힘들 수 있음
- 데이터베이스 비정규화 → 한 테이블에서 질의가 수행되도록 해야 함
| 사용자 규모 확장
- 시스템 규모 확장은 지속적이고 반복적
- 정리
- 웹 계층은 무상태
- 모든 계층의 다중화
- 가능한 한 많은 데이터 캐시
- 여러 데이터 센터 지원
- 정적 컨텐츠는 CDN을 통해 서비스
- 데이터 계층은 샤딩을 통한 규모 확장
- 계층은 독립적 서비스로분할
- 시스템의 지속적 모니터링, 자동화 도구 활용
| 개략적 규모 추정
- 얼마나 많은 요청을 처리해야 하는지, 얼마나 데이터를 저장해야 하는지, 얼마나 많은 서버가 필요한지를 개략적으로 추정하는 것 ⇒ 시스템 용량이나 성능 요구 사항을 개략적으로 추정
- 초기 설계 단계에서 과부하 방지, 비용 효율적 설계, 확장성 확보를 위해 수행 → 완벽한 값이 아닌 근사치 구하는게 목적
- 개략적인 규모 추정은 보편적으로 통용되는 성능 수치에서 사고 실험을 행해 추정치를 계산 ⇒ 어떤 설계가 요구사항에 부합할지 보기 위한 것
주요 지표
- QPS (Queries Per Second)
- 저장소 요구량
- 한 사용자당 데이터 크기 x 사용자 수 +(로그, 이미지, 백업 등 고려하여 여유율 추가)
- 캐시 요구량
- 서버 수
- 서버 수 = (최대 QPS / 서버 1대 처리 능력) × 여유율
2의 제곱수
- 데이터 볼륨의 단위를 2의 제곱수로 표현할 경우
-
| 2의 x 제곱 | 근사치 | 이름 | 축약형 |
|---|
| 10 | 1,000(천) | 1킬로바이트 | 1KB |
| 20 | 1000,000(백만) | 1메가바이트 | 1MB |
| 30 | 1000,000,000(10억) | 1기가바이트 | 1GB |
| 40 | 1조 | 1테라바이드 | 1TB |
| 50 | 1000조 | 1페타바이트 | 1PB |
| 시스템 설계
요구사항 이해
- 구체적으로 어떤 기능을 만들어야 하는지?
- 제품 사용자 수가 얼마나 되는지?
- 회사 규모는 얼마나 빨리 커질지 예상하는지? 이후의 규모가 어떻게 될지 어떻게 예상하는지?
- 회사가 주로 사용하는 기술 스택은 무엇인지?
- 설계 단순화를 위해 활용할 수 있는 기존 서비스는 어떤 것들이 있는지?