모든 컴포넌트가 단 한 대의 서버에서 실행되는 아키텍처입니다. 단일 서버에서는 웹, 앱, 데이터베이스, 캐시 등이 전부 서버 한 대에서 실행됩니다. 그렇다면 단일 서버에서 사용자의 요청은 어떻게 처리될까요?
Domain Name Service, DNS
에 물어봐야 합니다. 내가 www.naver.com에 접속하려고 하는데, 얘의 실제 주소는 어디니?와 같은 질문을 하는 것입니다. 이때 DNS는 도메인 이름에 맞는 IP 주소를 찾아 사용자에게 반환해 줍니다.사용자가 늘어난다면 서버 하나로는 감당이 버거울 수 있습니다. 따라서 하나의 서버는 웹이나 트래픽을 처리하도록 하고, 하나의 서버는 데이터베이스를 관리하도록 하면 독립적으로 확장이 가능해집니다.
❕관계형 데이터베이스
관계형 데이터베이스는 Relational Database Management System, RDBMS
라고도 부릅니다. MySQL, 오라클 PostgreSQL 등이 있습니다. 관계형 데이터베이스는 자료를 열과 칼럼으로 표현하며, SQL을 사용해서 여러 테이블에 있는 데이터를 JOIN하여 합칠 수 있습니다.
❕비관계형 데이터베이스
비관계형 데이터베이스는 NoSQL
이라고도 부릅니다. Redis, Neo4j, HBase 등이 있습니다. NoSQL은 다시 네 부류로 나눌 수 있으며, 그 분류는 다음과 같습니다.
또한 비관계형 데이터베이스는 일반적으로 조인 연산을 지원하지 않습니다.
❕언제나 관계형 데이터베이스? NO!
많은 개발자들이 관계형 데이터베이스를 선택하고 있습니다. 오랫동안 사용해 온 기술이고 익숙하기 떄문입니다. 하지만 내가 어떤 시스템을 구축할 것이냐에 따라 관계형 데이터베이스가 부적합할 때도 있습니다. 관계형 데이터베이스가 언제나 정답인 것은 아닙니다. 아래는 대표적으로 비관계형 데이터베이스가 바람직한 경우입니다.
❕수직적 규모 확장 (Scale Up)
수직적 규모 확장은 서버의 사양을 높이는 것입니다. 즉 다시 말해서 더 좋은 컴퓨터를 사는 것이죠. 서버로 유입되는 트래픽 양이 적을 때는 나쁘지 않은 선택일 수 있습니다. 또한 이 방법의 가장 좋은 점은 규모 확장 방법이 간단하다는 것입니다. 하지만 수직적 규모 확장에는 다음과 같은 단점이 있습니다.
❕수평적 규모 확장 (Scale Down)
이에 대한 대응책으로 등장한 것이 수평적 확장입니다. 대부분의 대규모 애플리케이션에서는 수평적 규모 확장 방법을 채택해 사용하고 있습니다. 수평적 규모 확장은 서버의 개수를 늘리는 방법입니다.
로드밸런서는 부하 분산 집합에 속한 웹 서버들에게 트래픽 부하를 고르게 분산하는 역할을 합니다. 그렇다면 로드밸런서가 어떻게 동작하는지 볼까요?
로드밸런서를 사용하게 되면 서버가 다운되었을 때, 아직 남아 있는 멀쩡한 서버로 요청을 위임하여 장애를 대응할 수 있습니다. 또한 웹사이트로 유입되는 트래픽이 가파르게 증가하면 로드밸런서가 우아하게 요청을 분배하여 서버에 보내 줍니다.
데이터베이스 역시 다중화하여 관리할 수 있습니다. 보통 주 데이터베이스와 부 데이터베이스 관계를 설정하고, 데이터 원본은 주 서버에 저장하고 복사본을 부 서버에 저장하는 방식입니다.
쓰기 연산은 주 서버에서만 지원합니다. 부 서버에서는 그 사본을 전달받고, 읽기 연산을 지원하는 식으로 주 서버를 보조합니다. 이러한 방식으로 데이터베이를 다중화하면 어떤 장점이 있을까요?
캐시 계층은 데이터베이스에 자주 접근하는 데이터들을 잠시 보관해 두는 공간입니다. 이러한 캐시 공간을 두면 데이터베이스의 부하를 줄일 수 있고, 자주 읽는 데이터들을 아주 빠르게 불러올 수 있습니다. 사용자의 웹 요청에 대해 캐시에 있는 데이터라면 그냥 읽어 오고, 아니라면 데이터베이스에 접근해서 데이터를 읽어 오는 방식입니다. 이러한 캐시 전략을우선 읽기 전략이라고 합니다. 하지만 캐시는 여러 주의점을 고려해서 사용해야 합니다. 캐시 계층을 두기 전에 어떤 점을 생각해 봐야 할까요?
❕어떤 상황에?
데이터가 갱신은 자주 일어나지 않지만 읽어오는 작업이 자주 일어나는 경우에 적합합니다.
❕어떤 데이터를?
캐시는 데이터를 휘발성 메모리에 두는 구조로, 영구적으로 저장되어야 하는 데이터를 캐시에 두는 것은 바람직하지 않습니다.
❕어떻게 만료되지?
만료된 데이터는 캐시에서 삭제해 캐시 메모리를 확보하는 정책 등이 필요합니다. 만료 기간 같은 경우는 너무 짧으면 데이터베이스 접근이 많아져 캐시를 두는 것이 무의미해지고, 너무 길면 데이터베이스와 다른 정보가 저장되어 있을 수 있기 때문에 주의해야 합니다.
❕장애를 대처하는 방법은?
캐시 서버를 한 대만 두는 경우 해당 서버가 Single Point of Failure, SPOF가 되어 버릴 가능성이 있습니다. 단일 장애 지점이란 어떤 특정 지점에서의 장애가 전체 시스템의 동작을 중단시켜버릴 가능성이 있는 경우입니다. 따라서 이러한 상황을 피하기 위해서는 여러 지역에 걸쳐 캐시 서버를 분산시키는 것이 좋습니다.
❕얼마나 크게?
캐시 메모리가 너무 작은 경우 데이터가 캐시에서 자주 삭제되고, 그만큼 데이터베이스에 자주 접속하여 데이터를 읽어와야 하기 때문에 캐시의 성능이 떨어집니다. 이를 막기 위해서는 캐시 메모리를 여유롭게 잡아야 합니다.
❕데이터 방출 정책은?
캐시 메모리가 꽉 차면 새로운 데이터를 받아오기 위해 기존의 데이터는 내보내야 합니다. 기본적으로 Least Recently Used로 가장 오래전에 사용한 데이터를 방출하거나, Least Frequently Used로 사용 빈도가 낮은 데이터를 방출합니다.
CDN은 정적 콘텐츠를 전송하는 데 쓰이는 지리적으로 분산된 서버의 네트워크입니다. 이미지, 비디오, CSS, JavaScript 파일 등을 캐시할 수 있습니다. 사용자가 어떤 웹 사이트를 방문하면, 그 사용자에게 가장 가까운 CDN 서버가 정적 콘텐츠를 전달하게 됩니다. CDN 서버가 가까이 있으ㅕㅁㄴ 가까이 있을수록 더욱 빠른 로딩 시간을 자랑합니다.
❕비용
CDN은 보통 제3 사업자에 의해 운영되고 데이터 전송 양에 따라 비용이 청구되기 때문에 비용에 대한 점을 고려해야 합니다.
❕적절한 만료 시한 설정
시의성의 중요한 콘텐츠의 경우 만료 시점을 잘 정해야 합니다. 너무 긴 경우 콘텐츠의 신선도가 떨어지고, 너무 짧은 경우 원본 서버에 빈번하게 접속해야 합니다.
❕장애 대처 방안
CDN이 죽었을 경우 원본 서버로부터 직접 콘텐츠를 가져오도록 하는 장애 대처 방안이 마련되어 있어야 합니다.
웹 계층을 수평적으로 확장하는 방법에 대해 고민해 봅시다. 그러기 위해서는 상태 의존적인 아키텍처에서 벗어나, 무상태 웹 계층을 만들어야 합니다. 위 그림은 서버가 클라이언트의 정보인 상태 정보를 보관하는 상태 정보 의존적인 아키텍처입니다. 이런 식의 아키텍처가 구현되었을 경우, 같은 클라이언트로부터의 요청은 항상 같은 서버로 전송되어야 합니다.
대부분의 로드밸런서가 이를 지원하기 위해 고정 세션이라는 것을 제공하고 있습니다. 하지만 이는 로드밸런서에 부담을 줍니다. 따라서 무상태 아키텍처를 구현하는 쪽을 권장합니다.
이러한 무상태 아키텍처에서 클라이언트는 어떤 웹 서버로든 요청을 전송할 수 있습니다. 웹 서버는 상태 정보가 필요할 경우 공유 저장소로부터 데이터를 가져옵니다. 따라서 상태 정보는 웹 서버로부터 물리적으로 분리되어 있고, 웹 서버가 더는 상태 정보에 의존적이지 않을 수 있습니다.
상태 정보를 저장하는 공유 저장소는 관계형 데이터베이스일 수도 있지만, 일반적으로는 NoSQL을 사용합니다.
위의 그림은 두 개의 데이터 센터를 이용하는 사례입니다. 보통 사용자는 물리적으로 가장 가까운 데이터 센터로 안내되는데, 이것을 지리적 라우팅(geoDNS-routing)이라고 합니다. 위와 같은 아키텍처로 데이터 센터를 여러 개 연결하려면 아래와 같은 기술적 난제들을 해결해야 합니다.
❕해결해야 하는 문제점
메시지 큐란 무엇일까요? 메시지 큐는 데이터의 무손실(Durability), 즉 메시지 큐에 일단 보관된 메시지는 소비자가 꺼낼 때까지 안전히 보관된다는 특성을 보장하는 비동기 통신을 지원하는 컴포넌트입니다. 메시지 큐의 기본 아키텍처는 위 그림과 같습니다. 생산자 또는 발행자라고 불리는 입력 서비스가 메시지를 만들어 메시지 큐에 발행합니다. 큐에는 보통 소비자 혹은 구독자라고 불리는 서버가 연결되어 있는데, 메시지를 받아 그에 맞는 동작을 수행합니다.
메시지 큐를 이용하면 서비스 또는 서버 간 결합이 느슨해져서, 규모 확장성이 보장되는 안정적 애플리케이션을 구성하기 적합합니다.
에러 로그를 모니터링하는 것은 중요합니다. 시스템의 오류와 문제들을 보다 쉽게 찾아낼 수 있도록 하기 때문입니다. 서버 단위에서 로그를 기록할 수도 있지만, 로그만 처리하는 단일 서비스로 빼서 편리하게 관리할 수 있습니다.
매트릭을 잘 수집하면 비즈니스적으로 유용한 정보를 얻을 수 있습니다. 또한 시스템의 현재 상태를 쉽게 파악할 수도 있습니다. 다음은 매트릭으로부터 알아낼 수 있는 몇몇 유용한 정보들입니다.
시스템이 커지면 생산성을 높이기 위해 자동화 도구를 활용해야 합니다. CI를 도와주는 파이프라인을 만들어 빌드, 테스트, 배포까지 자동화하면 더욱 편리하게 개발할 수 있습니다.