Apache APISIX를 사용한 Sticky Sessions - 이론

Nicolas Fränkel

Nicolas Fränkel

July 27, 2023

Technology

스티키 세션(sticky sessions), 또는 세션 어피니티(session affinity)는 라우팅 컴포넌트가 항상 동일한 업스트림 노드로 요청을 라우팅하는 메커니즘입니다. 이 글에서는 스티키 세션의 필요성, 대안, 그리고 Apache APISIX를 통해 이를 구현하는 방법에 대해 설명하겠습니다.

스티키 세션의 필요성

스티키 세션은 데이터베이스가 아닌 업스트림 노드에 상태를 저장할 때 인기를 얻었습니다. 간단한 전자상거래 쇼핑몰 예제를 통해 더 자세히 설명하겠습니다.

작은 전자상거래 사이트의 기본 구조는 웹 애플리케이션과 데이터베이스로 구성될 수 있습니다.

전자상거래 앱의 기본 아키텍처

비즈니스가 성공적으로 성장하면, 이 아키텍처를 확장해야 할 시점이 옵니다. 수직 확장(더 큰 머신)이 불가능해지면, 수평 확장(더 많은 노드)을 해야 합니다. 추가적인 애플리케이션 노드가 생기면, 웹 애플리케이션 노드 앞에 로드 밸런서 메커니즘을 추가하여 부하를 분산시켜야 합니다.

추가 노드를 위한 로드 밸런싱 아키텍처

매번 데이터베이스에 접근하는 것은 비용이 많이 드는 작업입니다. 자주 접근하지 않는 데이터라면 괜찮지만, 모든 요청에 대해 장바구니 내용을 표시하려면 속도를 높이기 위해 몇 가지 대안이 있습니다. 웹 애플리케이션이 서버 사이드 렌더링을 사용한다고 가정하면, 전통적인 해결책은 웹 애플리케이션 노드의 메모리에 장바구니 관련 데이터를 저장하는 것입니다.

그러나 사용자 X의 장바구니를 노드 1에 저장했다면, 사용자 X의 모든 요청을 동일한 노드로 전달해야 합니다. 그렇지 않으면 사용자는 장바구니 내용을 잃어버린 것처럼 느낄 수 있습니다. 스티키 세션 또는 세션 어피니티는 동일한 사용자를 동일한 노드로 일관되게 라우팅하는 메커니즘입니다.

스티키 세션의 한계

더 나아가기 전에, 스티키 세션의 중요한 한계를 설명해야 합니다. 데이터를 저장하는 웹 애플리케이션 노드가 어떤 이유로든 다운되면 데이터는 복구할 수 없이 손실됩니다. 위의 전자상거래 시나리오에서 이는 사용자가 가끔씩 장바구니를 잃어버릴 수 있다는 것을 의미하며, 이는 비즈니스 관점에서 받아들일 수 없습니다.

이러한 이유로, 스티키 세션은 **세션 복제(session replication)**와 함께 사용되어야 합니다: 한 노드에 저장된 데이터는 복제되어 다른 모든 노드와 동기화되어야 합니다.

세션 복제는 모든 기술 스택에 존재하지만, 관련된 표준은 없습니다. 저는 JVM에 익숙하므로 몇 가지 옵션을 소개하겠습니다:

  • Tomcat은 기본적으로 세션 복제를 제공합니다.
  • Hazelcast는 클러스터된 인메모리 솔루션을 제공하며, 다양한 수준에서 통합할 수 있습니다.
  • Spring Session은 특정 솔루션 위에 추상화 계층을 제공합니다.

데이터가 모든 노드(또는 원격 클러스터)에 복제되면, 스티키 세션이 더 이상 필요하지 않다고 생각할 수 있습니다. 이는 가용성만 고려할 때는 사실이지만, 성능을 고려할 때는 그렇지 않습니다. 데이터 지역성(data locality) 문제입니다: 네트워크를 통해 다른 곳에서 데이터를 가져오는 것보다 현재 노드에서 데이터를 가져오는 것이 더 빠릅니다.

Apache APISIX에서의 스티키 세션

스티키 세션은 어떤 로드 밸런서, 리버스 프록시, API 게이트웨이에서도 필수적인 기능입니다. 그러나 Apache APISIX의 문서는 이 주제에 대해 쉽게 접근할 수 있는 진입점이 부족합니다.

Apache APISIX는 _route_를 _upstream_에 바인딩합니다. 업스트림은 하나 이상의 노드로 구성됩니다. 요청이 라우트와 일치하면, Apache APISIX는 사용 가능한 모든 노드 중에서 요청을 전달할 노드를 선택해야 합니다. 기본적으로 알고리즘은 가중치 기반 라운드 로빈(weighted round-robin)입니다. 라운드 로빈은 한 노드에서 다음 노드로 순차적으로 전환하며, 마지막 노드 이후에는 다시 첫 번째 노드로 돌아갑니다. 가중치 기반 라운드 로빈에서는 가중치가 Apache APISIX가 한 노드에 전달하는 요청 수에 영향을 미칩니다.

그러나 다른 알고리즘도 사용할 수 있습니다:

일관된 해싱은 특정 값(NGINX 변수, HTTP 헤더, 쿠키 등)에 따라 동일한 노드로 요청을 전달할 수 있게 합니다.

HTTP는 상태 비저장 프로토콜이므로, 애플리케이션 서버는 첫 번째 응답에서 사용자를 추적하기 위해 쿠키를 설정합니다. 이를 "세션"이라고 합니다. 우리는 기본 세션 쿠키 이름을 알아야 합니다. 다양한 애플리케이션 서버는 다른 쿠키를 제공합니다:

  • JVM 기반 서버의 경우 JSESSIONID
  • PHP의 경우 PHPSESSID
  • ASP.Net의 경우 ASPSESSIONID
  • 기타 등등

저는 일반적인 Tomcat을 사용할 것이므로, 세션 쿠키는 JSESSIONID입니다. 따라서 두 노드에 대한 Apache APISIX 문서는 다음과 같습니다:

routes:
  - uri: /*
    upstream:
      nodes:
        "tomcat1:8080": 1            #1
        "tomcat2:8080": 1            #1
      type: chash                    #2
      hash_on: cookie                #3
      key: cookie_JSESSIONID         #4
  1. 업스트림 노드 정의
  2. 일관된 해싱 알고리즘 선택
  3. 쿠키 기반 해싱
  4. 해싱할 쿠키 정의

결론

이 글에서는 스티키 세션에 대해 자세히 설명하고, 스티키 세션과 함께 항상 세션 복제를 사용해야 하는 이유, 그리고 Apache APISIX에서 스티키 세션을 구현하는 방법을 설명했습니다.

더 알아보기:

Tags: