이 글은 Martin Kleppmann의 데이터 중심 애플리케이션 설계를 읽고 기억하고자 적는 게시글입니다.
1. 데이터 시스템에 대해
Martin Kleppmann이 1 장에서 중요하게 말하는 것은 상황에 맞는 데이터 시스템을 구축해야 한다. 라고 요약 할 수 있을 것 같습니다.
기존에는 데이터베이스, 큐, 캐시 등 다양한 범주의 도구들이 있었지만, 지금은 범주 간의 특징을 동시에 가진 도구들과 새로운 범주의 도구들이 등장하면서 이 책에서는 그 것을 데이터 시스템이라는 말로 모아서 말하고 있습니다.
이제는 데이터를 저장하는 것 뿐만 아니라 처리하는 방안을 고려하기 시작했고, 검색 서버의 경우 캐시나 색인을 유지하는 것도 사용자가 아닌 애플리케이션 설계 상에 존재합니다.
1 장의 핵심 내용은 다음과 같이 3가지의 소프트웨어 시스템의 주요 관심사입니다.
1. 신뢰성
- 애플리케이션은 사용자가 기대한 기능을 수행한다.
- 시스템은 사용자가 범한 실수나 예상치 못한 소프트웨어 사용법을 허용 할 수 있다.
- 시스템 성능은 예상된 부하와 데이터 양에서 필수적인 사용 사례를 충분히 만족한다.
- 시스템은 허가되지 않은 접근과 오남용을 방지한다.
위의 4가지는 소프트웨어가 기대하는 신뢰성입니다. 무언가가 잘못되더라도 모든 사항이 올바르게 동작하면 “이 소프트웨어는 신뢰성이 있다.”라고 말 할 수 있습니다.
여기서 잘못될 수 있는 일을 결함이라고 합니다. 그리고 이러한 결함을 대처하는 시스템을 내결함성과 탄력성을 지녔다고 말합니다.
모든 결함에 대해서 견딜 수 있는 서버는 존재하지 않기 때문에 특정 유형의 결함에 대한 내성이라고 이야기 하는 것이 맞다고 말합니다. 넷플릭스에서는 이런 내결함성을 최대화하기 위해서 개별 프로세스를 고의로 죽이는 카오스몽키와 같은 방법을 통해 “이런 문제가 생겨도 정상적으로 동작하나?”를 몸소 시행하는 경우도 있습니다.
또한 보통 경함과 장애를 같은 의미로 사용하기도 하는데, 실제로는 다른 의미 입니다.
결함은 특정 사양을 벗어난 시스템의 구성 요소라고 하지만, 장애는 사용자가 사용하는 서비스 자체가 멈춰서 제공되지 못하는 상황을 말합니다. 즉 장애가 더 심각한 말입니다.
1. 하드웨어 결함
하드웨어의 결함은 독립적입니다. 예를 들어 한 기기에서 결함이 발생해도 다른 기기는 정상적일 가능성이 매우 높습니다. 두 기기가 결함의 원인을 공유하고 있지 않기 때문입니다.
대체적으로 많은 서버를 운영하는 서비스는 자주 하드디스크의 결함이 발생한다고 합니다. 그래서 특정 하드디스크의 결함이 발생 할 때 서비스의 장애로 이어지지 않도록 다양한 방법을 생각합니다.
하드웨어에 중복을 넣는다.
하드웨어에 중복을 넣는 방법이 있습니다. 가장 잘 알려진 데이터 저장 방식의 RAID 구성도 있고 서버는 다운되었을 때 다른 서버가 요청을 받을 수 있도록 다중화 할 수 있고, 핫 스왑(이건 뭐지)이 가능한 CPU를 놓을 수 있습니다.
옛날에는 이러한 방식으로 하드웨어 세팅을 고가용성(High Availability)를 필요로 하는 서버에만 사용했다면, 요즘에는 불안정한 Cloud 서버를 사용하게 되면서 점차 주로 사용하는 방식이 되었습니다.
2. 소프트웨어 결함
소프트웨어 결함은 예상하기가 어렵고 노드 간 상관관계로 인해서 한 서버가 영향을 받으면 다른 서버도 영향을 받을 수 있습니다.
이런 문제는 실제로 유발되기 전까지 찾기 어려 울 수도 있고, 신속한 해결책은 없습니다.
결국 답은 빈틈없는 테스트, 프로세스의 격리, 죽은 프로세스의 재시작 허용, 측정, 모니터링 등 지속적인 확인이 필요하다고 합니다.
2. 확장성
저자는 확장성이라는 말을 그저 부하에 대처하는 시스템 능력이라는 말로 퉁치지 말라고 말합니다. 확장성을 이야기하는 건 시스템 자체의 능력보다 추가적인 부하를 다루기 위한 계산 자원의 투입 방안이나 시스템의 부하가 커졌을 때 대처하기 위한 선택을 고려하는 것 입니다.
부하를 어떻게 기술할까?
확장성을 논하기 위해서 가장 먼저 부하가 무엇인지를 정해야합니다. 그래야 “부하가 2배가 되면 어떻게 될까?”라는 근본적인 문제에 접근 할 수 있습니다.
대체적으로 부하는 부하 매개변수라는 특정한 숫자를 사용한다고 합니다. 시스템 설계마다 부하라고 부를 매개변수를 선택하는 기준이 달라지기 때문에 확장성을 고려한다면, 부하 매개변수를 알맞게 선택해야합니다.
일반적으로 사용되는 부하 매개변수에는 웹 서버의 초당 요청 수, 데이터베이스의 읽기 대 쓰기 비율, 대화방의 동시 활성 사용자, 캐시 적중률 등이 있습니다. 평균 부하가 중요 할 때가 있고, 소수의 극단적인 경우 ( 주말 오후의 쇼핑 앱의 부하 )를 중요시 할 때도 있습니다.
이 책에서는 좋은 예시를 가지고 설명해주었는데, 바로 트위터의 타임라인에 대한 데이터 파이프라인입니다.
기존에는 유저와 트윗, 팔로우 데이터를 담은 데이터베이스를 조인하여 서비스를 제공했었습니다. 하지만 이 방식에서 문제점은 모든 데이터베이스가 축적되어지는 형태이기 때문에 규모가 커질수록 큰 부하가 발생합니다.
그래서 트위터에서는 부하를 차별적으로 처리하기 시작합니다. 모든 트윗을 저장하는 큐에 데이터를 저장 한 뒤 각 유저의 트윗 데이터베이스로 전송합니다. 그리고 유저가 트윗을 읽을때는 자신의 타임라인만 읽어오면 (API) 끝입니다.
여기서 저자는 팬아웃이라는 개념을 사용합니다. 특정 액션을 취할 때 중간 과정이 얼마나 들어있는지를 표현하는데 예를들면 위의 트위터의 “트윗 게시”는
- 트윗을 큐로 전송
- 큐에서 각 팔로워들의 타임라인으로 전송
이렇게 두 번의 동작을 수행합니다. 하지만 읽을 때는
- 사용자의 타임라인 읽기
단 한 번의 동작으로 끝납니다.
트위터가 이러한 동작을 사용 한 이유는 읽기 요청이 쓰기 요청보다 많기 때문입니다. 자주 일어나는 동작의 부하를 줄이는게 더 좋다는 생각이죠.
애플리케이션 기반에서 파이프라인의 설계에서는 이렇듯 상황에 맞는 방식 적용이 중요함을 알게 되는 시간이였습니다.
성능은 어떻게 기술할까?
부하에 대해서 기술 할 수 있다면 마찬가지로 성능을 기술 할 수 있어야 합니다. 그렇게 되면 다음의 질문을 해소 할 수 있습니다.
“동시간대 평균 요청량이 지금의 2배가 된다면 자원을 어떻게 향상시켜야 할까? 혹은 어떻게 대처해야 할까?”
보통 일괄 처리 시스템에서는 처리량(throughput)을 말하고, 온라인 서비스에서는 서비스 응답 시간(response time)을 사용한다고 합니다.
이러한 성능 지표를 볼 떄 일반적으로 분포를 보게 되는데, 이떄 값이 대부분 일정하지 않을 것이라고 합니다. 이럴 때 다음의 질문에 대해 답하는 과정에서 좋은 답을 얻을 수 있었습니다.
사용자가 서비스를 사용 할 떄 응답을 받는 시간은? 이 답은 중앙값일 가능성이 높다고 말합니다. 평균의 경우 이상치에 대해 민감 할 가능성이 있기 때문에 대부분의 성능 평가에서는 백분위를 사용하는게 옳을 수 있습니다. 뿐만아니라 이상치의 개념도 백분위로 95% 99% 99.9%와 같은 지표로 찾는 것도 좋다고 합니다.
서비스를 개발 할 때 이러한 99.9%의 이상치로 인해 HOL Blocking이 발생하고, 그로인해 다른 클라이언트에게도 영향을 미칠 수 있다고 합니다. 하지만, 이러한 부하를 잡는 것은 효율이 좋지 않습니다.
그러면 부하에 대한 대응은?
부하에 대응하는 첫 방안인 하드웨어 측면의 방법은 용량 확장이 있습니다. 보통 수직적인 확장과 수평적인 확장이 있는데, 이러한 확장을 수행할 때 실용적인 측면의 확장에 신경써야 한다고 합니다. 적절한 성능의 장비를 적절한 수로 배치하는 것이라고 말 할 수 있습니다.
두 번째로 탄력적 확장이 있는데, 부하가 많이 발생하는 특정 시점에 컴퓨팅 자원을 동적으로 할당하는 방법입니다. AWS의 Auto Scaling가 이러한 방법에 해당한다고 생각합니다. 하지만, 이러한 컴퓨팅 방식이 오히려 부하를 예측하기 어려 울 수 있기 때문에 주의해야 한다고 합니다.
세 번째로 부하에 대응할 때 Stateful , Stateless를 잘 구분해야 합니다. RDB는 일반적으로 Stateful인데, 이러한 DB를 여러 노드에 확장한다면, 복잡성이 매우 높아진다고 합니다.