개요
내가 만들었던 GENSUIKA - 귀염뽀짝 티바트 수박게임 - 의 기술스택은 EC2 + PGDB 였다.
이를 위해 AWS의 EC2에 Spring boot + RDB 솔루션을 프리티어에 맞춰서 썼다.
기능 구현을 위해 꼭 저 기술스택이 필요했던 건 아니고 AWS도 써보고 기술스택도 좀 활용해보고
그런 용도였다.
근데 프리티어가 어느새 끝나서 월 6만 6천원이 카드에 찍히니까 정신이 들었다.
이미 13만원 정도가 날아간 상태에서 나는 데이터를 다 백업한 후 부랴부랴 서버를 내렸다.
평생 서버를 죽여둘 수 없으니 어딘가로 서버를 옮겨야했다.
목표
서비스를 옮길 대상을 찾기 위해 자그마한 서비스이기에 무료로 즐길 수 있을 것을 가장 먼저 고려했다.
최근에 AWS Lambda를 써본 경험이 있어서 Lambda(라고 편의상 줄여부름)가 가장 먼저 떠올랐고 선택했다. 고려된 점은 아래와 같다.
- 함수URL을 제공해주니 간단히 url을 proxy 할 수 있겠지? (이는 후술하겠지만 잘못된 판단이었다)
- Cold Start 및 느린 처리속도를 감당할 수 있다. 사용자가 많은 서비스가 아니기도 하고 API콜이 엄청 많은 서비스도 아니다.
- 이미 한번 학습해서 학습비용이 좀 낮다.
- 유명한 솔류션이라 트러블슈팅이 쉬워야 한다.
DB로는 DynamoDB를 사용했는데 고려된 점은 다음과 같다.
- 무료다
- 다른 단점은 무료라는 조건 하에 감당 가능하다.
- 참고자료가 그래도 많다?
이렇게, 동작이 되는 것을 우선시한다면 문제될 것이 없어보여서 작업을 시작했다.
어느때와 같이 추가 삽집이 걸렸지만...
가장 지키고 싶던 목표
기존 유저에게 배포한 클라이언트를 그대로 사용할 수 있게 하고 싶었다.
이를 위해 기존 URL을 유지하면서 모든 endpoint 들을 유지해야했다.
결론부터 말하자면 어이없는 이유로 이것은 실패하였다. (이유는 후술하겠다. ㅠㅠ)
과정
다음과 같이 진행했다.
- 기본적인 코드 이동 (Spring -> Lambda Handler)
- DynamoDB 사용해게 로직 수정
- DB 마이그레이션 (PGDB -> DynamoDB)
- 인프라 연결
Spring Boot (Kotlin) -> Python Lambda handler
로직 및 코드 구성이 그럭저럭 복잡하지 않아서, ChatGPT에 우루루 넣었더니 볼만한 코드를 뽑아줬다. 이 코드를 기반으로 어느정도 적절히 수정하니 볼만한 데이터가 나왔다.
자잘한 작업을 뺀다면 그렇게 어렵지 않은 작업이었다. 기초적인 틀이 금방 나왔고, 몇개 테스트해보니 그 틀이 잘 완성된걸 확인하였다.
참고로 이 과정에서 Ranking 데이터의 Cache 과정을 뺐다. Spring 기능으로 Cache를 구현한거라 옮기기 힘들었다. 우선 돌아가는게 먼저이므로... 제외했다.
Spring에서도 lambda handler를 사용할 수 있는데, Db 사용 로직도 다 들어내야해서 어차피 거의 처음부터 짜야하는 수준이고, sprign이 cold start가 좀 느리고 메모리 사용량이 많은 것 같아서 python으로 정했다.
DynamoDB 사용해게 로직 수정
db가 pgdb에서 DynamoDB로 바뀌었기 때문에 어느정도 로직이 바뀌어야 했다. 복잡한 쿼리문을 쓸 수 없고, 인덱스도 신경써야 했다.
왜냐면 DynamoDB에서서 글로벌 인덱스는 과금대상이기 때문이다...
그래서 인덱스가 안걸려있는 특정 필드 기준으로 query를 때리면 아에 쿼리가 실행이 안되는 경우가 있었다.
이건 자잘한건데 pgdb 생성때 insensitive하게 필드명을 설정해둬서 다시 UpperCamelCase로 전부 바꿨다. 이 부분이 조금 귀찮았다.
우선 깊은 생각 안하고 scan으로 full query 때리게 해서 구현하였다. 실제로 속도가 많이 느리더라. 그래도 아직 데이터가 적어서 3초 안에 모든 응답이 끝나서, 추후 개선해도 되겠다 싶어서 우선 인덱스가 없는 필드는 scan으로 때려서 가져오게 하였다.
이를 데이터를 옮기기 전에 하였다. 데이터 다 옮겨놓고 나면 필드 스키마 잘못 정의한거 고치기 힘드니까...
DB 마이그레이션 (PGDB -> DynamoDB)
PGDB는 내리기 전에 pgdump로 모든 데이터를 빼둔 상태였다.
이걸 chatgpt에게 슥슥 물어봐서 대충 AWS CLI 로 스스르르륵 밀어넣었다.
실제 데이터를 넣을때 조금 번거로운 게 있긴 했는데 삽질 조금 하니 크게 문제없이 잘 넣어졌다.
UpperCamelCase로 바꾸는게 또한번 귀찮았따......
인프라 연결
Lambda의 함수 URL 을 써서 유니티 로컬 클라이언트에서 확인해보니 전부 잘 동작. 야호~
이제 실제로 함수 URL -> CloudFlare CNAME Proxy -> 유저
이런식으로 해서 기존에 serving되던 URL에 그대로 붙여줄려고 했더니...
왠걸.. 이게 안된다...!!!
여기서부터 절망의 시작이었는데 가장 많은 삽질이 있었다.
삽질1. Lambda는 Host값 검사를 한다.
로컬에서 바로 함수 URL을 찌르면 잘 되는데 도대체 왜 이것이 안되는것인가...
한창의 삽질 끝에 다음과 같은 글을 찾았다. 이 글 의 내용은 요약하자면 다음과 같다
Lambda는 Host 헤더 검사를 해서, 그 Lambda URL으로 진입해서 들어온거여야 OK 해줌. 다른 Host에서 호출하면 안됨.
Cloudflare를 경유했기에 Host가 바뀌어서 Lambda에서 403 forbidden ({"message" : null"}) 을 반환해주는 것이었다.
다른 방법 뭐 없나... 열심히 찾아보기로 했다.
삽질2. 나는 Custom domain을 위해 API Gateway를 안쓰고 싶은데 다들 API Gateway를 쓰라 한다
다들 위 상황에 Lambda를 AWS API Gateway 에 붙여서 잘 설정하면 잘 된다고 말한다.
근데 나는 진짜 유료 서비스를 쓰기 싫었다. 이건 자존심문제...
근데 내 의지와 별개로 검색을 하면 계속 API Gateway를 쓰라고 나온다. 애써 무시해가며 방법을 찾았다.
그 과정에서 CloudFlare와 AWS를 넘나들면서 다양한 설정을 했다. 결과적으로 다 무용지물이었기에 적지않는다.
Cloudflare의 Page rules도 적절한 방법이 아니였다. 내가 잘못 설정했을지도...
해결1. Cloudfront 를 쓰면 된다.
열심히 구글링하다가... Cloudfront라는 Cache를 위한 endpoint를 만들어주는 완전 프리티어 서비스가 aws에 있고, 이거랑 lambda랑 이어서 쓰면 된다는 AWS 공식 가이드 글을 찾았다!!!
간단히 요약하면 이거다
Cloudfront에서 요청을 잘 바꿔주고 Origin도 바꿔서 잘 Lambda를 호출해주니 Lambda 함수 URL을 보호하려면 CloudFront를 쓰세염 ^0^
흥분되는 마음으로 잘~ 설정했다. 여기서 중요한건 인증서인데, 그 커스텀 도메인을 AWS에서 관리해주게 하기 위해서 AWS에서 인증서를 cloudflare에 요청해서 받아와야 한다. 이 과정에서 도메인을 소유하고있는지 확인하는 인증수단으로 CNAME 설정을 사용한다. 그래서 이걸 등록해주면 커스텀 도메인을 cloudfront에 붙일 수 있다 (Cloudfront하고 Cloudflare하고 엄청 헷갈렸다)
삽질3. 다 좋은데... 게임을 실행해보니 일부 요청이 안됨
일부 요청에서 삽질1에서 뜬 것과 다른 403 forbidden 에러가 나왔다. Message null 은 lambda에서 반환해주는 에러고, cloudfront에서 반환하는 403인것이었다.
진짜 일부 URL만 그래서 유니티 웹 리퀘스터에 문제가 있나 생각하면서 했는데.. 결과적으로 내 실수엿다. 코드를 다시보니 GET 요청을 보낼 때 body를 보내는 케이스가 있었던 것이다.
해결2. GET에는 Body를 넣지말자
cloudfront는 GET with BODY가 오면 403으로 반환한다. 검색해보니 스펙이더라... ㅠㅠㅠ
GET 요청에서 body가 하는일이 없이 코딩실수로 넣어둔거라, 제외하고 보내보니 응답이 잘 오더라.
이 시점에서 클라이언트를 변경해야했기에 기존 유저에게 배포한 클라이언트를 그대로 사용할 수 있게 하고 싶었다.
는 나의 목표는 실패로 끝났다...
그래도 이 시점에서 내 마이그레이션은 종료되었다.
소요시간
내가 예상한 총 작업시간은 7시간이고, 실제 각 스텝의 작업시간은 다음과 같다
- 기본적인 코드 이동 (Spring -> Lambda Handler) : 3시간 좀 넘게 걸림
- DynamoDB 사용해게 로직 수정 : 3시간
- DB 마이그레이션 (PGDB -> DynamoDB) : 1시간 좀 넘게 걸림
- 인프라 연결 : 6시간 (DNS 전파 확인하고 그런걸로 중간중간 뜨는 시간이 많긴 했음)
인프라 연결만 빼면 얼~추 비슷한데... 인프라 삽질은 진짜 시간을 예상할 수 없어서 그렇다.
그래도 작업은 예상하던 정도였고, ChatGPT 덕을 많이 봤다. 이친구 간단한 로직 마이그레이션은 잘한다.
느낀점
콜드 스타트는 생각보다 크게 신경쓰이지 않았다. Python이라 그런지...
잘 돌아가는거보니까 보람차다.
이후 과제
- 잘 돌아가지만 API 느림. 캐쉬 적용하고 쿼리 최적화 할 수 있게 테이블 구조 바꾸고 로직 수정
- 클라 수정이 필수라 클라 패치는 해야함
- 무료로 즐겨요 명륜진사인프라
결론
- AWS Lambda + cloudfront 쓰면 완전무료프리티어로도 custom domain을 붙인 서비스를 만들 수 있다.
- RDB 솔루션을 NoSQL로 가져올떄는... DynamoDB의 인덱스 추가는 유료라는 것을 기억하자.
- GET에 body 넣지 말라는건 이유가 있따...
'자기개발 > 개발이야기' 카테고리의 다른 글
파이썬 비교비용 프로파일링 삽질기 (0) | 2021.12.27 |
---|---|
Kafka의 Auto Commit 에서 Auto는 당신이 생각하는 Auto가 아닐 수 있다. (1) | 2021.12.26 |
BIC FESTIVAL 2021 (부산 인디 커넥트 페스티벌 2021) 전시 참가 후기 (0) | 2021.09.16 |
Unity3D에서 백그라운드 사운드 재생을 (완전 꼼수로) 하게 하는법 (0) | 2021.06.13 |
맥북에서 변환 딜레이 없이 한글, 영어, 일본어 키보드 다 잘 사용하기 (by Hammerspoon) (4) | 2021.03.03 |