최근 인계받게 된 프로젝트가 AWS Lambda를 사용해 배포된 프로젝트라,
서버리스에 대해 정리해 보았다.
AWS Lambda를 기준으로 작성되었다.
서버리스는 서버가 없다? X
서버를 개발자가 관리하지 않는다 O
서버를 항시 켜두는 게 아니고,
요청 발생시 휴면 상태에 있던 함수 실행하는 방식이다.
…
몇 가지 더 존재하지만, 우선 이 게시글에서는 Lambda를 위주로 작성했다.

각 Lambda함수마다 EC2기반의 개별 컨테이너에서 독립적으로 실행된다.
하나의 실행환경은 하나의 람다가 점유하여 1대1 대응되고, 이로 인해 같은 람다 함수가 종료 전 연달아 호출됐을 경우 새 컨테이너에서 병렬적으로 실행된다.

또한 각 람다 컨테이너는 복수의 가용 영역에서 실행되고, Worker Manager에 의해 항상 실행 가능한 워커를 관리하므로 Host Failure에 대해 고가용성을 제공한다.

내부적으로는 각 요청을 어느 AZ의 Frontend로 전달할지 결정하는 Load balancer,
요청을 전달받고 검증한 후 전달하는 Frontend,
요청을 받기에 괜찮은 상태인지 확인하고, 워커에 요청을 할당하는 Worker Manager
실제 코드를 실행하는 Worker 등 많은 구성요소로 관리된다.
람다의 실행환경에 대한 이해를 위해 라이프사이클을 이해하는게 필수적이다.

여기서 Lambda가 실행 환경을 바로 내리지 않는다는 점에 유의해야 한다.
이 점에서 Cold Start와 , Warm Start가 구별되고
성능 차이를 발생시킨다.

Warm Start: 이미 실행 준비가 완료된 상태

Warm Start에서는 실행 환경을 재사용하므로, 컨테이너가 종료되기 전 호출이 다시 발생하면 INIT단계를 스킵할 수 있다.
바로 핸들러 호출 단계로 접어들기 때문에, Cold Start에서 발생하는 Latency를 피할 수 있다.

동시 요청이 들어올 경우, 실행 환경을 병렬적으로 구성한다.
현재 준비된 실행 환경이 내려가기 전에 새 요청이 들어오면, 해당 실행 환경을 재사용하지만
그 시간에 Invoke단계라면 새 컨테이너가 필요하다.
Lambda의 경우 동시 실행 디폴트 1000개 제한이 걸려 있다.
실행환경이 유지되면 아래와 같은 영향이 있다.
이렇게 람다의 실행 방식을 살펴보았다. 이제 "그래서 대체 언제 실행되는건데?"에 대한 답을 해보자.
람다는 호출 트리거에 의해 실행된다. 이 실행 트리거는 여러 가지 종류가 있다.
예시: S3 버킷(이벤트소스)에 파일 업로드 → 파일 생성 이벤트 트리거 → 람다 함수 실행



- S3에 원본 이미지 업로드 → 썸네일 및 리사이징 람다 트리거 → 생성된 썸네일 저장

로그 데이터 Kinesis Stream으로 로드 → 로그 종류 구분하는 람다 함수 트리거 → 분류된 데이터로 파티션 생성하여 S3에 저장
CloudWatch 로그 스트림에서 특정 패턴에 매치 → 알람 전송 혹은 스토리지에 로그 저장하는 람다 트리거
인스턴스 상태 변화에 따른 알람 / 장애 발생시 복구 프로세스 자동화


- EC2 인스턴스 장애 발생 or 오토 스케일업 → SES sendEmail API를 이용해 메일 전송하는 람다 트리거
앞서 말했듯 Cold Start는 상당한 latency비중을 차지하므로, 이를 최대한 줄이는 것이 관건이다.
이를 위한 방법들이 있다.
기존에는 VPC ENI로인한 latency가 존재했으나, 이후 VPC접근 방식이 프록시와 같이 개선되어 해당 문제는 해결되었다.
만약 한 람다 함수가 DB커넥션을 맺어 뒀다면,
람다 종료시 DB커넥션이 종료되어야 한다.
그러나 Shutdown이벤트를 코드 상에서 감지할 수 없고 Connection Timeout에 의존하므로, 커넥션을 관리하지 않을 경우 문제가 발생한다.
서버리스 함수가 idle state(게으른 상태)로 진입했을 때 커넥션을 종료하지 않으므로, Idle 커넥션 문제가 발생한다.
또한 여러 람다 함수는 각기 다른 컨테이너에서 실행될 수 있으므로, 커넥선 재사용을 보장할 수 없다.
그렇다면 함수 실행시 DB커넥션을 맺고, 함수 종료시 커넥션 해제해야 하나?
→ 방법 자체가 틀린 건 아니지만, DB커넥션을 맺고 끊는 것은 고비용이다.

이를 해결하기 위해 DynamoDB, RDS 등에서 커넥션 풀링 기능을 하는 DB Proxy 제공하고 있다.
DB연결 전용 프록시 함수를 생성하여, 이 함수를 거치도록 해 각각의 람다 Function이 DB커넥션을 관리하지 않고, 이로 인해 Connection 수를 개선할 수 있다.
또한 이미 연결된 DB세션을 재활용하므로, Cold Start시 재연결 시간을 줄일 수 있다.
만약 RDS, DynamoDB를 쓰지 않는다면 어떻게 커넥션 관리를 하게 되는지 궁금해서 많이 찾아 보았는데, 이렇다 할 명쾌한 해결책은 찾아보지 못했다.
이에 대한 것은 더 조사가 필요할 것 같다!
https://futurecreator.github.io/2019/03/14/serverless-architecture/
https://velog.io/@woo_sae/AWS-Lambda에-대해-알아보자
Kinesis, Lambda를 이용한 로그 파이프라인 구성
Lambda, SNS, Cloudwatch를 이용한 복구 자동화