저번 글에서 서버의 확장은 어떤 것들이 있는지 알아봤습니다.
Scale out 확장을 선택하면 데이터 정합성 문제를 반드시 해결해야 합니다. 그렇다면 실제 프로젝트에선 어떤 식으로 발생할까요? 저의 웹 서버 애플리케이션은 로그인에 대한 인증 기능을 세션 기반으로 구현했습니다. 이때, Scale out을 통해 여러 대의 서버로 요청을 처리하도록 분산 서버 환경을 만들면 각 서버 별로 로그인 세션 정보가 다른 세션 불일치 문제가 발생합니다.
위 사진과 같이 어느 한 서버로 요청을 보내 서버에 세션 정보를 생성을 하면 생성된 세션 정보는 해당 서버를 제외한 다른 서버에서는 모르기 때문입니다.
세션 정보를 모든 서버가 공유하거나
클라이언트 요청을 세션 정보를 가지고 있는 서버에만 보내면 되는 거 아닌가요?
맞습니다. 클라이언트 요청을 한 서버에만 보내도록 고정하는 방법을 Sticky Session, 세션 정보를 모든 서버에 공유하는 방법을 Session Clustering이라 합니다.
Sticky Session
로드 밸런서가 동일한 클라이언트의 요청을 세션이 생성된 서버로만 보내도록 하는 방법입니다.
로드 밸런서는 서버 계층의 앞단에 존재하며 서버로 들어오는 모든 요청이 로드 밸런서를 거쳐가게 됩니다. 거쳐가는 과정에 클라이언트 요청의 쿠키 값이나 IP 세부 정보를 기반으로 동일한 클라이언트 인지 판단하고 세션이 생성된 서버로 다시 라우팅 되면서 클라이언트는 동일한 세션을 제공받을 수 있는 환경이 만들어집니다.
장점은 서버들끼리 세션의 정합성을 맞출 필요가 없습니다. 만약 세션 정합성을 맞추기 위해 어떠한 작업이 일어난다면 그 작업 또한 내부적으로 트래픽을 발생시킬 수 있습니다.
단점으로는 로드 밸런서가 클라이언트의 쿠키나 IP에 의해 트래픽을 처리하기 때문에 특정 조건에 의해 트래픽이 한 곳으로 몰릴 수 있고 그러면 Scale out의 장점인 서버의 가용성을 최대한 활용 못 하는 상황이 발생합니다. 또 서버에 장애가 발생 시 해당 서버의 세션 정보가 사라져 클라이언트는 다른 서버에서 세션 인증을 다시 해야 하는 문제가 생깁니다.
Session Clustering
Session Clustering은 대표적 WAS인 Tomcat에서 제공해주는 방법을 이용할 수도 있고 Session을 저장할 때 서버가 아닌 외부 리소스에 저장시켜 공유시키는 방법도 있습니다. 먼저 Tomcat에서 제공해주는 방법을 알아보겠습니다.
All-to-all Session Replication
Tomcat에서 제공하는 DeltaManager가 생성된 세션을 모든 서버에 복제를 해주는 방법입니다.
특정 서버에 생성된 세션을 클러스터를 이루는 모든 서버에 세션을 복제하기 때문에 클라이언트의 요청을 한 곳으로 지정하지 않아도 되고 다른 서버로 요청을 보내더라도 같은 세션을 유지할 수 있습니다. 이용하고 있는 서버에 장애가 발생해도 다른 서버에서 세션을 유지하고 있기 때문에 클라이언트는 동일한 서비스 환경을 제공받을 수 있습니다.
하지만 세션을 모든 서버에 복제하기 위한 트래픽이 발생하고 서버가 늘어날수록 하나의 서버가 감당해야 할 세션과 복제를 위한 트래픽이 엄청나게 증가한다는 단점이 존재합니다.
This works great for smaller clusters, but we don't recommend it for larger clusters
— more than 4 nodes or so.
Also, when using the DeltaManager, Tomcat will replicate sessions to all nodes,
even nodes that don't have the application deployed.
공식 문서에서도 소규모 클러스터 환경(노드가 4개 미만)에서 좋고 이보다 큰 클러스터 환경에서는 추천하지 않는다고 말합니다. 그리고 DeltaManager를 사용한 방식은 애플리케이션이 배포되지 않은 노드에도 복제를 시도하기 때문에 불필요한 트래픽을 서버에 발생시키는 문제도 존재하죠.
Tomcat에서는 이러한 문제를 해결하기 위해 BackupManager를 이용한 방법을 제공해줍니다.
Primary-secondary session replication
BackupManager는 세션을 모든 서버에 복제하지 않고 백업 서버에만 복제를 합니다.
클라이언트 요청이 들어오면 해당 요청을 처리한 Primary 서버에 세션을 생성하고 Secondary 서버(백업 서버)에만 완전한 세션 복제를 진행합니다. 나머지 Proxy 서버에는 Session Id와 Primary/Secondary 서버의 주소만 가지고 있고 만약 Proxy 서버에 클라이언트 요청이 들어오면 Primary 서버에 요청을 보내 완전한 세션 데이터를 복제합니다.
이 방법은 세션을 모든 서버에 복제하지 않기 때문에 저장해야 할 세션 정보의 양이 상대적으로 줄어든다는 장점이 있습니다.
단점은 Primary-Secondary 이외의 서버로 요청이 들어오면 여전히 완전한 세션 복제를 위한 과정이 수행된다는 점이 있습니다.
Tomcat은 세션 불일치 문제에 대해 여러 가지의 해결책을 제시하고 있습니다. 하지만 서버가 확장될수록 복제해야 할 서버가 늘어나고 추가적인 오버헤드가 커집니다. 또 위의 모든 방법들은 서버가 세션이라는 상태(데이터)를 가진다는 것은 변함이 없죠. "서버가 상태(데이터)를 가진다"라는 의미는 Scale out 방식으로 확장을 했을 때 서버가 가지고 있는 데이터를 확장하는 서버에도 똑같이 맞춰줘야 한다는 뜻입니다.
만약 서버가 상태(데이터)를 가지지 않는다면?
서버가 상태(데이터)를 가지지 않는 Stateless 서버라면 Scale out 확장을 해도 데이터(세션) 정합성을 맞춰 줄 필요가 없게 됩니다. 또 서버 장애 시 가지고 있는 데이터가 없어 정보의 손실을 신경 쓸 필요도 없게 되죠.
한마디로 Scale out의 장점을 극대화하면서 단점은 최소화할 수 있고 대용량 트래픽에 대응해 효율적으로 확장을 할 수 있다는 의미입니다.
Stateless 서버를 만들기 위해서는 로컬 서버에 저장되는 데이터가 없어야 하고 이를 구현하기 위해서는 세션 저장소를 서버가 아닌 외부로 분리를 해주는 게 좋습니다.
Session Storage 분리
Session 저장소를 로컬 서버에서 분리해 세션을 따로 저장하고 필요시 조회하는 방법입니다.
Scale out 확장을 하더라도 세션을 저장하는 곳이 외부에 있어 세션 불일치 문제가 발생하지 않습니다. 또 Sticky Session처럼 로드 밸런서를 통해 특정 트래픽을 분리할 필요도 없구요.
하지만 세션 저장소를 따로 분리한 만큼 서버의 관리 포인트가 더 증가하고 저장소 서버에 장애 발생 시 세션을 사용하는 모든 서버에 영향이 끼치는 위험이 있습니다. 그리고 세션을 외부에서 가져와 사용하기 때문에 로컬 메모리에 저장해 사용하는 것보다 성능적인 면에서 떨어질 수 있습니다.
여러 가지 방법이 있는데 정합성 문제를 해결하려면 어떤 것을 적용하는 게 좋을까요?
이전 글에서 말했듯이 어떤 것을 적용하는지에 대한 정답은 없지만 해답은 있습니다. 자신의 상황에 맞게 기술을 선택하고 합리적인 근거를 뒷받침할 수 있으면 해답이 될 수 있습니다. 저는 아래의 기준으로 Session Storage 분리를 선택하였습니다.
- 나의 서비스가 대규모로 성장한다고 가정한다.
- Stateless 서버가 가지는 장점을 활용한다.
- Scale out 확장 시 로그인 정보를 특정 서버만 가져 일관적인 인증 서비스를 제공 못하는 단점을 제거한다.
- WAS에 장애가 발생해도 다른 서버에서 세션을 사용하는 데에 지장이 없다.
- 서버가 늘어남에 따라 발생하는 오버헤드가 적고 트래픽이 균등하게 분배되지 못하는 현상을 해결할 수 있다.
'Database > Redis' 카테고리의 다른 글
Redis Session 병목 해결 및 최적화하기 (0) | 2021.06.03 |
---|---|
로컬 캐싱에서 글로벌 캐싱으로 변경한 이유 (0) | 2021.05.02 |