네이버 오픈소스를 활용하여 확장성 있는 서버 아키텍처를 구축하고 성능 개선해보기 - 1편

12월 31일, 2017

본 글은 한양대학교 컴퓨터공학부의 3학년 2학기 수업인 소프트웨어스튜디오2 의 과제를 진행하며 학습하고 구현한 내용을 담고 있습니다.

실제 다수의 사용자를 처리하기 위해 완벽하게 세팅을 하는 목적 보다는 확장성 있는 서버 아키텍처는 어떻게 구성해야 하며 활용할 수 있는 오픈소스는 무엇이 있는지를 배우는 것에 중점인 과목이기 때문에 실무 상황과는 다소 차이가 있을 수 있습니다.

또한 이 글에서 이해를 돕기 위해 첨부된 그림은 모두 직접 그린 그림입니다. 퍼가실 때 출처라도 남겨주시면 감사하겠습니다. (아이콘 출처: visualpharm.com, iconfinder.com, iconarchive.com, thenounproject.com)

개론

제목이 무지 깁니다. “네이버 오픈소스를 활용하여 확장성 있는 서버 아키텍처를 구축하고 성능 개선해보기” 라니, 무려 41자네요. 이 문장을 크게 3개의 단어로 나누면 “네이버 오픈소스”, “확장성 있는 서버 아키텍처”, 그리고 “성능 개선”이 될 것입니다. 다수의 사용자가 몰렸을 때의 성능을 확장성을 통해 개선해보되, 네이버 오픈소스를 활용해서 해보는 내용이죠!

굳이 네이버 오픈소스인 이유는 해당 과목이 네이버와 협력해서 진행되고 있는 과목이기 때문입니다. 네이버에서 직접 강의에 참여하여 자신들의 오픈소스 프로젝트를 소개하기도 했죠.

이 과목은 무언가 한 가지 작은 주제에 집중해서 깊게 배우게 목표는 아니었습니다. 그것보다는 오픈소스를 활용하여 다양한 실험을 진행해보며 개선을 위해 어떤 방법을 시도할 수 있는지, 또 무엇이 괜찮은 방법인지를 체크해보는 목적의 과목이라고 보는 게 더 알맞은 것 같습니다.

이 글은 이 수업을 들으면서 배운 것들과 구현한 내용들을 쓸 예정인데, 무작정 제가 개발한 내용들을 설명하는 것보단, 전체적인 확장성 있는 시스템에 대해 먼저 정리를 진행하고, 이를 어떻게 구현했는지의 순서로 작성하려 합니다.

글은 크게 4가지 섹션으로 구성하려 하는데, 첫째로는 “확장성 있는 서버 아키텍처”, 다음으로는 “성능 개선 방법”, 그리고 이들을 구현하기 위한 “네이버 오픈소스”, 마지막으로는 “네이버 오픈소스를 활용하여 성능 개선을 위해 한 작업들“로 풀어 나갈 예정입니다.

첫째. 확장성 있는 서버 아키텍처

확장성이 있는 서버 아키텍처라 함은 수천, 수만 명의 사용자가 접속했을 때도 대응이 가능한 구조를 갖췄다는 것을 의미합니다. “대응이 가능한 구조”라는 말은 당장 다수의 사용자를 커버할 수 있는 상태를 말하는 건 아닙니다. 아직 서버를 구입하지 않았어도, 서버 구비만 하면 손쉽게 서버의 성능을 향상시킬 수 있을 정도로 아키텍처 구성이 완료되었고, 준비되어 있음으로 보는 것이 더 바람직합니다.


큰 기업들은 이렇게 수많은 노드와 함께 서버를 구성합니다. / 페이스북 데이터센터 / 출처: www.oss.kr

기본 서버 구성

그럼 먼저 확장성을 갖추지 않은, 아주 기본적인 서버 구성에 대해서 먼저 살펴보도록 합시다. 가장 범용적으로 사용되는(혹은 사용되었던) 서버 스택으로는 APM이라는 녀석이 있습니다. 이는 Apache, PHP, MySQL의 앞자를 따서 만든 용어인데, 웹 서버로는 Apache를 사용하고, 서버사이드 언어로는 PHP, 데이터베이스MySQL을 사용한 조합입니다.

웹 서버는 Application Layer(OSI 7계층)에 있는 HTTP라는 프로토콜에 맞춰 동작시키게 한(implementation) 소프트웨어입니다. 그중 가장 대표적인 게 Apache고, 요즘은 nginx가 뜨고 있죠. 기본 작업으로는 사용자에게 요청이 오면 그 요청을 해석하여 서버 컴퓨터에 있는 파일을 불러주고 보내주는 일을 하죠.

서버사이드 언어는 사용자와 동적으로 통신하기 위한 언어라고 할 수 있습니다. 예를 들어 사용자가 아이디와 비밀번호를 입력하고 로그인을 시도했을 때, 해당 아이디가 있는지, 또 비밀번호가 일치하는지를 확인하는 작업과, 만약 로그인이 성공했다면 HTML파일에 해당 유저의 이름을 넣어서 HTML을 편집한 후 전송하는 역할을 서버사이드 언어에서 하죠.

데이터베이스는 위 예제의 아이디나 비밀번호 같은 정보를 파일로 관리하지 않고 별도의 소프트웨어를 통해 관리하기 위해 사용합니다. 데이터베이스 관련 수업을 듣거나 서적을 보면 왜 데이터베이스 소프트웨어를 사용해야하는지에 대해 알 수 있을 것입니다.


한 때는 소규모 사이트를 제패했던 APM 스택의 환경

사실 서버를 구성하기 위해서 위의 3개를 모두 설정할 필요는 없습니다. Apache같은 웹 서버를 따로 쓰지 않아도 Spring이나 Flask, Django 등 웹 프레임워크 자체에서 웹 서버의 기능도 겸하는 경우도 많습니다. 다만 이들을 분리함으로 인해 작업이 명료해지고 추상화되어 전체적인 구조에서 보면 더 간단해지는 효과 때문에, 서버를 개선하면서 기능을 나누면 나누지 통합하는 과정은 거의 진행되지 않습니다.

웹 서버는 사용자들이 접속하는 출입구 역할을 담당하며 프로세스들이 안전하게 실행됨을 보장합니다. 서버사이드 언어는 사용자의 입력에 따라 동적인 결과를 출력하는 역할만 담당합니다. 데이터베이스는 굉장히 안정적이고 파워풀하게 데이터를 저장하고 관리하는 역할을 맡죠.

Scale-up과 Scale-out

위에서는 추상화를 통해 서버에서 필요한 작업들을 어떤 소프트웨어가 분담하는가에 대해 이야기를 해봤습니다. 하지만 이 글에서 다룰 부분은 “확장성”에 대한 부분입니다. 어떻게 하면 수천, 수만 명의 사용자가 접속했을 때도 대응을 할 수 있을까요? 방법은 크게 2가지가 있습니다.

1. Scale-up (수직확장)

가장 간단한 방법입니다. 그냥 원래 있던 서버의 성능을 높이는 방법입니다. CPU를 싱글코어에서 16코어로, RAM을 4GB에서 128GB로, 비싼 하드웨어로 모두 교체하고 기업용 서버를 구매하면 상당히 성능을 증가시킬 수 있습니다.

하지만 문제가 있어 보입니다. 우선 성능 증가에는 한계가 있습니다. CPU를 아무리 증가해도 10,000개의 코어를 가진 CPU를 지구상에서 찾기는 어려울 것 같습니다. 안정성에도 문제가 있습니다. 하나의 서버에 몰빵해서 성능을 엄청 증가시켰는데 그 서버에 문제가 생겨서 오작동을 하면 이제는 수만 명의 사용자가 서비스를 이용할 수 없게 됩니다. 즉, 안정성은 증가하기는커녕 더 떨어질 수 있습니다.

2. Scale-out (수평확장)

Scale-up과 반대되는 개념입니다. 서버의 성능을 증가시키는 것이 아닌, 서버의 수를 증가시키는 방향으로 전체적인 성능을 향상시키겠다는 방법입니다. 값비싼 전용 장비를 구매할 필요도 없고, 무엇보다 서버 한 개쯤은 죽어도 큰 문제는 없습니다. 안정성도 같이 증가하게 되죠.

Scale-out이 scale-up보다 훨씬 좋아 보이지만 무조건 그렇지만도 않습니다. 다수의 서버를 사용한다는 것은 그만큼 어려운 일이죠. Scale-out으로 인해 concurrency 문제(동시성 제어)가 쓰레드 단위가 아닌, 서버 노드 단위로 발생할 수 있습니다.

Scale-Up과 Scale-Out

Scale-Out과 Scale-Up 방법을 양자택일해서 쓸 필요는 없습니다. 초기에는 적당히 서버의 성능을 높이다가, 어느 순간부터는 서버를 더 구매해서 사용하는 방법이 더 효율적일 수 있습니다. Scale-Out으로 서버를 늘여 나가다가 그 서버들에 CPU나 RAM을 더 달아서 다시 Scale-Up 시킬 수도 있습니다. 하여튼 확실한 건, Scale-Up의 한계 때문에 사용자가 많아진다면 다수의 서버를 관리하기 위한 방법에 대해 고려해야 한다는 점입니다.

Shared Everything과 Shared Nothing

그렇다면 여러 개의 서버를 사용하기 위해 어떤 방법을 쓸 수 있을까요? 조금 더 구체적으로 원래 하나의 서버 노드만 사용하고 있었는데 이를 여러 개의 서버 노드로 나누려면 어떻게 해야 할까요? 크게 2가지 방법이 있습니다.

먼저 세포가 복사되듯이 모든 노드에 똑같은 정보를 복사해서 노드를 복제하는 방법이 있습니다. 다음으로는 전체 데이터를 1/n로 나누어 각자 나눠가져가는 방법이 있죠. 전자를 Shared Everything이라 하고, 후자를 Shared Nothing이라 합니다.

1. Shared Everything

모든 노드가 데이터를 공유하게 됩니다. 각자가 독립된 “완성된 정보”를 가지고 있으므로 가용성은 정말 높습니다. 대부분의 노드가 죽어도 하나의 노드만 정상적으로 작동 중이면 서비스를 작동시킬 수 있으니까요. 하지만 그만큼 큰 문제가 있습니다. 바로 난이도죠. 여러 노드가 항상 같은 정보만 갖게 하기 위해서는 고도의 동기화 기술이 필요합니다.

보통 이런 cocurrency control 문제는 multi-threading이나 multi-processing 에서 많이 겪게 되는데, Shared Everything에서는 네트워크 단위의 concurrency control을 해야 합니다. 엄청난 난이도에 더불어, 오버헤드로 인해 오히려 성능 저하까지 발생할 수도 있죠.

2. Shared Nothing

Shared Everything과 상반되는 개념이니 이 방법은 왠지 간단할 것 같습니다. 노드끼리 같은 정보는 저장하지 않으며 담당 데이터를 나누어 자신이 처리하는 데이터만 가지고 있게 되죠. 예를 들어 서울의 전화를 담당하는 노드는 “02”로 시작하는 전화번호만, 경기도의 전화를 담당하는 노드는 “031”로 시작하는 전화번호만 가지고 있으면 되는 식이죠. 조금 더 구체적으로는 Sharding이나 Partitioning이라는 방법을 통해 데이터를 분할하게 됩니다.

구현 난이도가 낮을 뿐더러, lock 등에 의한 부하가 없기 때문에 상당히 높은 성능 향상을 얻을 수 있습니다. 대신에 노드 별로 중복되는 정보가 없기 때문에 가용성을 증가시키기는 어렵죠.

Shared Everything과 Shared Nothing

Shared Everything과 Shared Nothing도 꼭 양자택일해서 쓸 필요는 없습니다. 적당히 섞어 쓸 수도 있습니다. 먼저 Shared Nothing을 통해 데이터별로 구간을 나누어 쪼개고, 구간 별로는 Shared Everything을 통해 다수의 노드가 관리하는 식으로 아키텍처를 구성할 수도 있습니다. 위의 예제처럼 서울과 경기도, 또 다른 광역시도마다 지역번호를 나누고 지역 번호 내에서는 여러 개의 노드가 데이터를 공유하는 방법이죠. 이를 통해 가용성과 성능을 각각 적당히 높일 수도 있습니다.

둘째. 성능 개선 방법

지금까지 확장성 있는 서버를 구축하기 위해서는 어떤 것들을 고려해야 하는지에 대해 알아봤습니다. 다음으로는 두 번째 주제인 서버 성능 개선 방법에 대해서 얘기해 봅시다.

멀티 노드 구성

그래도 앞에서 확장성에 대해 배웠는데, 이를 성능에 적용시키는 방법을 알아봐야겠죠? 서버를 늘이긴 해야하는데, 서버를 통으로 복제한다고 해서 서비스가 예전처럼 잘 돌아간다는 보장은 없을 겁니다. 서버를 늘이기 전과 후의 동작이 같음이 보장되어야 하죠.

이를 확인하기는 쉽지 않습니다. 작은 것부터 하나하나 봐서 잘 돌아가는지 확인을 해봅시다. 앞서서 기본적인 서버 구성은 “웹 서버 + 서버사이드 언어 + 데이터베이스” 로 많이들 사용한다고 했습니다. 이 3가지 별로 각각 확장 가능성에 대해 논의해 봅시다.

1. 웹 서버

웹 서버는 출입구 같은 역할을 합니다. 사용자가 요청을 보냈을 때, 그에 대응되는 값을 보내주면 되죠. 그 값은 이미지 같은 정적인 파일이 될 수도, 혹은 서버사이드 언어를 호출하고 그 프로그램이 반환한 HTML 등의 결과 값이 될 수도 있죠.

웹 서버는 실시간으로 변화하는 데이터를 거의 가지지 않습니다. 데이터라고 해봤자 정적 파일들이나 설정 파일, 로그 등이 있겠죠. 즉 concurrency control 상당히 쉬운 편입니다. 그냥 같은 파일들을 공유하게 설정하고 여러 노드를 띄우면 되죠.

2. 서버사이드 언어

서버사이드 언어는 조금 더 복잡하고 디테일한 프로그램입니다. 사용자의 로그인이나 회원가입 등의 “기능”을 처리하고, 보통 이에 사용되는 데이터는 데이터베이스에서 가져오죠. 웹 서버와 데이터베이스 사이에 위치한 소프트웨어라고 보아도 되죠.

그리고 이런 서버사이드 언어나 서버사이드 프레임워크(Spring, Django, Flask) 등은 일반적으로 다수의 세션 간의 메모리 공유를 지원하지 않거나, 지원한다고 하더라고 권장하지 않습니다. (특수한 방법을 써서 구현해야 합니다)

세션은 쉽게 말해 사용자와 연결된 끈을 의미합니다. 즉, 세션 간 메모리를 공유하지 않는다는 것은 끈들이 독립적으로 연결됨을 말합니다. 꼬여있지 않기 때문에, 쉽게 여러 그룹으로 나눌 수 있죠.

세션간에 메모리를 공유하지 않고 상호 독립적으로 동작한다면 기존에 한 개의 서버에 1,000개의 세션이 있었던 것을 10개의 서버로 각각 100개씩 떼어 보내도 문제가 없습니다. 이를 고려해서 개발을 진행했다면 웹 서버와 마찬가지로 concurrency control 상당히 쉽게 됩니다.


싱글 노드에서 쓰레드간 메모리 공유가 없었다면 쉽게 멀티노드로 구성할 수 있습니다.

서버사이드 언어는 웹 서버와 함께 (프레임워크에 내장된 미니 웹 서버를 사용할 수도 있습니다) 여러 서버에 설치되게 됩니다. 서버가 그대로 복제되는 것이기 때문에 사용자는 이 서버들 중에 아무 곳에나 요청을 보내도 같은 응답을 받을 수 있습니다. 그래도 사용자 보고 “너 이 서버들 중에 아무 곳에나 보내” 라고 하기는 조금 그러니, 보편적으로 이들 서버를 총괄하여 요청을 받는 관문을 두게 됩니다(물론 그렇지 않는 경우도 있습니다). 관문은 데이터가 도달하면 자신과 연결된 서버 중 아무 곳에 데이터를 전송하게 되죠. 그리고 이 관문을 보통 Load Balancer라고 부릅니다.

가장 기본적인 Load Balancer는 연결된 서버 중 하나에 랜덤으로 데이터를 전송할 것입니다. 조금 더 똑똑한 Load Balancer는 트래픽에 여유가 있는 서버에 데이터를 전송할 수도 있죠. Load Balancer는 소프트웨어로 만들어질 수도 있고, 라우터처럼 소프트웨어 없이 하드웨어로만 구성할 수도 있습니다.


사용자는 이제 Load Balancer에 요청을 보내게 되고,
Load Balancer는 다신과 연결된 서버 중 하나에 그 요청을 그대로 전달하게 됩니다.

3. 데이터베이스

문제는 데이터베이스입니다. 데이터베이스는 원래부터 트랜잭션의 처리로 악명이 높았습니다. ACID 같은 성질도 정하며 concurrency 보장을 위한 기술이 집합된 끝판왕이라고 할 수 있죠.

DBMS의 ACID 성질: Atomicity(원자성) + Consistency(일관성) + Isolation(고립성) + Durability(지속성)

단일 노드 내에서도 multi-theading 때문에 문제가 많았던 소프트웨어입니다. 멀티 노드로 구성하면 더더욱 문제는 심각해지겠죠. Lock만 보아도 네트워크 단위의 lock은 얼마나 많은 오버헤드가 생길지 감이 안 잡힙니다.

사실 위에서 말했던 Shared Everything과 Shared Nothing 기법은 이런 문제가 많은 데이터베이스를 위해서 연구되었다고 보아도 됩니다. 웹 서버나 서버사이드 언어는 이런 고려를 덜 하도록 하고, 그 대신에 데이터베이스에서 이 문제를 확실하게 처리하는 방향으로 기술이 발전한 것이라고 할 수 있죠.

Master-Slave 구조

실제로는 위 그림처럼 Write Operation을 수행하는 노드는 하나만 두고 Read Operation만을 수행하는 노드를 여럿 두어 동시성을 제어합니다. 이런 것을 Master-Slave 구조라고 하죠. 이런 식으로 노드를 구성하면 동기화의 방향이 양쪽에서 한 쪽으로 바뀌므로 구현 난이도가 훨씬 쉬워지고 오버헤드도 적어지게 됩니다. 실제 상황에서 Read Operation이 Write Operation보다 압도적으로 많이 때문에 이런 방법으로 멀티 노드를 구성해도 상당한 효과를 볼 수 있죠.

캐시 사용

사용자 대응을 위해 무작성 여러 개의 서버를 사용하는 방법도 있겠지만, 다른 방법도 많을 겁니다. 그중에서도 “캐시”라는 녀석에 대해서 집중적으로 다뤄보도록 합시다.

1. 데이터베이스로의 캐시

캐시(Cache)의 의미는 광범위합니다. 속도를 높이기 위해 임시적으로 사용되는 것이면 모두 캐시라고 할 수 있죠. 캐시 메모리는 주 메모리의 속도를 높이기 위해 사용되는 하드웨어고, 디스크 캐시는 디스크 접근시간을 줄이기 위해 사용되죠. 웹 브라우저의 캐싱은 같을 것으로 예상되는 정보들을 네트워크를 통해 다시 요청하는 것이 아니라, 로컬 컴퓨터 내에 있는 리소스를 사용하는 것을 의미합니다.

여기서 말하는 캐시는 데이터베이스로의 캐시를 의미합니다. 서버 애플리케이션에서 쿼리를 사용하여 DBMS에 데이터를 요청하고 그 결과를 사용해서 사용자에게 전달하는데, 이때 자주 사용되는 쿼리에 대한 결과 값을 캐시에 임시로 올려두어 속도를 증가시키겠다는 뜻이죠.


그림처럼 기존 APM 스택에 캐시 데이터베이스를 끼워 넣을 수도 있습니다.

이러한 캐시는 전체 데이터가 온전하게 들어있음을 보장하지는 않습니다. 원본 데이터는 보통 RDBMS에 저장하고, 캐시는 "있으면 좋고 없으면 그만" 같은 느낌으로 사용하는 것이지 “난 항상 캐시에 데이터를 저장하니까 캐시엔 데이터가 분명 들어있을 거야” 같은 생각을 갖고 개발을 진행했다가는 문제가 생길 수도 있습니다. 그렇기 때문에 응용 프로그램에서는 아래 그림 같은 플로우로 캐시를 사용해야 합니다.

캐시 데이터베이스 Search/Set 시나리오

2. 관계 모델을 key-value 형태로 캐싱 하기

관계형 데이터베이스는 “릴레이션”이라는 구조 내에 데이터를 저장하고, “쿼리”라는 명령어를 통해 데이터를 가져오거나, 저장하거나, 수정하거나, 삭제합니다. 하지만 캐시는 일반적으로 “key-value” 형태의 데이터 모델을 사용합니다. 하나의 키에 하나의 값이 대응되는 지극히 간단한 구조죠. 애초에 RDB는 현실 세계의 데이터를 효율적이고 완전하게 저장하기 위한 용도고, 캐시는 일부 데이터에 대해 빠른 속도를 보장하기 위한 목적이기에 둘의 저장 방식은 전혀 다릅니다.

때문에 캐시 데이터베이스는 설치하기만 하면 단순히 RDBMS의 성능을 개선해주는 소프트웨어가 아닙니다. 개발자가 스스로 자주 사용하는 쿼리를 보고, 고유한 key를 정의하여 필요한 정보를 캐싱해야 합니다. SQL 질의문 하나로 예를 들어 봅시다.

SELECT *
FROM goods, (SELECT COUNT(*) FROM `goods_likes` WHERE goods_id = goods.id)
WHERE goods.category = 'food'
ORDER BY register_date DESC

카테고리가 food 인 상품을 최신 순으로 가져오되 그 상품에 대한 ‘좋아요 수’를 함께 가져오는 쿼리입니다. 조건에 정렬에 중첩 질의까지 붙어있으니 꽤나 오래 걸릴 것 같습니다. 이 쿼리에 대한 결과를 key-value store를 이용하여 저장해보도록 합시다. depth를 사용할 수 있다면 아마 이런 식으로 할 수 있을 것 같네요.

├─ food
│   ├─ 2017-12-01
│   │       ├─ 상품1: 정보1, 정보2, ... 정보n, 좋아요 수
│   │       ├─ 상품2: 정보1, 정보2, ... 정보n, 좋아요 수
│   │       ├─ 상품3: 정보1, 정보2, ... 정보n, 좋아요 수
│   │       └─ 상품4: 정보1, 정보2, ... 정보n, 좋아요 수
│   ├─ 2017-12-02
│   │       ├─ 상품10: 정보1, 정보2, ... 정보n, 좋아요 수
│   │       ├─ 상품11: 정보1, 정보2, ... 정보n, 좋아요 수
│   └─ ...
├─ digital
|   └─ ...
│ ...

위 쿼리를 위해서는 이런 구조체에서 먼저 카테고리, 즉 food로 이동한 뒤, 하위의 키인 날짜를 바탕으로 소팅을 해서 그 안에 있는 상품 정보를 가져올 수 있습니다. 이런 복잡한 구조체를 지원하지 않아도 food/2017-12-01/상품1 처럼 키를 정의하고 range-search를 통해 검색을 수행할 수도 있습니다.

이처럼 캐시 데이터베이스를 사용하기 위해서는 완전히 다른 형태의 데이터베이스를 쿼리를 보고 개발자가 해석하여 key-value store에 적합한 구조를 만드는 식으로 해결해야 합니다. 물론 위 같은 예시는 원시적인 방법이고, 좋은 cache store는 더 풍부한 기능을 제공하여 아주 간단하고 깔끔하게 캐싱을 할 수 있습니다.

3. 캐시의 멀티노드 구성

캐시 데이터베이스는 기능이 단순하고 성능이 높다고 했습니다. 데이터의 안정성을 잘 보장하지도 않습니다. (단, 단순 캐시 기능 이상으로 NOSQL로 취급되는 소프트웨어는 안정성을 보장하기도 합니다) 안정성이 매우 중요한 게 아니라는 것은 concurrency control을 엄격하게 진행할 필요가 없다는 것을 의미하기도 하죠. 즉, RDBMS보다는 멀티노드 구성이 훨씬 쉬운 편입니다.

물론 캐시도 데이터를 저장하기 때문에 데이터를 어떻게 저장할 것인가 대해서는 고려를 해야 합니다. Shared Everything과 Shared Nothing 기법도 당연히 캐시에서의 고려 사항이죠. 조금 더 안전하고 hit-rate가 높은 캐시를 구성하기 위해서는 Shared Everything을 쓰면 될 것 같습니다. 성능과 트래픽 분산을 위해서라면 Shared Nothing 을 써야 겠지요.


캐시가 데이터 조회의 속도를 증가시킨다고 말했는데, 캐시 사용으로 인해 이 뿐만 아니라 다른 이득도 볼 수 있습니다.

만약 캐시 서버 노드를 5대, RDBMS 노드를 1대를 사용하고 있는 상황에서 사용자 증가로 인해 서버 성능이 2배로 필요해진다면 RDBMS 노드는 1대만 더 추가하고 캐시 노드를 5대 더 증가시키는 방향으로 서버를 증설할 수 있습니다. 캐시를 쓰지 않았다면 RDBMS 서버만 10대씩 사용해야 하는 상황이 올 수도 있는데, 그러면 동기화 작업으로 인한 오버헤드 때문에 상당한 성능 저하를 겪거나, 아니면 비싼 돈을 들여 이 문제를 해결하기 위한 또 다른 방법(전문가 영입, 고급 SW 사용 등..)을 찾아야 할 것입니다.

캐시의 멀티노드를 구성할 때 Shared Nothing으로 세팅하여 데이터를 1/n로 노드마다 나눠갖는 방법을 사용한다면 노드간의 동기화 작업이 전혀 필요없게 됩니다. RDBMS는 n배로 증설한다고 해서 n배의 효과를 볼 수 없지만, 캐시 서버를 n배로 증설하면 n배의 효과를 볼 수도 있는 것이죠.

캐시는 이처럼 서버를 확장할 때 성능을 향상시킬 수 있는 여러모로 효율적인 기법이라고 할 수 있습니다.

1편의 마무리

종합적인 아키텍처

Load Balancer부터 멀티노드 서버 어플리케이션, 캐시 데이터베이스, Master-Slave 구조의 데이터베이스를 함께 쓰게 되면 아래의 아키텍처가 될 겁니다. 이 정도면 확장성도 있고, 성능도 좋다고 말할 수 있죠.

종합적 아키텍처

2편

이어서 “확장성 있는 서버 구축을 위한 네이버 오픈소스”“네이버 오픈소스를 활용하여 성능 개선을 위해 한 작업들”의 내용이 남아 있으나 이 포스트의 길이가 너무 길어져 2편에서 써보려고 합니다.

“확장성 있는 서버 구축을 위한 네이버 오픈소스”로는 Arcus, nBase-ARC, NGrinder를 다루며 각각 mecached기반의 캐시 클라우드, redis기반의 캐시 클라우드, 웹 기반 스트레스 테스트 툴로 네이버가 개발하고 오픈소스로 공개한 프로젝트인데, 이들을 정리 한 내용을 담으려 합니다.

“네이버 오픈소스를 활용하여 성능 개선을 위해 한 작업들”에는 “기본 서버 구성”, “Arcus/nBase-ARC로 데이터 조회를 빠르게 하기”, “멀티 노드 구성을 통해 트래픽 분산시키기”, “NGrinder를 이용하여 성능 측정하기”의 주제로 작성할 예정입니다.

감사합니다.