본문 바로가기
학습기록

Scale-up을 적용한 무상태 웹 계층에서의 세션 불일치 문제

by seongju.lee 2023. 7. 5.

 

 

사용자가 늘어남에 따라 웹 계층은 수평적 규모 확장을 하게 된다.

하지만, HTTP는 무상태 프로토콜로 사용자가 어느 웹 서버로 접속하느냐에 따라서 세션 데이터가 불일치하는 문제가 발생할 수 있을 것이다. "이 문제에 대해 어떻게 해결해야 하나?"에 대해 알아보았다.
우선, 내가 고민한 상황을 그림으로 표현하면 아래와 같다.

  1. 사용자가 로그인 등의 작업을 수행하고, 인증을 요청한다.
  2. 로드밸런서를 통해 A서버로 인증 요청이 전달되고, A서버는 인증을 완료한 뒤 사용자의 세션 정보를 A서버에 저장한다.
  3. 이후, 사용자가 인증이 필요한 서비스를 접속하는데 B서버나 C서버로 전달되면???
    -> B, C에는 사용자의 세션 데이터가 없기 때문에 재인증을 해야 할 것이다.

이렇게 데이터 정합성 문제가 발생한다.

그럼 이러한 유형(세션 불일치)의 데이터 정합성 문제를 해결하기 위해선 어떻게 처리해야 할까?

세 가지를 알아볼 것이다. (sticky session, session clustering , 세션 저장소 분리)

 

1. 로드 밸런서의 sticky session(고정 세션)

사용자 수에 따른 규모 확장에서 정리했듯이 로드밸런서가 세션 불일치 문제를 해결하기 위해서 sticky session(고정 세션)이라는 기능을 지원한다.

sticky session(고정 세션)이란 말 그대로 사용자의 세션이 저장된 서버로 항상 사용자의 요청을 전달하는 것이다.

첫 번째 요청을 통해 특정 웹서버에 세션이 저장되면, 그 뒤에 이어지는 요청들에 대해서 모두 특정 웹 서버로 전달되게 하는 것이다.

위 사진에서는 clientA의 인증 요청이 웹서버 A로,
clientC의 인증 요청이 웹 서버 C로 전달되었으므로
다음 요청에는 각 클라리언트의 세션 데이터가 저장된 웹 서버로 요청이 전달된다.

즉, 첫 요청을 통해 세션이 저장된 서버에 고정으로 계속 요청이 전달되는 것이다.

이와 같은 sticky session을 유지하기 위해서 쿠키를 이용하거나 요청 IP를 tracking 하여 판단한다.

 

sticky session 단점

  • 로드 밸런서는 특정 서버의 부하를 줄이기 위해서 사용하지만, 세션 정보가 하나의 서버에만 존재한다면 특정 서버에 트래픽이 몰릴 가능성이 생기게 된다.
  • 특정 서버에 장애가 생기면 해당 서버가 가진 세션 데이터들이 모두 소실된다.

 

이처럼 sticky session은 결국 로드밸런서의 목적이 사라지는 방법이고, 위 단점들을 보완하기 위해 세션들을 하나로 묶어 관리할 수 있다.

 

 

2. session clustering

session clustering은 세션들을 하나의 클러스터로 묶어서 관리하는 방식이다.

각 웹서버에서 세션이 생성되면 클러스터 내에 있는 다른 웹 서버들에게 세션을 전파함으로써 세션 관리가 이루어진다.

Load Balancer 기능을 적절히 활용할 수 있게 되어 특정 서버의 부하는 막을 수 있게 된다.

또한, 특정 웹 서버에 장애가 발생해도 문제가 발생하지 않기에 가용성 측면에서도 적절한 방법이다.

하지만 이 방법 또한 여러 문제점을 가지고 있다.

 

sessiong clustering 단점

  • 세션을 전파하는 과정에서 서버 수에 비례하여 트래픽이 발생한다. 
  • 세션을 모든 서버가 복제하여 가지고 있기 때문에 매우 비효율적인 메모리 관리가 되고 있음을 알 수 있다.

 

(사실, 위 기능은 Tomcat에서 all-to-all 세션 복제 방식이라고 하며, 상대적으로 이보다 메모리를 효율적으로 사용하기 위해
primary-secondary 세션 복제 방식도 존재한다. 이는 세션 데이터 전체를 복제하지 않고 session Id만을 복제하는 방식이다.)

 

어쨌든, session clustering을 통해 정합성 문제는 해결할 수 있지만 성능면에서 한계가 존재함을 알 수 있다.

 

3. 세션 저장소 분리

말 그대로 세션 저장소를 외부로 분리하는 방식이다.

위처럼 세션 저장소를 별도로 분리함으로써 얻는 이점은 아래와 같다.

  • 한 서버에 장애가 발생하더라도 세션 데이터가 손실되는 이슈가 없다. 즉, 가용성이 확보된다.
  • 세션 데이터가 한 군데에 저장되어 있으므로, 세션 데이터가 불일치하는 등의 정합성 문제가 전혀 발생하지 않는다.
  • 세션 데이터의 정합성을 유지하기 위한 별도의 트래픽이 발생하지 않는다.

 

그렇다면 세션 저장소는 어떤 데이터베이스를 사용해야 할까? - 1

(이 부분은 사견입니다..ㅎ)

세션 스토리지도 결국 외부에서 데이터를 읽어와야 하는 I/O작업이다.
그러므로 빠른 I/O 수행을 위해 Disk 기반 데이터베이스 보단 In-Memory 기반인 Memcached/Redis 중 한 가지를 사용할 것 같다.
"그렇다면 세션 데이터는 영속성을 안 가져도 될까?"에 대해 생각해 봤을 때, 세션 데이터라는 것이 결국 사용자의 상태를 추적하는 것이고 이 상태라는 것은 변경되고, 기간이 만료되기도 하므로 영속성일 필요가 없다고 생각한다.

 

그렇다면 세션 저장소 자체에 장애가 발생하면?

당연히 장애가 발생할 것이다. 하지만 앞서 말한 것처럼 세션 데이터는 만료된다는 특성을 가진다.

그러므로 일반적으로 Disk 기반 데이터베이스에 비해 데이터 손실에 대한 피해가 적다.

(예를 들어, 로그인한 인증 정보가 소멸된다고 하면 다시 로그인하면 되는 가벼운 문제이다.)

 

하지만, 장애처리에 대해서 failover 방식이 있어야 가용성이 보장되는 서비스가 될 것이다.

 

그렇다면 세션 저장소는 어떤 데이터베이스를 사용해야 할까? - 2

앞서 In-Memory 기반 저장소를 사용한다고 했다. 그럼, 그중에서도 어떤 DB를 사용하는 것이 좋을까에 대해서 적어보고자 한다.

 

우선, 세션 데이터는 {sessionId: value} 형식인 key-value형식으로 구성된다. 따라서 Memcached/Redis를 비교해보고자 한다.

앞서 장애가 발생했을 때 failover를 통해 가용성을 보장한다고 이야기했었다. 그럼 두 DB는 failover 방식에 있어서 어떤 차이가 있는지 살펴보자.

 

 

  • 장애 자동 복구 기능(failover)
    • Redis의 failover
      • Replication을 통해 주 DB를 지정하고, 여러 대의 부 DB 서버를 둔다.
      • 주 DB에 장애가 발생하면 부 DB 중 하나를 주 DB로 전환하는 과정을 통해 failover를 지원하도록 할 수 있다.

    • Memcached의 failover
      • Consistent Hashing이라는 알고리즘을 통해 여러 대의 서버에 데이터를 분산 저장한다.
      • 즉, 한 대에 장애가 발생하면 해당 서버에 저장된 데이터는 손실이 발생한다.
      • 하지만 나머지 서버에 있는 데이터들을 재배치할 필요가 없기에 지속적으로 운영이 가능하다.
      • Replication을 지원하지 않기 때문에 분산된 각 서버(노드)에 백업 서버를 직접 구현함으로써 가용성을 보장해야 한다.

 

  • 싱글 스레드와 멀티 스레드
    • Memcached의 멀티 스레드
      • Memcached는 멀티 스레드로 동작한다.
      • 하지만, 데이터에 접근하는 경우에 Golbal cache lock을 통해 싱글 스레드처럼 동작되도록 한다.
      • 때문에 스레드 방식 차이로 인한 Redis와의 성능 차이는 거의 없다고 한다.

    • Redis의 싱글 스레드
      • Redis는 싱글 스레드로 동작한다.
      • 덕분에 스레드 간의 자원 공유 문제로부터 자유롭다.
      • 데이터가 메모리에 바인딩되므로 CPU가 Redis에서 병목현상을 발생시키는 빈도는 적다.
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. - redis QnA 페이지