최근 AWS에서 EKS에서 MSA를 기반으로 운영중인 웹 서비스에서 세션이 잘 유지되지 않아 로그인을 했음에도 세션이 유지되지 않고 무한 로그인 시도가 이뤄졌다. 클라이언트의 요청이 여러 곳으로 분산되어 생긴 문제였다.
내가 생각한 MSA 기반 아키텍처에서 세션을 유지하기 위한 방법으로는 아래 두가지 방법이 있다.
첫번째 방법은 Application LoadBalancer에서 sticky session을 사용하여 세션을 유지하는 방법이다.
두번째 방법은 별도의 Session Storage를 구성하여 세션을 유지하는 방법이다.
전자와 후자를 대표적인 장단점을 이야기해보겠다.
먼저 Sticky Session은 구성과 구축이 간단하다는 것이다.
하지만 전자의 방식은 치명적인 단점이 있다. MSA의 장점을 버리는 꼴이며, 무결성 유지가 불가능하다.
위 사진은 A User는 Sticky Session을 통해 A Pod만 연결되는 상황이다.
이해를 위해 간소화하여 그린 다이어그램이다.
평상 시 A POD가 정상 작동할 경우 위 사진처럼 값을 잘 받아올 수 있다.
위 사진은 A POD가 어떠한 이유로 서비스를 제공할 수 없게 된 상황이다.
이때 A User는 더 이상 Sticky Session을 통해 A POD에서 값을 받아올 수 없다.
즉 A POD가 가지고 있던 자체 Session Storage를 활용할 수 없어 사용자 세션이 초기화된다.
단순 로그인이면 사용자는 "아.. 왜 이래" 정도로 다시 로그인하고 넘어갈 수 있겠지만 결제 모듈이나, 민감정보를 송수신한다면.. 무슨 일이 일어날지는 몰라도 딱히 유쾌한 상황은 아닐거다.
그렇다면 별도의 Session Storage를 사용했을 때의 장단점을 이야기해보겠다.
위 사진은 별도의 Session Storage를 배치했을 때 작동 방식이다. 모든 POD에 적절히 로드밸런싱이 잘 이뤄질 것이다.
이 때 POD 하나가 비정상적으로 서비스를 제공할 수 없는 상황을 가정해보자.
위 사진은 서비스를 제공하던 POD가 더이상 서비스를 제공할 수 없게 된 상황이다.
하지만 Session Storage가 POD에 자체적으로 저장되어 있는 것이 아닌 별도의 Session Storage를 위한 DB를 배치하여 이상없이 값을 올바르게 받아올 수 있다.
단점을 들어보겠다. Session Storage를 위한 DB를 추가 배치하며, 요구되는 리소스가 많아지며, 자칫하면 부하를 유발할 수 있다는 점이다.
Session Storage Server의 장애 조치를 간단히 언급하겠다.
'Session Storage DB 혹은 Server 자체가 죽으면 이것도 의미 없지 않느냐' 라는 의견이 있을 수 있다. 해당 내용에서 Session Storage Server로 NoSQL Database인 redis를 사용할 것이다.
위 사진을 통해 쉽게 이해가 가능하다. redis는 흔히 RDBMS에서 장애 조치를 위해 클러스터링을 구성하는 것처럼 redis 또한 클러스터링 구성을 통해 장애 조치가 가능하다.
본론의 상황으로 돌아와 본인은 해당 문제를 해결하기 위해 NOSQL Database인 Redis를 구성하여 별도의 Session Storage를 사용하는 방법을 택했다.
인터넷에서 이를 위한 구성 방법을 찾아보던 중 Redis 서버 구축에 대한 예시는 많다. 하지만 정작 Python의 대표적인 웹 API 라이브러리인 flask 코드에서 Redis를 연동하고, 사용하는 방법을 다룬 내용이 부실하거나, 없다시피하여 이 글을 통해 정리하고자 한다.
아래 예제를 통해 간단하게 공부한 내용을 정리했다.
예제
간단하게 환경을 구축하는 방법을 포스팅했다.
가장 먼저 redis service를 구성한다. redis를 띄우는 방법은 여러가지가 있다. AWS ElasticCache를 사용하는 방법과 직접 로컬에 구성하는 방법이 있다. 본인은 로컬에서 Docker를 통해 구성하는 방법을 택했다.
본인은 간편한 구성을 위해 redis를 docker container로 구성했다.
아래 명령어를 통해 공식 redis container image를 다운로드 받아 구성이 가능하다.
$ docker pull redis:latest
$ docker run -d -p 6379:6379 --name session_storage redis:latest
아래 명령어를 입력하여 구성된 redis container의 상태를 확인한다.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e734bb8554d0 redis:latest "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 0.0.0.0:6379->6379/tcp session_storage
아래는 Python flask를 이용하여 세션에 정보를 저장하고, 조회하는 간단한 웹 애플리케이션 코드이다.
import redis
from flask import *
from flask_session import Session
app = Flask(__name__)
app.secret_key = 'worldskills'
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_REDIS'] = redis.from_url('redis://localhost:6379')
server_session = Session(app)
@app.route('/info', methods=['GET','POST'])
def set():
if request.method == 'GET':
username = session['username']
gender = session['gender']
age = session['age']
return jsonify({'username':username,'gender':gender,'age':age})
else:
session['username'] = request.form.get('username')
session['gender'] = request.form.get('gender')
session['age'] = request.form.get('age')
return jsonify({'upload':'success'})
app.run(host='0.0.0.0', port=8080)
# requirements.txt
flask
flask_session
redis
방금 전 구성한 웹 애플리케이션을 실행시켜준다.
그리고 아래는 테스트를 위한 Python 코드이다.
import requests
res = requests.post("http://localhost:8080/info", data={'username':'itguny04', 'gender':'male', 'age': '19'})
print(res.status_code)
# 200이 나와야 정상
session = res.cookies['session']
res = requests.get("http://localhost:8080/info", cookies={'session':session})
print(res.json())
# {'age': '19', 'gender': 'male', 'username': 'itguny04'} 등 방금 전 POST 요청 시 넣었던 값이 나와야 함.
주석에 적어둔 것처럼 session storage에 저장된 값을 잘 가져오는 것을 확인할 수 있다.
{'age': '19', 'gender': 'male', 'username': 'itguny04'}
직접 redis server에서 session storage에 저장된 값을 확인할 수 있다.
$ docker exec -it session_storage redis-
127.0.0.1:6379> keys *
1) "session:358832fa-7960-4fb7-9c2f-cc5be4db9f57"
상기 명령을 입력했을 때 아래 세션 값이 출력된다. 이 때 출력값은 다를 수 있다.
1) "session:358832fa-7960-4fb7-9c2f-cc5be4db9f57"
127.0.0.1:6379> get session:358832fa-7960-4fb7-9c2f-cc5be4db9f57
"\x80\x04\x956\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\busername\x94\x8c\bitguny04\x94\x8c\x06gender\x94\x8c\x04male\x94\x8c\x03age\x94\x8c\x0219\x94u."
출력된 session 값을 통해 data를 get 했을 때 저장된 값이 올바르게 출력된다.
"\x80\x04\x956\x00\x00\x00\x00\x00\x00\x00}\x94(\x8c\busername\x94\x8c\bitguny04\x94\x8c\x06gender\x94\x8c\x04male\x94\x8c\x03age\x94\x8c\x0219\x94u."
값에 \x80\x04 ... 등이 출력되는 것을 보고 의아하겠지만 유니코드로 인코딩된 내용이다. 저장된 값에 문제점은 없다.
해당 내용에 대해 포스팅을 마치겠습니다.
혹시라도 틀린 내용이나, 조언해주실 부분이 있다면 댓글 남겨주시길 바랍니다.
'IT Study > AWS & Server, Network' 카테고리의 다른 글
[AWS] ECS(Elastic Container Serivce)란? (2) | 2022.07.18 |
---|---|
[AWS, Docker] Docker Image build & Push ECR (0) | 2022.04.24 |
2021 전국기능경기대회 클라우드컴퓨팅 - 1과제 (0) | 2022.02.16 |
[Linux] 나를 위한 tmux 핵심 사용법 (0) | 2022.01.14 |
[Linux] ssh key pair 추가, 변경 (0) | 2021.12.21 |