AWS lambda tip: cold start

May 7, 2019 - 3 minute read -
aws

labmda가 요청을 받았을 때 코드가 실행 될 컨테이너가 존재하지 않으면 컨테이너를 새로 생성 후 코드를 실행하게 되는데, 여기서 지연이 발생한다. 이러한 Cold start 발생 빈도수와 지연 시간은 각각의 상황에 따라 상이하다.(VPC 설정 유무, memory, code size…)

사실상 굳이 신경쓰지 않아도 될 만큼의 지연이 발생하는 경우, 그냥 사용하면 된다.

아래는 그런 경우가 아닐 때의 해결방법으로, AWS 시니어 개발자 Chris Munns의 발표 내용 요약본 중 생성된 람다 컨테이너를 계속 유지하는 Best practice에 관한 이야기다.

기본적인 내용은 한 번 생성된 컨테이너가 닫히지 않도록 CloudWatch를 이용해서 계속 핑을 하나씩 보내는 것이다.
(다만, 이미 존재하는 Lambda 컨테이가 있더라도 요청이 그 컨테이너로 가는 것을 100% 보장하지는 않고, 그렇게 되도록 ‘노력’한다라고 aws측에서 이야기 함)
그리고 다음의 조건을 충족시킬 때 가장 좋다!

  • 핑을 보낼 때 5분 보다 더 긴 간격으로 보낼 것
  • API Gateaway 같은 거 거치지 말고 직행으로 Lambda를 실행시킬 것
  • warming 요청인 것을 payload에서 확인 할 수 있을 것
  • warming ping일 경우 본래의 람다 코드를 다 실행 시키지말것

Resource

https://www.jeremydaly.com/15-key-takeaways-from-the-serverless-talk-at-aws-startup-day/

위에서 제시된 Best practice에 따라 만들어진 Lambda-warmer 라이브러리가 있다.

const warmer = require('lambda-warmer')

exports.handler = async (event) => {
  // if a warming event
  if (await warmer(event)) return 'warmed'
  // else proceed with handler logic
  return 'Hello from Lambda'
}

기본적인 사용법은 매우 간단하다. 핸들러 밖에서 lambda-warmer를 실행하고, 핸들러 가장 윗줄에 event의 payload를 검사해 warming ping인지 유저의 request인지 확인 후 예외 처리를 한다.

여기에 더해 lambda-warmer는 concurrency 기능도 지원한다.

lambda-warmer의 코드를 뜯어서 살펴보면,

// Fan out if concurrency is set higher than 1
    if (concurrency > 1 && !event[config.test]) {

      // init Lambda service
      let lambda = require('./lib/lambda-service')

      // init promise array
      let invocations = []

      // loop through concurrency count
      for (let i=2; i <= concurrency; i++) {

        // Set the params and wait for the final function to finish
        let params = {
          FunctionName: funcName,
          InvocationType: i === concurrency ? 'RequestResponse' : 'Event',
          LogType: 'None',
          Payload: new Buffer(JSON.stringify({
            [config.flag]: true, // send warmer flag
            '__WARMER_INVOCATION__': i, // send invocation number
            '__WARMER_CONCURRENCY__': concurrency, // send total concurrency
            '__WARMER_CORRELATIONID__': correlationId // send correlation id
          }))
        }

        // Add promise to invocations array
        invocations.push(lambda.invoke(params).promise())

      } // end for

      // Invoke concurrent functions
      return Promise.all(invocations)
        .then((res) => true)

    } else if (invokeCount > 1) {
      return delay(config.delay).then(() => true)
    }

n개의 concurrency를 보장하기 위해, delay()함수를 이용하는 것을 볼 수 있다. 여러 개의 lambda를 인보크 할 때, 이미 생성된 람다가 재실행 될 수 있기 때문이다.

예를 들면 5개의 람다를 인보크 시켜야 하는데, 1번 람다가 생성 된 후, 2,3,4,5번의 람다가 생성되지 않고 계속 1번으로 요청이 들어가는 경우가 있을 수 있다.
그걸 방지하기 위해 나머지 2, 3, 4, 5번의 람다가 생성 될 때 까지 delay()를 사용해 바쁘게 굴려 다른 요청을 받지 못하게 한다.

Resource

https://www.jeremydaly.com/lambda-warmer-optimize-aws-lambda-function-cold-starts/