티스토리 뷰

2. 여러 대의 서버에 흩어져 있는 세션을 어떻게 관리할 수 있을까?

저번 글에서는 서버의 성능을 향상시킬 수 있는 두 가지 방법, Scale Up과 Scale Out에 대해 알아보았다. 각기 다른 장단

점이 있었지만 여러가지 측면에서 내가 진행하고 있는 SNS 서비스 구현 프로젝트에 더 적합한 방법은 Scale Out이었다.

Scale Out을 선택하면 로드밸런싱을 통해 부하를 여러 대의 서버로 분산 처리를 해야만 한다. 이 과정에서 고민거리가 하

나 더 뒤따른다. 바로 데이터 불일치다.

 

분산 서버 환경에서 발생하는 데이터 불일치

대부분의 웹 서비스는 stateful한 서비스다. 서버에 저장된 사용자의 세션 정보를 기반으로 사용자 맞춤 서비스를 제공할

수 있기 때문이다. 그러나 분산 서버 환경에서 이를 구현하기란 쉽지 않다. 각 서버마다 있는 세션 저장소에 저장되어 있는

세션 정보들이 다른 서버와 공유되지 않기 때문이다.

 

가령, 사용자가 서비스를 사용하기 위해 로그인을 했다고 생각해보자. 사용자가 로그인을 요청하면 서버에서는 해당 사용

자가 처음 서비스에 접속했을 때 생성된 세션에 사용자의 로그인 정보를 저장한다. 그리고 로그인 정보가 저장된 세션의

sessionid와 함께 로그인이 성공했다는 응답을 사용자에게 전달한다. 사용자는 이후 요청부터 요청 헤더에 서버로부터 받

은 sessionid를 넣어서 서버에 보내고, 서버는 전달된 sessionid를 통해 사용자를 식별해서 그에 맞는 서비스를 제공한다.

 

그런데 분산 서버의 환경의 경우, 같은 사용자가 보낸 요청이 매번 같은 서버로 들어간다는 것이 보장되지 않는다. 서버1

에서 로그인한 사용자가 게시글 작성 요청을 보냈는데 이번에는 서버3으로 들어간다고 하자. 그럼 사용자가 현재 갖고있

는 sessionid를 전달해도 해당 sessionid는 서버1에 저장되어 있는 것이기 때문에 서버3에서 조회해도 세션이 존재하지 않

으므로 다시 로그인을 해야되는 상황이 발생한다. 운이 나쁘면 이 사용자는 요청을 보낼 때마다 로그인을 다시 해야될 수

도 있다. 이 문제를 해결하기 위해서는 흩어져 있는 세션 정보를 효율적으로 관리하는 기술이 필요하다.

 

Sticky Session

먼저, Sticky Session 방법이 있다. Sticky Session이란 사용자의 세션을 처음 생성된 서버에 바인딩하여 이후 동일한 사용

자로부터 들어오는 모든 요청을 동일한 서버로 보내는 것이다. 말 그대로 하나의 서버에만 붙어있는 세션이다.

 

 

위의 그림은 로드 밸런서에 의해 user1은 로그인 요청이 Server1로 보내져 Server1에 세션 정보가 저장되어 있고, user2

는 로그인 요청이 Server3로 보내져 Server3에 세션 정보가 저장되어 있는 상태다. 이 상태에서 user1과 user2가 각각 다

른 요청들을 보내면 로드 밸런서는 요청을 보낸 사용자의 IP주소나 쿠키로부터 어떤 서버에 고정되어 있는지 확인한 후,

해당 요청을 지정된 서버로 보낸다. 따라서, 사용자는 세션이 유지되는 동안에는 같은 서버를 통해 서비스를 이용할 수 있

기 때문에 데이터 불일치가 발생하지 않는다.

 

데이터 정합성 문제를 해결했지만 이 방식은 다음과 같은 단점을 가진다.

  • 하나의 서버에 트래픽이 몰릴 수 있다.

    : 로드 밸런서는 사용자들이 처음 요청을 보낼 때 서버 간 균형을 맞춰 적절하게 분배를 했을 것이다. 하지만 이후 사

    용자들의 활동은 천차만별일 것이다. 어떤 사람은 로그인만 하고 서비스를 이용하지 않을 수도 있고, 어떤 사람은 그

    누구보다도 적극적으로 서비스를 이용할 수도 있다. 문제는 여기서 발생한다. 원래 로드 밸런싱을 통해 많은 트래픽이

    동일하게 나누어져야 하는데 Sticky Session을 사용하면 현재 각 서버들이 분담하고 있는 작업량과는 상관없이 지정

    된 서버로만 보내야 하기 때문에 하나의 서버에만 많은 트래픽이 분배되어 과부하가 발생할 수 있다.

  • 세션 정보가 사라질 수 있다.

    : 서버의 유지보수를 위해 의도적으로 서버를 종료하든, 예기치 않은 문제로 장애가 발생하여 서버가 다운되든 서버가

    멈추는 경우는 다양하다. 어느 경우이던 간에 서버가 종료되면 로드 밸런서는 트래픽을 해당 서버를 제외하고 나머지

    서버로 보낸다. 그럼 종료된 서버에 저장된 세션 정보는 사라지고, 해당 서버에 세션 정보가 있던 사용자는 1번에서

    언급했던 문제 상황과 같은 상황에 놓이게 된다.

 

Session Clustering

'클러스터'라는 단어는 여러 분야에서 다양한 의미로 사용되고 있다. 정확히 지칭하는 바는 다 다르지만 보통 '클러스터'라

하면 어떤 단위를 기준으로 나눠진 집합이나 그룹이라는 뜻을 내포하고 있다. 이 단어가 컴퓨터 분야에서 쓰이면 하나로

연결된 여러 컴퓨터들의 집합을 의미한다. 결국, 클러스터링이라는 것은 이러한 클러스터, 즉 여러 대의 컴퓨터 혹은 서버

들이 네트워크로 연결되어 마치 하나의 시스템처럼 동작하는 것을 말한다. 세션 클러스터링도 마찬가지로 여러 서버에 흩

어져 있는 세션들을 하나의 그룹으로 묶어 동일한 세션으로 세션 관리를 하는 것이다. WAS에 따라서 세션 클러스터링 방

식이 다르다. 그 중에서도 Spring Boot의 내장 WAS에 해당하는 Tomcat의 세션 클러스터링 방식을 위주로 살펴보자.

 

Tomcat의 세션 클러스터링에 관한 공식문서에 들어가면 다음 문장들이 쓰여져 있다.

Using the above configuration will enable all-to-all session replication using the DeltaManager to replicate session deltas. By all-to-all, we mean that every session gets replicated to all the other nodes in the cluster.

즉, Tomcat에서는 all-to-all 세션 복제 방식을 사용하고 있으며, 이는 각 서버에서 생성된 모든 세션들을 클러스터로 묶인

모든 노드에 복제한다는 것을 의미한다.

 

 

예를 들어, 위와 같이 user1은 server1에, user2는 server2에서 로그인을 하고 이에 대한 세션 데이터가 각 서버에 생성되

었다고 가정하자.

 

 

이렇게 새로 세션이 생성되었거나 세션 정보가 변경될 때마다 Tomcat에서 세션을 제어하고 세션 복제를 처리하는

DeltaManager가 클러스터로 묶인 다른 모든 서버에 이와 동일한 세션 데이터를 복제한다.

 

 

그럼 로그인을 했던 사용자들의 요청이 처음 로그인을 했던 서버가 아닌 다른 서버로 보내져도 모든 서버에 각 사용자들

과 매핑되는 세션 데이터를 가지고 있기 때문에 로그인을 다시 할 필요없이 계속해서 서비스를 이용할 수 있다.

하지만 위와 같은 all-to-all 방식은 소규모 클러스터에는 적합하지만 서버가 4대 이상인 대규모 클러스터에는 적합하지 않

다. 데이터가 변경될 때마다 세션 복제가 일어나기 때문에 서버의 수가 늘어날수록 메모리를 많이 사용하고 네트워크 트

래픽도 증가하기 때문이다. 또한, DeltaManager는 클러스터에 묶인 모든 서버에 위와 같은 작업을 하기 때문에 웹 애플리

케이션이 운영되고 있지 않은 서버에도 세션 데이터를 복제한다는 문제점도 가지고 있다.

 

그래서 Tomcat은 이러한 단점을 보완하기 위해 BackupManager라는 관리자를 제공한다. BackupManager은 응용 프로그

램이 배포된 하나의 백업 서버에만 세션 데이터를 복제하고 나머지 서버에는 Session ID 값만 복제하여 전달한다.

그러나 세션 복제 방식 자체에는 피할 수 없는 단점이 있다. 서비스를 이용하는 사용자가 늘어날수록 세션을 복제하는 횟

수와 복제해야 하는 세션 데이터의 양이 증가하기 때문에 이에 대한 과부하가 발생할 수도 있다는 것이다. 아무리

BackupManager를 사용하여 세션을 복제하는 서버의 수를 줄였다고는 하나 Session ID 값만 전달받는 서버의 경우, 결국

Session ID 값이 저장되어 있는 서버에 요청해서 온전한 세션 데이터를 받아와야 하기 때문에 이에 대한 트래픽이 발생할

수 밖에 없다.

 

 

가장 먼저 알아봤던 Sticky Session은 특정 서버에만 사용자 트래픽이 증가하여 과부하가 생기거나 어떠한 이유로든 서버

가 종료될 시, 해당 서버에 저장된 세션 정보가 없어진다는 단점이 있다. 이는 Scale Out을 선택하여 얻고자 했던 트래픽

분산이라는 이점을 잘 살릴 수 없는 방법이다. 또한 Sticky Session의 단점을 보완한 세션 클러스터링도 세션에 대한 변경

이 일어날 때마다 세션을 복제해야 하기 때문에 사용자 수가 늘어난다면 성능적인 면에서 한계점이 생기게 된다. 그렇다

면 어떤 방법이 Scale Out에 적합할까? 바로 세션 스토리지를 분리하는 것이다.

 

세션 스토리지 분리

지금까지 살펴본 방식들은 모두 각 서버의 메모리에 세션을 저장했었다. 이와 달리 세션 스토리지를 분리한다는 것은 아

래 그림과 같이 세션을 저장할 새로운 저장소를 따로 두고 이곳에서 세션 정보를 공유하는 것이다.

 

 

세션 스토리지 분리 방식을 사용하면 각 서버는 외부 스토리지에 대한 정보만 알고 있으면 되기 때문에 서버를 추가하거

나 삭제하는 것이 쉬워진다. Sticky Session처럼 세션이 처음 생성된 서버에 따라 트래픽이 분배되지 않으므로 특정 서버

만 트래픽이 몰려 과부하가 생기는 상황은 거의 일어나지 않는다. 또한 세션을 복제하지 않고 서버와 분리된 하나의 저장

소에서 모든 서버의 세션을 공유하기 때문에 데이터 정합성 이슈는 물론 톰켓 세션 클러스터링 방식의 한계점도 해결된

다. 그러나 하나의 저장소를 사용하여 세션을 관리하기 때문에 해당 저장소가 다운되면 모든 서버가 세션을 사용할 수 없

다. 그렇기 때문에 세션 저장소에 대한 클러스터링도 별도로 진행해야 한다.

 

 

세션 스토리지를 분리하기로 결정했다면 이제 어떤 세션 스토리지를 사용할 것인가에 대한 고민이 뒤따른다. 다음 글에서

는 세션 스토리지로 사용할 수 있는 데이터베이스의 종류를 알아보고 이에 대한 장단점을 비교해볼 것이다.

 

 


  • 진행 프로젝트

https://github.com/f-lab-edu/sns-itda

 

f-lab-edu/sns-itda

Contribute to f-lab-edu/sns-itda development by creating an account on GitHub.

github.com

 

 

  • 이어지는 글

1. 서버를 어떻게 확장시킬 것인가 - Scale Up VS Scale Out

 

분산 처리 환경에서 대용량 트래픽을 견디기 위한 로그인 기능 구현-1. 서버를 어떻게 확장시킬 �

1. 서버를 어떻게 확장시킬 것인가? - Scale Up VS Scale Out 만약에 내가 만든 이 서비스가 엄청 잘 돼서 사용자 수가 폭발적으로 늘어나고 이 많은 사람들이 동시에 접속한다면 서버 하나로 버틸 수 ��

chagokx2.tistory.com

 

댓글