AWS Lambda한테 억까 당한 ssul (feat. cold starts)

              

카카오톡 챗봇 산돌이

kakao i builder를 통해 챗봇을 운영하면서 현재 스킬 발화(api가 필요한 발화)는 모두 aws lambda를 활용해서 Serverless 개발을 하고 있다. 나름 자랑 아닌 자랑이라면 현재 유저가 3500명이 넘었다.!!
한국공학대학교 산돌이 챗봇 바로가기

Serverless란?

서버리스란 개발자가 서버를 관리할 필요 없이 애플리케이션을 빌드하고 실행할 수 있도록 하는 클라우드 네이티브 개발 모델이다.
즉, 클라우드 제공업체가 서버 인프라에 대한 프로비저닝, 유지 관리 등을 처리해주기 때문에 개발자는 조금 더 비즈니스 로직 작성에만 집중할 수 있게 된다.

AWS Lambda란?

Lambda는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있게 해주는 컴퓨팅 서비스입니다. Lambda는 고가용성 컴퓨팅 인프라에서 코드를 실행하고 서버와 운영 체제 유지 관리, 용량 프로비저닝 및 자동 조정, 코드 및 보안 패치 배포, 코드 모니터링 및 로깅 등 모든 컴퓨팅 리소스 관리를 수행합니다. Lambda를 사용하면 거의 모든 유형의 애플리케이션 또는 백엔드 서비스에 대한 코드를 실행할 수 있습니다. Lambda가 지원하는 언어 중 하나로 코드를 공급하기만 하면 됩니다.

aws Lambda 출처

나에게 왜그러는거야

그런데 한가지 문제가 발생했다.
현재 발생한 날씨 이슈

네이버의 날씨 페이지로부터 정왕동의 날씨를 크롤링해오고 있었는데 종종 페이지의 태그 구조가 변경되면 수정을 해줘야할때가 있었다.
그래서 수정 후 코드를 적용하였더니 위와같이 error code : 'NoneType' object is not subscriptabl 에러가 발생하였다.

kakao i builder에는 다양한 리턴타입이 존재하고 그에 맞는 알맞은 json 형식으로 객체를 변환해줘야한다.

날씨는 simpleText라는 형식으로 우리가 파싱한 내역을 json 형식으로 만들어 주고 있다.

코드는 모듈화되어 있기 때문에 일부 필요 코드는 아래와 같다.

result = f"{dates}기준 정왕 날씨입니다\n현재날씨는 {today[0]}이고, {today[1]}"
return settings.GEN.set_text(result)
        
...
        
GEN = ReturnType()  # kakao-i type json generator
...
        
def init_json():
    return {
        "version": "2.0",
        "template": {
            "outputs": [
            ],
            "quickReplies": [
                {
                    "messageText": "도움말",
                    "action": "message",
                    "label": "도움말"
                }
            ]
        }
    }

...

class ReturnType:  # 리턴 타입별 JSON 형식을 만드는 곳 입니다.
    def __init__(self, reply_json: dict = None):
        self.return_json = init_json()
        if reply_json is not None:
            self.return_json['template']['quickReplies'].append(reply_json)

    def set_text(self, text, is_init=True):  # 텍스트 형식
        if is_init:
            self.return_json = init_json()  # 이전에 들어간 텍스트가 유지될건지 여부
        basic_text = {
            "simpleText": {
                "text": str(text)
            }
        }
        self.return_json["template"]["outputs"].append(basic_text)
        return self.return_json

코드 상에는 아무런 문제가 없었기 때문에 우리는 JSON 마샬링 과정에 문제가 발생하였다 생각했고 Json 형식으로 만들어주는 ReturnType에 문제가 있다고 생각하였다.

kakao님 정답을 알려주세요 흑흑

그래서 일단 kakao 측에 한번 문의를 넣어봤다.

스킬을 정상적으로 연결했음에도 여러 사유로 인해 봇이 동작하지 않을 수 있습니다.
1. 봇 작업자의 스킬 서버가 로컬(local) 환경인 경우
2. 스킬 기능은 동작했으나 출력할 값이 없는 경우
3. JSON 포맷 구성이 올바르지 않은 경우
4. 봇 서버에 이슈가 있는 경우 등

흠... 그렇구나 일단 1번은 aws lambda로 작동하고 있으니 문제가 없고 2번과 3번 4번이 문제가 있다고 생각했다.

그리고 시간이 너무 늦어서 다음날 해야지 하고 잠에 들었다..

AWS의 억까는 이렇게 시작됐던 거였다.

갑자기 하룻밤 자고 일어나니 잘되는 광경을 볼 수 있다.

문제의 이유

cold start란...
람다는 인스턴스 풀을 공유한다. 람다가 실행되면 한 인스턴스를 받은 후 함수가 실행되기 위한 세팅(코드 로드, 초기화 등등)을 해야한다. 이러한 이유로 예상보다 더욱 오랜 시간이 걸리게 되는데 이를 cold start라 한다. 한 함수가 실행된 후 5~7분이 지나면 인스턴스를 초기화 한다고 한다.

aws lambda 에는 cold time 과 warm time이란게 있었다. lambda가 비싸다보니 요청이 적으면 cold time에 들어가서 api 호출까지 예열을 해야되서 시간이 오래걸렸던거다.
그래서 그때 런타임 에러가 발생하는데 함수가 시간을 초과하였거나 구문 오류를 감지했거나 응답 객체를 JSON으로 마샬링하는 데 실패하여 런타임이 함수를 종료하게 된다.

그래서 저희 산돌이 코드 내 exception에 걸려서 어제와 같은 error code : 'NoneType' object is not subscriptable 라는 에러가 발생해서 데이터가 없는데 핸들링하려하니 문제가 발생했던 거였다.

해결 방법

1. 람다를 계속 호출해준다.
사실 항상 컨테이너가 준비되어 있게 하도록 람다를 지속적으로 호출하는게 가장 좋다. 하지만 호출이 될때마다 비용이 산정되는 방식이기 때문에 그만큼 비용이 더 들 수 있다.

2. 람다의 메모리를 늘려 스펙을 높인다.
메모리를 더 할당할 수록 컴퓨팅 스펙이 높아지기 때문에 처리속도가 빨려져서 딜레이가 줄어들 수 있다.

3. 프로비저닝된 동시성 활성화
2019년 콜드스타트 문제를 해결하기 위해 나온 옵션으로 함수의 호출에 바로 응답할 수 있게 미리 준비하는 옵션이다. 이를 활성화 하면 미리 함수의 환경을 세팅해두기 때문에 딜레이가 줄어들지만 추가적인 비용이 든다.

정리

한마디로 정리하자면 돈을 더 써야 된다는 문제였다.. 흑흑
돈이 있으면 이런 부분도 해결할 수 있을텐데... ㅠㅠㅠ 점점 유저는 많아지는데 이런 문제들을 어떻게 해결해야할지 멘토나 사수가 있었으면 하는 바램이 크다.

좋은 웹페이지 즐겨찾기