4 minute read

Scale Out

ESD(전자 소프트웨어 유통) 프로젝트의 트래픽 변화에 능동적으로 대응하기 위해 scale out을 사용하기로 선택했습니다. scale out은 동일한 사양의 여러 서버를 들여서 서버의 확장성을 확보하는 방법입니다.

scale out에 대해 더 알고 싶으신 분들은 이전 포스트에 들어가서 보시면 됩니다.

이번 포스트에선 서버가 여러 대 있는 상황에서 사용자 정보를 일시적으로 보관하기 위한 세션의 정합성 유지 방법을 찾는 과정을 담았습니다. 여기서 세션이란 무엇이고, 분산 서버 환경에서 어떤 문제가 있으며, 해결 방안 후보들에 대해 알아보겠습니다.


Session이란

현재 인터넷에서 사용되는 모든 웹 서비스에서 세션이 사용됩니다. 세션은 웹 상에서 애플리케이션 간에 일시적으로 상태를 유지하기 위해 사용되며 지정된 유지 기간이 지나면 자동 소멸됩니다.

Untitled

J2EE 애플리케이션 환경에서는 JSESSIONID를 사용하게 됩니다. JSESSIONID는 톰캣(Tomcat)이나 제티(Jetty) 같은 서블릿 컨테이너(was)가 세션을 식별하기 위해 발급하는 key입니다. 즉, 세션의 키(key)는 JSESSIONID가 되고 값(value)은 세션 아이디가 됩니다.

클라이언트가 요청을 보낼 때 was는 JSESSIONID가 있는지 확인한 다음, 없다면 세션 아이디를 생성해 쿠키에 넣어 Set-Cookie HTTP 응답 헤더에 넣어 클라이언트에게 응답합니다. 그러면 클라이언트는 다음 요청마다 요청 헤더에 받았던 쿠키를 넣어 보내고, 서버는 보내온 쿠키 내 세션 아이디를 메모리에 저장된 세션 아이디와 비교해 일치한다면 같은 유저임을 식별할 수 있습니다.


문제 : Session 불일치

문제는 Scale out 적용 시 하나 이상의 서버가 있는 환경에서 세션을 식별하지 못하는 세션 불일치가 생겨납니다. 클라이언트가 처음 요청을 보낸 서버의 메모리에 해당 클라이언트의 세션 아이디를 저장합니다. 만일 클라이언트가 다른 서버로 접근한다면 해당 서버의 메모리엔 세션 아이디가 없기에 아예 새로운 유저라고 생각해 새로운 세션 아이디를 생성합니다. 결국 세션의 장점이자 사용 이유인 정보의 동일성을 잃어버리게 됩니다.


사용 가능한 옵션

세션 정합성 문제를 해결하기 위해 찾은 3가지 옵션인 세션 클러스터링(session clustering), 스티키 세션(sticky session), 레디스(reids)를 살펴보도록 하겠습니다. 참고로 제 프로젝트는 스프링 부트를 사용하기에 톰캣 위주로 자료를 찾았다는 점 감안하고 읽어주시기 바랍니다.

1. 세션 클러스터링(clustering)/레플리케이션(replication)

세션 클러스터링 또는 세션 레플리케이션은 여러 인스턴스로 묶인 동일한 클러스터 내에서 세션에 저장된 데이터를 복사해 다른 인스턴스에서도 공유되도록 하는 기술입니다. 여러 서버에 하나씩 실행되는 was를 하나로 묶고 모든 서버가 데이터를 복제함으로써 하나의 서버처럼 동작할 수 있게 해줍니다.

제 프로젝트에서 사용하는 톰캣 9버전에서 설명된 세션 클러스터링의 경우 DeltaManager를 이용해 all-to-all 세션 레플리케이션이 가능하다 써있습니다. All-to-all은 클러스터 내에 있는 모든 노드에 세션을 복사할 수 있습니다. 즉, 여러 was가 세션을 공유하면서 하나의 was가 다운되도 다른 was로 세션을 유지할 수 있습니다.

하지만 클러스터가 커질 수록 네트워크 트래픽 부하와 성능 저하가 일어나게 되는 단점이 있습니다. 클러스터로 묶인 인스턴스 개수와 오버헤드 수치도 같이 올라가는 양의 상관관계를 보이기 때문입니다. 이를 보완하기 위해 응용 프로그램에 배포된 하나의 백업 노드에만 세션 정보를 복제하는 BackupManager 기능을 지원합니다. 그리고 과부하를 막기 위해 cluster를 작은 단위로 쪼개서 관리하는 방법이 있습니다.


2. 스티키 세션(sticky session)

스티키 세션은 로드 밸런싱을 통해 하나의 세션이 지정된 서버에만 접속하도록 세션 퍼시스턴스(session persistence)를 주는 방법입니다. 서버끼리 세션 데이터를 교환하지 않아도 되서 시스템의 속도를 향상시킬 수 있습니다.

여기서 로드 밸런싱이란 여러 서버에 걸친 네트워크 트래픽 분산 프로세스로 트래픽을 균등하게 분산해 한 서버의 과부하를 방지하고 애플리케이션 응답률을 상승시킬 수 있습니다. 로드 밸런싱엔 적용할 수 있는 여러 알고리즘(least connection method, least response time method, round robin 등)이 있습니다.

로드 밸런싱은 OSI 7계층에서 이뤄지는데 주로 사용되는 계층은 L4(transport 레이어)와 L7(application 레이어)입니다. L4에서는 패킷 헤더에 저장된 IP 주소와 TCP/UDP 포트 정보를 기반으로 어떻게 요청을 여러 서버에 보낼지 결정합니다. TCP 스트림의 첫 몇 개의 패킷에 있는 주소 정보만을 보며 패킷 내 정보는 체크하지 않습니다. 과정이 단순하고 정보를 많이 수집하지 않아서 예전에 많이 사용됐습니다.

L7은 HTTP 헤더, 패킷 내 정보, 트래픽 내용, 쿠키 정보 등을 기반으로 요청 배분 방법을 정합니다. L4보다 확연히 많은 정보를 분석하기에 비용이 상승하는 대신 전달하는 데이터를 완전히 이해해 더 정확하고 세심하게 배분할 수 있는 장점이 있습니다. 이미지, 동영상 같은 정적 컨텐츠, 실시간 동적 컨텐츠, 트랜잭션 정보를 구분해 해당 업무에 최적화된 애플리케이션 서버에 보내는 방법으로 전체 서비스의 속도와 수행 능력을 확연히 올릴 수 있습니다. 또한 현재 하드웨어의 연산 능력으로 L7 로드 밸런싱을 충분히 소화할 수 있기에 많이 사용되고 있습니다.

하지만 특정 서버에만 세션이 몰릴 경우 서버 과부하가 올 수 있습니다. DNS로 클라이언트 캐싱을 하면서 외부 IP 하나를 서버 A에 매칭시켰다고 가정합니다. 겉으로 보이는 IP는 하나지만 라우터로 연결된 사설망에 많은 수의 사설 IP가 연결될 수 있습니다. 그럼 하나의 서버에 사설 IP 개수만큼의 이용자가 붙게 되면서 최악의 경우 서버가 다운되면서 서버에 있던 모든 세션 데이터를 잃어버릴 수 있습니다. 로드 밸런서가 중간에 세션을 다른 서버로 옮기는 것도 가능하지만, 그 과정에서도 데이터 손실이 발생합니다.


3. 레디스 (Redis, Remote Dictionary Server)

레디스는 인메모리 오픈소스 데이터 저장소로 데이터베이스, 캐시, 메시지 브로커 용도로 사용됩니다. 메모리를 이용하기에 데이터 I/O 처리 속도가 빠르며 key-value 형태로 데이터를 저장하며 String뿐만 아니라 리스트, 해시, 스트림 등 다양한 자료형을 담을 수 있는 장점이 있습니다.

레디스를 여러 서버와 DB 사이에 두면 레디스에서 세션 정보를 저장해 모든 서버가 공유할 수 있습니다. 또한 서버가 늘어나거나 다운되더라도 다른 서버가 레디스 저장소 내 키값을 가져와 세션 아이디를 확인할 수 있습니다. 같은 요청의 경우에는 DB가 아닌 레디스에 요청을 해서 DB 부하 완화와 속도 향상을 볼 수 있습니다.


선택 : 레디스

변칙적인 트래픽에 유연하고 신속하게 대처할 수 있는 레디스를 사용하기로 했습니다. aws에서 소개한 바로 실시간 애플리케이션에 초당 수 백만 건의 요청을 지원하고 속도와 실시간 응답이 중요한 게임, 채팅/메시징, 스트리밍 등 사업에서 많이 사용된다 합니다. DB가 상주한 디스크로 왕복을 해야 하는 세션 클러스터링과 스티키 세션과 다르게 레디스는 주 메모리에서 간단한 쿼리문으로 더 많은 작업을 빠르게 처리 가능합니다.


마치며

Redis 세션을 적용한 프로젝트는 여기에서 확인해볼 수 있습니다.
긴 글 읽어주셔서 감사합니다. 여러분 모두 화이팅입니다.

image


출처

세션 관련
세션
자바의 세션 관리법
set-cookie

세션 클러스터링 관련
Apache Tomcat 9

sticky session 관련
Session Stickiness
Load Balancing 101
nginx L4
nginx L7

레디스 관련
redis persistence
Amazon redis

Categories:

Updated:

Comments