서버 다중화 환경에서의 세션 불일치
단일 서버 환경에서는 session을 통한 로그인을 구현할때 session 불일치 문제를 신경쓸 필요가 없다.
하지만 서비스가 커짐에 따라 한대의 서버로 운영하는것이 불가능해졌다고 가정해보자. 그래서 서버를 업그레이드 해야되는데 다음과 같이 두가지 방식이 존재한다.
- 첫번째 방법은 scale-up 방식이다.
- 서버 자체 성능을 늘려 부하를 견딜수 있게 하는 방식이지만, 여전히 서버 한 대에 모든 트래픽이 집중되므로 만일에 서버 장애가 생길시 서버가 복구될 때까지 서비스를 중단해야 하는 상황이 발생할 수 있는 위험이 있다. 사용하려던 서비스가 중단된다면 엄청난 비즈니스 손실(수익 손실)이 생길 수 있다.
- 두번째 방법은 scale-out 방식이다.
- 서버를 여러대로 늘려서 각 서버에 로드밸런싱으로 트래픽을 분산하게 한다. 그래서 서버 한대에 장애가 생겨도 다른 서버는 살아있으니 서비스 문제가 생기지 않는다.
- 그러나 이때 서비스 이용에 발생하는 커다란 문제점이 있는데 바로 데이터 정합성, 세션 불일치 문제다.
- 왜냐하면 여러 대의 서버가 각각 세션 저장소를 독립적으로 갖기 때문에 데이터 불일치 문제가 발생하기 때문이다.
※ 참고
Scale-Up : 서버의 스펙을 늘리는 방식. (서버의 메모리카드나 cpu 부품을 업그레이드하여 부하를 최소화)
Scale-Out : 서버의 갯수를 늘리는 방식. (여러대의 서버를 두어 부하를 분산)
※ 참고
로드밸런싱 이란?
여러 대의 서버를 두고 트래픽을 분산처리하여 서버의 로드율 증가, 부하량, 속도저하 등을 해결해주는 서비스.
팀의 업무를 팀장이 팀원들에게 배분하는것과 같은 이치이다.
이해하기 쉽게 그림으로 예시를 들어보자.
1. 서버를 3대를 증강시키고 로드밸런서로 각 서버로 트래픽 분산이 되게 운영중이다.
2. 이때 사용자가 서버1에 접속해 로그인을 진행해 서버1에 세션이 저장된다
3. 로그인 한후 게시판에 글을 쓰기 위해 서버에 요청했는데, 로드밸런싱 알고리즘으로 인해 서버1이 아닌 서버2로 요청되어진다.
4. 하지만 서버2에는 사용자의 로그인 세션이 없기때문에 또 로그인하라는 응답을 보내게 된다. (방금 로그인 했는데?)
이 처럼, 서비스 안정을 위해 서버를 늘려놨더니 서비스 품질 자체가 떨어지는 결과를 맞이해 버렸다.
이러한 세션의 데이터 정합성 문제를 해결하기 위한 방법에는 무엇이 있는지 살펴보자.
※ 참고
데이터 정합성 (data consistency) 이란?
어떤 데이터들의 값이 서로 일치함
세션 객체는 어떤 형태로 이루어져 있을까 ?
세션 객체는 Key에 해당하는 SESSION ID와 이에 대응하는 Value로 구성되어 있다.Value에는 세션 생성 시간, 마지막 접근 시간 및 User가 저장한 속성 등 이 Map 형태로 저장된다.
세션의 불일치 해결 방법
Sticky Session 방식
간단하게 생각해보면 로드밸런서가 랜덤으로 아무 서버에게 요청을 보내서 발생된 문제이니까, 만일 클라이언트의 요청이 어느 한 서버에 도달해 세션 데이터가 생겼다면, 앞으로 이 서버는 해당 클라이언트만의 요청/응답만 처리하도록 고정해주면 된다.
Sticky Session이란 말 그대로 고정된 세션을 의미한다.
Sticky란 번역하면 끈적임, 껌딱지 라는 뜻이다. 쉽게 말해서 해당 서버에 껌딱지처럼 붙어있다고 생각하면 된다.
다음은 sticky session 방식으로 어떻게 로드 밸런서가 클라이언트와 서버의 연결을 지속시킬수 있는지의 과정이다.
- 클라이언트는 서버에게 처음 요청을 전달한다.
그러면 로드밸런서는 서버들 중 하나에게 요청을 보내 처리한다. - 서버에서 클라이언트에 응답을 보낼 때, Set Cookie : SERVERID=서버1 이런 형태로 정보를 쿠키에 담아 보낸다.
쉽게 말해 너를 담당한 서버 정보를 클라이언트의 쿠키에 저장하는 것이다. - 이후, 클라이언트가 다시 서버에 요청을 보낼때 Cookie : SERVERID=서버1 을 함께 보낸다
그러면 로드 밸런서가 우선적으로 요청에 쿠키 정보가 있는지 부터 확인하고, 쿠키의 정보를 확인했다면 해당 요청은 해당 쿠키가 생성되어 있는 서버로 보내지게 된다. - 만약 존재하지 않는 쿠키라면, 로드 밸런서의 알고리즘에 의해 선정된 다른 서버에 쿠키가 생성되어 다음에 똑같은 요청이 오면 같은 경로로 맵핑시켜 줄 수 있도록 한다.
이렇게 동일한 사용자가 세션이 있는 해당 서버에 계속 요청을 보낼 수 있도록, 지속적으로 서버 정보가 쿠키를 통해 응답에 삽입되어 보내지게 되어, 클라이언트와 서버가 서로 연결을 유지할수 있는 것이다.
이러한 방식을 사용하면 유저는 세션에 대한 데이터 불일치 문제에서 자유로워질 수 있게 된다.
※ 참고
대표적으로 AWS ELB(Elastic Load Balancer)는 Sticky session을 활용한 서비스를 제공한다.
Sticky Session 문제점
- 특정 서버에 트래픽이 집중되는 문제
고정된 세션을 사용한다는 말은 사용자가 접속해야 하는 서버가 고정되어 있기 때문에 하나의 서버에 트래픽이 집중될 수 있다는 위험성을 갖고 있다는 말이다.
즉, 본래 목적인 로드 밸런싱이 제대로 이뤄지지 않을 수 있다. - 세션 정보의 유실
서비스 중에 만일 하나의 서버에 장애가 발생하게 되면 해당 서버를 사용하는 사용자들은 세션 정보를 모두 잃게 된다.
이렇게 되면 다른 서버에서 세션 인증을 다시 해야 하는 문제가 생기게된다.
이처럼 Sticky session에서는 사용자와 세션 정보를 갖고 있는 서버를 1:1로 매핑해주어 세션 불일치를 해결해지만, 문제가 발생하면 Scale out의 장점인 트래픽 분산과 가용성을 제대로 활용하지 못하게 되는 경우가 발생할 수 있게 된다.
Session Clustering 방식
앞서 sticky 세션은 각 서버에 세션을 저장해놓았더니 세션 불일치는 해결되었지만 도리어 성능이 안좋아진다는 결과를 낳았다.
그러면 세션 정보를 각 서버마다 저장하는게 아닌 세션 데이터를 복사해 서버들에게 전파해 가져다 쓸수 있으면 되지 않을까?
세션 클러스터링은 서버들을 하나의 클러스터로 묶어 관리하고, 클러스터 내의 서버들이 세션을 공유할 수 있도록 하는 방식이다.
예를들어 서버1에서 login session이 저장되었다면, 서버2와 서버3에도 서버1에 저장되어있는 세션을 전파(복사)하는 것이다.
WAS마다 session clustering을 지원하는 방식이 조금씩 다른데,
그중 가장 대표적 WAS인 톰캣(Tomcat) 에서는 어떻게 Session Clustering을 구현하고 있는지 알아보자.
Tomcat Session Clustering
Tomcat에서는 크게 두 가지 방식으로 Session Clustering을 구현 기능을 제공한다.
1. All-to-All Session Replication
All-to-All 방식은 하나의 세션 클러스터 내에서 데이터가 변경되면 변경된 사항이 다른 모든 서버로 복제 되는 방식으로, 톰캣에서 제공하는 DeltaManager 클래스를 통해 구현된다.
특정 서버에 생성된 세션을 클러스터를 이루는 모든 서버에 세션을 복제하기 때문에 클라이언트의 요청을 한 곳으로 지정하지 않아도 되고 다른 서버로 요청을 보내더라도 같은 세션을 유지할 수 있다.
만일 이용하고 있는 서버에 장애가 발생해도 다른 서버에서 세션을 유지하고 있기 때문에 클라이언트는 동일한 서비스 환경을 제공받을 수 있게 된다.
하지만 All-to-All 방식에서는 모든 서버가 전체 세션 데이터를 유지하고 있기 때문에, 다른 서버에서 세션을 찾기 위한 추가적인 네트워트 I/O가 발생하진 않지만, 그만큼 많은 메모리가 필요하다는 단점이 있다.
그리고 세션을 저장할 때 서버 수 만큼 복제하고 각 서버에 전달, 저장해야하기 때문에 서버 수에 비례하여 네트워크 트래픽이 증가하게 되기도 한다.
추가적으로 세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로 인한 세션 불일치 문제와 같은 예상치 못한 문제가 발생할 가능성이 존재한다.
※ 참고
톰캣 공식 문서에서도 All-to-All 방식은 소규모 클러스터 환경(노드가 4개 미만)에서 좋고, 이보다 큰 클러스터 환경에서는 추천하지 않는다고 한다.
그리고 DeltaManager를 사용한 방식은 애플리케이션이 배포되지 않은 노드에도 복제를 시도하기 때문에 불필요한 트래픽을 서버에 발생시키는 문제도 존재한다.
Tomcat에서는 이러한 문제를 해결하기 위해 다음으로 소개할 BackupManager를 이용한 방법도 제공해준다.
2. Primary-Secondary Session Replication
Primay-Secondary 방식은 Primary 서버의 세션 데이터를 Secondary(Backup) 서버에만 전체 복제하여 저장하는 방식으로, BackupManager 클래스를 통해 이 방식을 제공하고 있다.
All-to-All Session Replication 방식은 그냥 무식하게 모든 서버에 복제하고 저장하는 것과 달리, Primary 서버와 Secondary(Backup) 서버에만 전체 세션을 복제하여 저장하되, 나머지 이외의 서버들에는 세션의 Key에 해당하는 JSESSIONID만 복제, 저장함으로써 메모리를 절약할 수 있는 방식이다.
하지만 만일 Primary, Secondary 서버를 제외한 다른 서버에 세션 정보를 요청할 경우 다시 온전한 세션 정보를 얻기 위해서는 Primary, Secondary에 다시 요청을 보내야한다는, 세션 복제를 위한 과정이 수행되는 문제점이 존재한다.
※ 참고
Primary-Secondary Session Replication 방식은 비교적 대규모 클러스터 환경에서 적합한 방식이라고 한다.
Session Clustering 문제점
위에서 톰캣 세션 클러스터링을 설명하면서 간간히 문제점에 대해 언급했지만 취합하여 정리하자면 다음과 같다.
- 서버 세팅의 어려움
이 방식은 scale out 관점에서 새로운 서버가 하나 뜰 때마다 기존에 존재하던 WAS에 새로운 서버의 IP/Port를 입력해서 클러스터링 해줘야 하는 불편함이 있다. - 추가 메모리 비용
서버마다 동일한 세션 정보를 가지고 있어야 되기 때문에, 서버가 확장될 수록 복제해야 할 세션 데이터가 늘어나고 이는 추가적인 오버헤드로 이어진다.
Tomcat을 예로 들었을 때, 모든 데이터를 각각의 Tomcat 노드에게 전달해야 해야 하고 배포하는 노드가 아닐 경우에도 복사를 진행하기 때문에 불필요하게 메모리를 차지된다.
즉, 효율적인 메모리 관리가 이루어지지 않는다. - 네트워크 트래픽 증가
데이터 변경이 발생할때 마다 세션을 전파(복사)하는 작업이 일어나기 때문에 네트워크 요청 트래픽이 증가하게 된다.
그리고 서버가 늘어날수록 이 트래픽은 더욱 심해질 것이다.
또한, 복사 뿐만 아니라 서버가 늘어남에 따라 세션을 저장하고 찾아오는 과정에서 추가적인 트래픽이 발생 할수도 있어 확장에 한계가 있다. - 시차로 인한 세션 불일치 발생
세션 전파 작업 중 모든 서버에 세션이 전파되기까지의 시간차로 인한 세션 불일치 문제와 같은 예상치 못한 문제가 발생할 가능성이 존재한다.
이처럼 세션 클러스터링은 sticky 세션의 문제점인 특정 서버에만 트래픽이 몰리는 문제를 해결할 수 있었다.
그러나 세션 클러스터링이나 sticky 세션이나 서버가 세션이라는 상태(데이터)를 가진다는 것은 변함이 없다는 특징이 있다
"서버가 상태(데이터)를 가진다"라는 의미는 Scale out 방식으로 확장을 했을 때 서버가 가지고 있는 데이터를 확장하는 서버에도 똑같이 맞춰줘야 한다는 뜻이다. 이는 곧 오버헤드로 이어진다.
정리하면, 세션 클러스터링은 정합성 이슈를 해결할 수 있지만, 성능적인 한계가 존재한다고 말 할 수 있다.
Session Storage 방식
그렇다면 위의 두 방식의 단점을 보완하여 다중 서버에서 세션을 공유할 수 있는 방법이 없을까?
모든 서버에 일일히 세션 메모리를 복제/저장하는 것이 낭비라면, 그럼 별도의 세션 저장소를 외부에서 생성하고 각 서버들이 가져와 사용하면 되지 않을까?
세션 스토리지는 기존의 서버 내 세션 저장소를 이용하지 않고, 로컬 서버에서 분리해 별도의 세션 저장소를 두고 서버들이 이를 공유함으로써 세션 불일치를 해결하는 방식이다.
따라서 새로운 서버를 추가하더라도 추가한 서버에만 세션 저장소 정보를 명시해주기만 하면 되기 때문에 기존 서버의 수정이 발생하지 않는다는 장점이 있게 된다.
그래서 세션을 저장할 때 세션을 복제해 다른 서버들에 보낼 필요가 없어 WAS들끼리 불필요한 네트워크 통신 과정을 진행하지 않아도 되어 성능면에서도 유리하다.
또한, 한 서버에 장애가 발생하더라도 세션은 이와 독립되어 별도로 존재하기 때문에 세션을 활용한 서비스에 영향을 미치지 않는다.
Session Storage 종류
세션 Storage의 종류에는 두가지가 있다.
먼저 첫번째로 Disk방식으로 되었는 데이터베이스가 있고 두번째로 In-Memory 기반의 데이터베이스가 있다.
해당 방법으로 세션의 정합성 문제를 해결할때 세션 저장소로 어떤것을 사용할지 고민해보고, 그에 따른 타당한 이유가 뒷받침 되어야 한다.
1. Disk Database (MySql, Oracle)
말 그대로 세션 데이터를 디스크에 저장하는 것이다.
따라서 전원이 공급이 안되도 디스크에는 정보를 잃지 않고 잘 유지한다.
하지만 큰 단점이 하나 있는데 속도가 너무 느리다는 것이 맹점이다.
2. In-Memory (Redis / Memchached)
In-Memory는 데이터를 메모리에 저장하는 방식다.
그래서 I/O속도가 디스크와 비교해서 매우 빠르다.
하지만 이것또한 단점이 있는데 전원이 공급되지 않으면 기억하고 있는 데이터를 모두 잃어버리게 된다.
두가지 방식중에 세션 데이터를 저장한다는 관점에서 어느것이 좋을까?
생각해보면 Session은 영구적인 저장이 필요하지 않는다.
예로, 개인정보가 더 중요한 요즘은 지정한 시간을 넘기면 자동으로 로그아웃되게 처리 되어 있다.
그렇기 때문에 세션 정보는 영구적인 정보 저장을 약속한다는 디스크 방식의 데이터베이스는 매력적이지 못하다고 볼 수 있다.
대신에 디스크에 비해 압도적을 입출력 속도가 빠른 In-Memory 방식이 더 적합하다.
Session Storage 문제점
- 문제가 생기면 모든 서버가 장애
세션 클러스터 같은 경우 하나의 서버에 장애가 생겨도 나머지 서버에 미리 복제를 해두었기 때문에 문제는 없다.
그러나 세션을 저장하고 있는 Session storage 자체에 장애가 발생할 경우 모든 세션을 잃어버려 세션을 사용하는 모든 서버에 영향이 끼치는 위험이 있다.
그래서 이러한 문제를 보완하기 위해 동일한 세션 저장소를 하나 더 구성하는 방법(마스터-슬레이브 복제)으로 해당 문제를 해결하곤 한다. - 성능적인 마이너스
별도의 Session storage로부터 세션을 불러와야 하기 때문에 추가적인 네트워트 I/O가 발생한다는 점이다.
세션을 외부에서 가져와 사용하기 때문에 로컬 메모리에 저장해 사용하는 것보다 성능적인 면에서 떨어질 수 밖에 없다.
참고
- https://liasn.tistory.com/3
- https://1-7171771.tistory.com/126
- https://d2.naver.com/helloworld/284659?source=post_page-----f1fd4ceecbc7----------------------
- https://hyuntaeknote.tistory.com/6