참고: https://www.youtube.com/watch?v=0PRjqEQ2J3g&t=56s 

https://www.slideshare.net/awskorea/aws-lambda-aws-aws-summit-seoul-2019


Front End Invoke: 실제 API call을 받는 역할(동기 / 비동기 호출 모두 관장)

(Synchronous Inoke)

Counting Service: 사용자가 얼마나 많은 API 요청을 하는지 모니터링하고 제한 기능 제공

Worker Manager: 실제 Container의 상태를 관리하고 API 요청을 가용 가능한 Container로 중계

Worker: 고객 함수(코드)가 안전하게 실행되는 실제 Container 환경

Placement Service: Worker에 Sandbox 구성을 자원 활용률이 높고, 고객 서비스 영향이 없도록 관리

 

Load Balancing & Auto Scaling

Load Balancing(Worker 배정 과정)
Auto Scaling(Worker Node 추가)

(Worker가 있는 건 알겠는데 얼마나 바쁜지 일을 더 줘도 되는지 Mgr가 Placement에게 물어본다.)

(필요한 Function이 일하고 사라지는 개념, 일을 다하면 리소스에 대한 return → 이 역할도 Placement 담당)

 

Handling Failures

멀티 AZ로 관리 되기 때문에 한 AZ에서 장애 발생해도 상관 없다!

 

Isolation

고객의 Workload = function = code는 항상 안전한 환경에서 실행한다.

Firecracker (하나의 host 내에서 수백개 ~ 수천개 단위의 vm 돌아갈 수 있음)

→ 하나의 코드가 그냥 독립되어서 돌아간다고 생각하면 됨

https://github.com/firecracker-microvm/firecracker

이렇게 되면 굉장히 많은 vm이 동시에 제한된 하드웨어 리소스를 공유하는 건데 충돌 발생 안하나???

→ Guest OS에서 Host OS로 접근할 때의 Driver를 개선! (firecracker device 안에서 직접적으로 하드웨어를 액세스)

(VirtIO: 게스트 커널의 디바이스 드라이버와 협력해 게스트 커널을 부팅하는데 필용한 하드웨어를 가상으로 제공)

  • Guest OS가 VirtIO 드라이버에 쓰기 요청
  • VirtIO 드라이버가 커널 링 버퍼의 공유 메모리에 요청을 PUSH
  • Firecracker가 쓰기 요청을 풀고 물리 디스크에 쓰기

Utilization

"Lambda의 경우 사용한 만큼 요금을 지불하기에 주어진 시간 내 최대한 시스템을 바쁘게 돌려야 좋다."

→ 균형 잡힌 분배가 아닌 몰빵

동일한 Workload를 한 곳에 담게 되면 자원에 대한 충돌이 일어날 확률이 높다.

→ 각 각 다른 타이밍에 다른 자원을 사용하는 멀티태스킹을 해라!

Worker 외부의 RemoteNAT 컴포넌트로 ENI와의 NAT 처리 진행

https://aws.amazon.com/ko/blogs/compute/announcing-improved-vpc-networking-for-aws-lambda-functions/

  • 시간이 많이 걸리는 처리는 Lambda Function의 스케일 시가 아닌 Lambda Function 작성 시에 실행되므로 스케일이 빨라진다
  • VPC Lambda의 대기 시간이 낮아지고 대기 시간이 예측하기 쉬워진다
  • ENI 및 IP 주소 범위에 제한을 두지 않고 VPC Lambda를 실행할 수 있다
  • 하나의 ENI를 많은 Worker에서 공유하고 이용하기 때문에 IP 주소의 소비 수가 줄어 NW 관리가 간소화된다

Lambda 와 RDS/RDBMS 접근

전형적인 DB 접근

  • 여러 가용 영역 내 Subnet에 ENI 사용: AZ 레벨의 이벤트 또는 IP 소모 문제 피할 수 있음
  • Lambda는 VPC 내 ENI로 접근: 가용 IP에 따른 확장성의 제약을 고려해야한다, ENI 신규 구성은 시간이 소모됨.
  • Public host name DNS 쿼리를 피할 수록 좋음
  • 기본적으로 VPC의 Lambda는 인터넷 접근 불가능: NAT 추가하고 Routing Table 구성 필요

 

DB 커넥션 관리는 어떻게 해야할까?

(수많은 Lambda가 필요할 때)

  • Connection Pooling과 Lamdba
    • Container 당 하나의 connection 만 사용: Connection Pool Size = 1
    • Handler 밖에 Global section 에 DB connect 객체를 생성, 재활용

 

위처럼 하면 생기는 문제점

  • Lambda container가 사라지는 지 인지할 수 없음: Connection을 명시적으로 닫을 수 없음(Database TTL에 의지)
  • Lambda container의 생성 삭제를 조정할 수 없음: Idle(대기) connection이 많이  생성될 수 있음
  • 여러 Lambda 함수 실행은 여러 다른 Container에서 실행될 수 있음: Connection 재사용 보장 못함

 

해결 방법 1: Concurrency limit

  • 계정 Concurrency 제한 / 함수 Concurrency 제한
    • AWS support - Account 제한 요청
  • Lambda는 호출 제한이 있을 경우 retry 수행/관리

"Concurrency limit: 본인의 전체 서비스에 문제가 없도록 보호하기 위해 존재, 필요에 따라 조절 가능"

  • Account level: 계정 내 모든 lambda에 적용되어 DB 접근 함수 제한이 간단하지 않음
  • Function level: 어떤 Lambda 함수가 DB 접근이 필요한지 확인, Peak 발생하기 전에 알고 App 레벨의 이해 필요

 

해결 방법 2: 동적 Connection 관리

  • 확장 가능한 구조 구현
  • Connection 수를 관리 가능, Lambda 함수 개수와 무관
  • 고려 사항: 관리 리소스 증가, Connection 재사용 불가, 약간의 Latency 증가
더보기

❗ 그래서 결론적으로 뭔 소리냐...? ❗

(Lambda + RDS의 경우 문제점)

1) lambda와 rds가 connection을 맺었는데 lambda가 명시적으로 connection 종료를 하지 않은 채 없어졌는데, 또다른 요청이 들어왔을 경우 새로운 connection을 맺게 됨...(max_connection 문제)

2) lambda가 과도하게 invoke, 실행되는 동안 모든 lambda가 connection을 맺게 되면 또 max_connection 초과

 

(해결 방안)

1) Connection 관리: handler 하나에 1 Connection / globel scope으로 connection 관리

2) RDS Proxy: Connection Pool은 Proxy가 가지고 있고, Lambda는 프록시를 통해 대신 Connection을 사용

3) API 호출 최적화: lambda가 동시에 적게 실행 + Connection이 빠르게 반환되게

 

그래도 가능하다면 DynamoDB나 AWS Aurora와 같이 Scale Out이 가능한 DB를 사용하는 것이 좋다.

 

참고: https://velog.io/@jdd04026/Lambda

 

Tip. Lambda 함수 트레이싱

  • 필요한 이유("lambda는 실행 후 사라지는 개념..")
    • 서버리스 아키텍처/애플리케이션 개발 시 Debug 편의성
    • 서버리스 아키텍처/애플리케이션 동작 시 성능 문제 해결의 편의성
  • 방법
    • CloudWatch를 통한 Metrics 모니터링
    • CloudWatch log 통한 log 분석
    • log와 metric으로 부족한 경우:
      • 전체 요청 처리 시 서버리스 함수 간의 관계 파악 확인
      • 요청 처리 시 다른 리소스 접근에 대한 관계 파악 확인
    • X-ray 서비스 활용: 서버리스 아키텍처를 만들고 람다로 서비스 개발 시 전체 흐름 확인

 

Tip. Lambda 커스텀 런타임 사용

  • Layer 기능을 활용하자!

 

'Cloud > AWS' 카테고리의 다른 글

[AWS] Lambda에 X-Ray 적용하기  (0) 2022.11.06
[AWS] Serverless Service - Lambda 편 (3)  (0) 2022.11.06
[AWS] Serverless Service - Lambda 편 (1)  (0) 2022.11.05
[AWS] API Gateway - Websocket API  (0) 2022.11.01
[AWS] Lambda Warm Start / Cold Start  (0) 2022.10.28

 Managed Level - Console

  • 구성
    • 일반구성: 기본 설정 관련
      • 메모리: 메모리에 비례하는 CPU가 함수에 할당/Compute Optimizer 옵트인 가능
      • 임시 스토리지: /tmp 디렉터리, 최대 10GB까지 대기 시간이 짧은 안전한 임시 파일 시스템 액세스
      • 제한 시간, 실행 역할
    •  트리거
      • 소스: Alexa, Apache Kafka, API Gateway, ALB, CW Logs, CodeCommit, Cognito Sync Trigger, DynamoDB, EventBridge, Kinesis, MQ, MSK, S3, SNS , SQS 등..
    • 권한
      • Lambda 실행 역할 / 태그 기반 액세스 제어 / 리소스 기반 정책 등..
    • 대상
      • 소스가 비동기식 호출: SNS, SQS, Lambda, EventBridge
      • 소스가 스트림 호출: Kinesis, DynamoDB
    • 함수 URL: Lambda에 고유한 HTTP(S) 엔드포인트 추가
      • 언제 사용할까?
    • 환경 변수: key-value
    • 태그
    • VPC: (default로 퍼블릭으로 액세스 가능한 리소스로 deploy)
    • 모니터링 및 운영 도구: CW, X-Ray, CW Lambda Insights
    • 동시성: 함수에 대한 최대 동시 실행 횟수 설정
    • 비동기식 호출: 처리되지 않은 이벤트 처리
      • lambda가 자체적으로 재시도를 처리(최대 2번)
      • 그래도 스로틀 발생, 오류 반환 시 대기열에 보관(최대 6시간)
      • 보관 이후에 삭제하는데 이를 SQS나 SNS로 전송(배달 못한 편지 대기열)
    • 코드 서명: 신뢰할 수 있는 코드만 함수에서 실행되도록
    • 데이터베이스 프록시
      • 데이터베이스 연결 풀 관리
    • 파일시스템: EFS 연결
    • 상태 머신: Step Functions
  • 별칭: 함수 버전을 매핑
  • 버전

Code Level(Python 기준)

  • Lambda Handler Function:
    • Lambda 콘솔의 기본 이름: lambda_function.lambda_handler
      • lambda_handler: 함수 이름(핸들러 이름 바꾸고 싶으면 여길 바꾸면 됨)
      • lambda_function: 핸들러 코드가 저장된 파일
##Global Scope

def lambda_handler(event, context):
	## business logic here
    return response
  • event: 
    • Lambda가 Invoke 될 때 다른 서비스에서 이벤트 (혹은 데이터) 를 전달 받는다.
    • 어떤 서비스로부터 Invoke 되었는지에 따라 event의 구조가 달라진다.
    • Invoke 방식에는 Event / RequestResponse 로 나뉨.
      • RequestResponse: API Gateway → Lambda
      • Event: Response를 바라진 않음. 이벤트 전달이 목적!(Asynchronous)
    • Lambda가 Event 방식으로 호출된 경우, 실행 결과로 response 값이 있으면 이 데이터는 Invoke를 요청한 클라이언트에게 다시 전달될까? → NO!
import os

def lambda_handler(event, context):
    print('## ENVIRONMENT VARIABLES')
    print(os.environ)
    print('## EVENT')
    print(event)
더보기

 여기서 착각했던게 있는데...

이 테스트 이벤트의 경우에는 테스트를 위한거고 실제적으론 함수를 invoke 하는 서비스의 이벤트를 받는 것!

테스트 용으로 람다 함수가 잘 작동하는지 확인하고 해당 서비스와 트리거를 하던 연결을 하던 해서 사용하면 될듯..

(아마 서비스마다 들어오는 이벤트 형식이 지정되어있는 것 같다... 테스트 이벤트 템플릿 보면 겁네 많음)

  • context:
    • Lambda의 Runtime 정보를 핸들러에게 제공
      • function_name, function_version, invoked_function_arn, memory_limit_in_mb, aws_request_id, log_group_name 등..(로깅 등의 용도로 사용할 수 있는 메타데이터)
      • 오랫동안 실행되는 Lambda의 경우 get_remaining_time_in_millis() 가 쓸모 있을지도..
import time

def lambda_handler(event, context):   
    print("Lambda function ARN:", context.invoked_function_arn)
    print("CloudWatch log stream name:", context.log_stream_name)
    print("CloudWatch log group name:",  context.log_group_name)
    print("Lambda Request ID:", context.aws_request_id)
    print("Lambda function memory limits in MB:", context.memory_limit_in_mb)
    # We have added a 1 second delay so you can see the time remaining in get_remaining_time_in_millis.
    time.sleep(1) 
    print("Lambda time remaining in MS:", context.get_remaining_time_in_millis())
  • Global Scope:
    • 핸들러 바깥의 영역으로 init(cold start 시)에만 실행되는 부분
    • Worker 노드가 종료되지 않으면 메모리에 남아있음(캐시처럼 사용할 수 있는 트릭)
    • 모놀리식 구조로 코드 작성했을 때 RDS 커넥션 추가해도 괜찮다..
      • but, 함수를 기능별로 나눴을 경우 RDS 커넥션 해두고 끊지 않으면 DB 쪽에서 max_connection에 걸릴 수도.. → Global Scope에 연결하는 것보다 사용할 때마다 연결 하고 끊는게 좋다
    • init은 했는데 terminate을 따로 안해주니까 이런 경우에 시스템이 어떻게 동작할까에 대한 고민을 해라!
  • 로깅:
  • 오류:
    • Lambda invoke → 호출 요청 수신 → 검증 과정
      • 실행 역할 권한 검증 → 이벤트 문서가 유효한 JSON 문서인지 확인 → 파라미터 값 검사
    • 요청이 검증을 통과하면 Lambda가 함수 인스턴스로 요청을 보냄
      • Lambda 런타임 환경의 경우 이벤트 문서를 객체로 변환한 후 함수 핸들러에 전달
    • 오류 발생 반환: 예외 유형(오류의 원인), 메시지 및 HTTP 상태 코드

Infra Level

  • Lambda Invoke:
    • Invocation Type: Invoke 시 어떤 방식으로 실행할지 결정 가능
      • Event: Asynchronous 방식
        • Lambda 함수가 성공했는지 여부를 알 수 없음
        • Invoke Event를 잘 수신했다(Accept의 의미): "StatusCode: 2XX"
        • → 코드가 잘 동작했는지 확인할 수 있는 방법이 없어 DLQ가 필수다!
        • S3, SNS, EventBridge (CloudWatch Events)
      • RequestResponse(default): Synchronous 방식
        • "StatusCode: 200" : 성공적으로 잘 수행했다
        • Kinesis, DynamoDB Streams, SQS, ALB, API Gateway
      • DryRun: 파라미터 및 권한을 검사해서 실행 가능한지에 대한 체크(실제 Invoke는 아님)
    • Asynchronous invocation:
      • event를 queue(SQS)에 넣는다 : Lambda 내부적으로 queue를 가지고 있음
        • lambda 함수가 적절한 타이밍에 큐에 쌓인 이벤트를 처리를 못하면 이벤트가 손실 될 수 있음
        • 따라서, 반드시 수행되어야하는 태스크는 별도의 고객 소유 SQS를 통해 Invoke 시켜야함
          • lambda + sqs + lambda 의 구조가 best practice
      • 하나의 이벤트를 중복처리하는 경우도 존재:
        • Event-driven 방식으로 동작하는 함수는 반드시 Idempotency(멱등성)를 고려하여 작성
      • 함수 실패의 경우 Dead Letter Queue를 통해 알림 받기 가능
    • Invocation Type에 따른 Lambda 트러블 슈팅:
      • 대량의 데이터를 SQS에 넣었다. Lambda를 Invoke 시키는 시나리오에서 Lambda 실패, 데이터는 어디로 갈까? Lambda 함수는 기다리고 있으면 다시 또 실행될까?
        • SQS는 RequestResponse(Synchronous) 방식: retry를 몇 번 시도 후 그래도 실패하면 실패 이벤트 처리로 넘어감. → 실패 시 고객이 SQS의 처리상태를 통해 직접 알 수 있음
      • 대량의 파일을 S3에 업로드해서 Lambda를 Invoke 시키는 시나리오에서 실패했다, 이벤트는 어디로 갈까? Lambda 함수는 기다리고 있으면 다시 또 실행될까?
        • S3 는 Event(Asynchronous) 방식: retry 몇 번 시도 후 실패시 이벤트 손실이 생길 수 있음 → DLQ를 지정하는 방법밖에 없음(Lambda 내부 문제)

 

Lambda 내부 아키텍처

  • 컴포넌트들이 달성하고자 하는 목표?
    • Load Balancing
    • Scaling Up and Down
    • Handling Failures
    • Predictable Low Latency
    • Security Isolation
    • Managing Utilization
    • ...

 

  • Synchronous Invoke:
    • Front End Invoke: IAM Policy, Role 확인(authentication) / Concurrency 더 수행할 수 있는지 / 메타데이터 확인 등
    • Counting Service: concurrency limit을 넘지 않는지
      • 근데 concurrency를 초과해서 실행될 수 있음 → limit에 도달하자마자 막으면 고객들이 maximum concurrency를 누릴 수 없음
    • Worker Manager: Front End와 함께 AutoScaling
    • Worker: sandbox 구성(해당 cpu, memory limit 설정, 고객 코드, runtime, 실행환경 초기화 등..)
    • Placement Service

(Alb(우리한텐 안보임)가 받아 Front End로 Front End가 Worker Mgr에게 sandbox 가져오라고 명령

sandbox 있으면 Worker 가져오고,

없으면 Placement 에게 sandbox 만들 수 있는 Worker 달라고 요청, Worker를 Worker Mgr에게 전달, Worker Mgr가 sandbox에 init command 날리고 Front End에게 전달 Front End가 코드 invoke 명령어 내리고

결과가 Alb를 통해 나간다.)

→ Warm Sandbox 존재: Claim Worker 과정과, init 과정이 필요 없음! (빠르게 invoke 가능) ⇒ Warm Start

Warm Sandbox 없음: Claim Worker 과정과 init 과정 필요 (지연이 있음) ⇒ Cold Start

 

  • Asynchronous Invoke:
    • Poller: 큐에 저장된 이벤트 꺼내와서 처리되는 과정을 전부 관리
    • State Manager/Stream Tracker: Poller 서비스를 스케일링, 이벤트 소스 스트림을 관리하거나 등..
    • Leasing Service: 어떤 Poller 서비스가 어떤 이벤트를 담당할건지

(synchronous와 Front End까진 같다, Font End에서 Worker Mgr로 가면 sync / SQS로 가면 async

Front End에서 SQS로 넣게 되면 함수 결과값으로 StatusCode: 2XX 던져주고 끝.

SQS에서 Poller가 이벤트 가져와야하는데 어떤 Poller가 어떤 이벤트 담당할지 결정하는게 Leasing Service(그림엔 없음)

SQS에서 메시지를 가져온 Poller가 새로운 클라이언트가 되어서 Front End를 호출할 때 RequestResponse 방식으로 호출 - 이 뒤로는 sync와 똑같이 이루어진다.)

 

Firecracker

 

간단하게(?) WebSocket API, Lambda, DynamoDB를 사용해 서버리스 채팅 앱을 구축해보자..

https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/websocket-api-chat-app.html

해당 링크에서 cloudformation 템플릿으로 작업 후에 하나씩 살펴보자..


  • CloudFormation 생성

CloudFormation 템플릿

 

  • WebSocket API 생성

라우팅 선택 표현식 값으로 표현된 속성은 클라이언트가 API 호출할 때마다 전송되는 모든 메시지에 포함되어야 한다.

$request.body.action 의 경우, 클라이언트 요청의 body 내에 "action" 키에 mapping 된 값으로 route 한다는 의미!

 

다음으로 넘어가 $connect / $disconnect / $default 다 추가하기.

(요청과 일치하는 다른 경로가 없을 때 $default 경로를 호출)

통합 연결에서 Lambda 선택 후 각 각 맞는 함수를 넣어준다.

 

  • Test the chat API

wscat 유틸을 사용해 테스트 해보자

 

터미널 환경을 위해 cloud9을 열어주자... 2개를 만들 것!(메시지를 던지는 서버 1 / 메시지를 받는 서버 2)

wscat을 사용하기 위해선 npm이 필요!

npm -v
npm install -g wscat

publish 된 API endpoint에 연결

wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/{STAGE}

api gateway의 대시보드를 보면 websocket url 알 수 있음..

위의 명령어 입력 시 connected 가 뜨면 연결 된 것..

DynamoDB에 connect Id 가 들어간 것을 볼 수 있음

sendMessage 함수 테스트를 위해 아래와 같은 JSON 메시지 전달 (cloud9 -1)

던진 메시지를 보기 위한 것..(cloud9 -2)

두 개의 터미널(cloud9 -1,2)에서 연결을 종료 시킨 뒤 다시 dynamoDB를 확인해보면...

 

이제 하나씩 다시 살펴보자!


일단 Lambda 함수들 먼저 보면

Connect / Disconnect / Default / sendMessage 이렇게 4개의 함수가 생성되어 있다.

(물론 모든 함수들은 트리거로 위에서 생성했던 API Gateway 잡혀있음!!)

  • ConnectHandler
const AWS = require('aws-sdk');
      const ddb = new AWS.DynamoDB.DocumentClient();
      exports.handler = async function (event, context) {
        try {
          await ddb
            .put({
              TableName: process.env.table,
              Item: {
                connectionId: event.requestContext.connectionId,
              },
            })
            .promise();
        } catch (err) {
          return {
            statusCode: 500,
          };
        }
        return {
          statusCode: 200,
        };
      };

DynamoDB 테이블에 connectId 값을 넣는 작업

 

  • DisconnectHandler
const AWS = require('aws-sdk');
      const ddb = new AWS.DynamoDB.DocumentClient();
      
      exports.handler = async function (event, context) {
        await ddb
          .delete({
            TableName: process.env.table,
            Key: {
              connectionId: event.requestContext.connectionId,
            },
          })
          .promise();
        return {
          statusCode: 200,
        };
      };

DynamoDB 테이블에 connectId 값에 해당되는 record 삭제

(따라서 위 두 함수의 역할에는 기본적인 Lambda + DynamoDB 에 접근할 수 있는 권한이 필요!)

 

  • DefaultHandler
const AWS = require('aws-sdk');

      exports.handler = async function (event, context) {
        let connectionInfo;
        let connectionId = event.requestContext.connectionId;
      
        const callbackAPI = new AWS.ApiGatewayManagementApi({
          apiVersion: '2018-11-29',
          endpoint:
            event.requestContext.domainName + '/' + event.requestContext.stage,
        });
      
        try {
          connectionInfo = await callbackAPI
            .getConnection({ ConnectionId: event.requestContext.connectionId })
            .promise();
        } catch (e) {
          console.log(e);
        }
      
        connectionInfo.connectionID = connectionId;
      
        await callbackAPI
          .postToConnection({
            ConnectionId: event.requestContext.connectionId,
            Data:
              'Use the sendmessage route to send a message. Your info:' +
              JSON.stringify(connectionInfo),
          })
          .promise();
      
        return {
          statusCode: 200,
        };
      };

잘못된 경로(?)로 들어오면... 들어오는 방법 설명!

 

  • sendMessageHandler
const AWS = require('aws-sdk');
      const ddb = new AWS.DynamoDB.DocumentClient();
      
      exports.handler = async function (event, context) {
        let connections;
        try {
          connections = await ddb.scan({ TableName: process.env.table }).promise();
        } catch (err) {
          return {
            statusCode: 500,
          };
        }
        const callbackAPI = new AWS.ApiGatewayManagementApi({
          apiVersion: '2018-11-29',
          endpoint:
            event.requestContext.domainName + '/' + event.requestContext.stage,
        });
      
        const message = JSON.parse(event.body).message;
      
        const sendMessages = connections.Items.map(async ({ connectionId }) => {
          if (connectionId !== event.requestContext.connectionId) {
            try {
              await callbackAPI
                .postToConnection({ ConnectionId: connectionId, Data: message })
                .promise();
            } catch (e) {
              console.log(e);
            }
          }
        });
      
        try {
          await Promise.all(sendMessages);
        } catch (e) {
          console.log(e);
          return {
            statusCode: 500,
          };
        }
      
        return { statusCode: 200 };
      };

메시지 주고 받게...

(위 두 함수들은 연결된 클라이언트들 호출을 위한 "execute-api:ManageConnections" 권한이 필요하다.)

 

❗ 근데.. 위는 정말 간단하게 메시지 주고 받는 거고... 실제로 사용할 땐 어떤 식으로 사용하는지 궁금하다... ❗

그리고 웹소켓 연결 후 주고받는 메시지들은 어떻게 관리가 되는지, 유실은 없는지 등의 궁금증이 생기네...?

'Cloud > AWS' 카테고리의 다른 글

[AWS] Serverless Service - Lambda 편 (2)  (0) 2022.11.05
[AWS] Serverless Service - Lambda 편 (1)  (0) 2022.11.05
[AWS] Lambda Warm Start / Cold Start  (0) 2022.10.28
[AWS] Fan-Out 시나리오 -1(easy)  (0) 2022.10.11
[AWS] Event-Driven Architecture  (0) 2022.10.11

제일 중요한(뇌피셜) 개념을 다뤄보려 한다..

사실 내가 이걸 먼저 보려고 했었는데...😂


Warm Start / Cold Start

Lambda 동작 순서

lambda code 작성 & 배포 → Load Code as .zip → Download to container → Bootstrap code(compile, load code to container) → run code

https://velog.io/@milkcoke/Lambda-Cold-start

(Bootstrap: 특정 언어에 대해 컴파일하여 기록하는 것)

 

Warm Start

  • 이미 실행 준비가 완료된 상태

 

Cold Start

  • 배포 패키지의 크기와 코드 실행 시간 및 코드의 초기화 시간에 따라 새 실행 환경으로 호출을 라우팅할 때 지연 시간이 발생
  • 쉽게 얘기하면 잠시 절전 모드의 컴퓨터를 키는 느낌 이랄까...
  • 발생 원인
    • 인스턴스 자체가 떠있지 않은 상황:
      • 컨테이너를 실행시키기 위해선 인스턴스가 활성화되어 있어야하는데 요청과 요청 사이의 간격이 너무 크거나, 최초의 요청이여서 인스턴스가 내려가있을 수 있다.
    • ENI 적용 시간:
      • Lambda도 결국엔 서버이다. 통신을 위해선 ENI가 컨테이너에 붙어야 한다. 특히, VPC를 사용하는 Lambda의 경우 반드시 적용된다.
      • 예를 들면, DB와 연결할 때, DB는 내부 서버에게만 열어야하기 때문에..
      • 이를 해결하려면 VPC + ENI의 성능을 향상시켜야 한다!
    • Dwell Time:
      • lambda 구조에서도 봤듯이 요청이 들어오면 컨테이너로 바로 들어가는게 아니라 큐에 쌓이게 된다.
      • 큐에서 요청을 꺼내 컨테이너로 보내는 시간: Dwell Time
      • 이 시간은 언제 길어질까?
        • 한 계정에 동시에 띄울 수 있는 최대 컨테이너 수(Max Concurrent Executions)는 제한되어 있음
        • 요청이 엄청 들어왔다고 가정했을 때, 컨테이너가 어떻게 증가할까?
          • 아마 컨테이너가 하나씩 선형적으로 증가하는 그림은 아닐듯
        • 한 계정에는 하나의 서비스를 만들기 위해 여러 종류의 Lambda를 사용하게 된다. 각 Lambda에서 생성될 수 있는 컨테이너 수를 제한하는데(Reserved Concurrent Executions) 이 설정보다 많은 요청이 동시에 들어오면 Throttle 발생!

 

Cold Start 해결 방법

  • Lambda 메모리 늘리기:
    • 메모리를 올리면 인스턴스의 사양(cpu)이 올라가 처리 속도 자체가 빨라질 수 있다고 한다.
    • but, 비용 고민: (메모리별 가격) * (요청이 처리되는 시간) * (요청 수)
    • 메모리가 올라가 요청이 처리되는 시간이 줄어들 수도 있고 그냥 메모리만 올라가면 음... 계산기 잘 두드려라
  • lambda 컨테이너 재사용:
    • 한번 호출되고, 다음 호출이 5분 이내인 경우 그 후속 호출에 컨테이너 재사용
    • 자체적인 Warm Start 구성이라고 생각해도 될듯?
    • 여기서 꼼수를 쓰자.. 요청 없어도 5분마다 강제 실행을 시키는 것!
      • CW 활용, Route 53 HealthCheck를 람다 API Gateway Endpoint 설정 등...
      • but, 쓸데 없는 지출이 증가됨
    • 시간이 오래 걸리는 코드는 global로 선언하는 게 좋다
      • 재사용할 때 돌아가지 않도록!
      • 예를들면 db_connection과 같은 작업?
더보기

warm-cold-start-test func

let isWarmStart = false;

exports.handler = async () => {
  let message;
 
  if (isWarmStart) {
    message = "warm start";
  } else {
    message = "cold start";
    isWarmStart = true;
  }
 
  return {
    statusCode: 200,
    body: JSON.stringify({ message }),
  };
};

그냥 아주 단순한 코드.. global로 isWarmStart 변수 선언하고 handler 실행되면 true로 값 바꿈.

처음에 실행한 결과값

그 이후 두번째 실행한 결과값

 

일정 시간 뒤에 다시 실행해보면 cold start를 다시 출력할 것이다..

 

'Cloud > AWS' 카테고리의 다른 글

[AWS] Serverless Service - Lambda 편 (1)  (0) 2022.11.05
[AWS] API Gateway - Websocket API  (0) 2022.11.01
[AWS] Fan-Out 시나리오 -1(easy)  (0) 2022.10.11
[AWS] Event-Driven Architecture  (0) 2022.10.11
[AWS] Lambda Layer  (0) 2022.10.07
SNS topic에 게시된 메시지가 복제되어
Kinesis Data Firehose 전송 스트림, SQS 대기열, HTTP(S) 엔드포인트 및 Lambda 함수와 같은 여러 엔드포인트로 푸시되는 경우

https://docs.aws.amazon.com/ko_kr/sns/latest/dg/sns-common-scenarios.html

 

이 중에서 나는 SNS - SQS 팬아웃 구조를 실습할 것이다!


(상황 설정: 온라인 상점에 주문이 접수될 때마다 주제에 대한 SNS 메시지를 전송하는 클라우드 네이티브 앱 개발 중

→ 해당 주제를 구독하는 SQS 대기열은 새 주문에 대해 각각 동일한 알림을 수신할 것)

 

  • SNS Topic 생성

 

  • SQS 대기열 생성

각각 이름이 Orders-for-Inventory / Orders-for-Analytics 인 Queue 생성

 

  • 대기열에서 주제 구독

 

  • 주제에 메시지 게시

 

  • 구독 확인

각 각 대기열 들어가서 폴링 하기

 

❗ 진짜 간단하게 SNS + SQS 구조를 나타낸 것... ❗

 

'Cloud > AWS' 카테고리의 다른 글

[AWS] API Gateway - Websocket API  (0) 2022.11.01
[AWS] Lambda Warm Start / Cold Start  (0) 2022.10.28
[AWS] Event-Driven Architecture  (0) 2022.10.11
[AWS] Lambda Layer  (0) 2022.10.07
[AWS] Amazon Rekognition 개념  (1) 2022.10.05
이벤트를 사용하여 분리된 서비스를 서로 트리거하고 통신하며
마이크로 서비스로 구축된 현대적 애플리케이션에서는 일반적


이벤트: 상태의 변화 또는 업데이트, 식별자

(이벤트 생산자 → 이벤트 라우터 → 이벤트 소비자)

 

사용 사례

  • 계정 간, 리전 간 데이터 복제
    • 이벤트 라우터를 사용하여 시스템 간에 데이터를 전송하면 다른 팀과 독립적으로 서비스를 개발하고 크기 조정하고 배포 가능
  • 리소스 상태 모니터링 및 알림
  • 팬아웃 및 병렬 처리
    • 이벤트에 반응하여 작동해야 하는 시스템이 많은 경우
    • 라우터가 이벤트를 시스템에 푸시하면, 각 시스템이 각각 다른 목적으로 이벤트를 병렬로 처리 가능
  • 이기종 시스템의 통합
    • 시스템이 서로 다른 스택에서 실행 중인 경우 결합하지 않은 상태로 시스템 간 정보 공유 가능

 

고려 사항

  • 이벤트 소스의 내구성. (이벤트 소스를 신뢰할 수 있어야 하며 전송이 보장되어야 함)
  • 성능 제어 요구 사항. (애플리케이션이 이벤트 라우터의 비동기 특성을 처리할 수 있어야 함)
  • 이벤트 흐름 추적. (모니터링 서비스를 통한 동적 추적은 가능, 코드 분석을 통한 정적 추적은 불가능)
  • 이벤트 소스의 데이터. (상태를 다시 빌드해야할 경우 이벤트 소스가 중복 제거되고 순서가 지정되어야 함)

 

여기까지가 간단한 아키텍처 관련 내용 이였고.. 이벤트 라우터 역할의 서비스를 알아보자.

간단하게 알아보고 자세한 내용은 나중에 콘솔 뜯을 때 확인하자...😅


Amazon EventBridge

  • AWS, 기존 시스템 또는 SaaS 애플리케이션에서 대규모 이벤트 기반 애플리케이션 구축
  • 이벤트 수신, 필터링, 변환, 라우팅 및 전송 기능을 제공하는 serverless event bus
  • 스키마
    • 전송되는 이벤트의 구조를 정의
    • 사용자 정의 스키마 생성 또는 업로드 / 이벤트 버스 이벤트에서 직접 스키마 유추
    • API를 사용해 IDE에서 직접 스키마에 대한 코드 바인딩으로 작업 및 관리 가능
  • 이벤트 버스
    • 이벤트를 수신하는 파이프라인
    • 규칙을 특정 이벤트 버스와 연결(해당 이벤트 버스에서 수신한 이벤트에만 규칙이 적용됨)
      • 각 이벤트 버스에 대해 최대 300개의 규칙
    • Default event bus: AWS 서비스에서 이벤트 수신
    • Custom event bus: 다른 계정/리전으로 이벤트를 보내거나 이벤트 수신
    • Partner event bus: SaaS 파트너로부터 이벤트 수신
  • 규칙
    • 들어오는 이벤트를 일치시키고 처리를 위해 대상으로 보냄
    • 이벤트 패턴(이벤트 구조와 규칙이 일치하는 필드 정의) / 일정(일정한 간격으로 작업 수행) 기반
    • 규칙에 대해 최대 5개의 대상 정의 가능
  • 이벤트 아카이브 및 리플레이
    • 아카이브 생성 시 이벤트 패턴을 지정하여 아카이브로 보낼 이벤트 결정 
    • 이벤트를 리플레이하여 오류를 복구, 애플리케이션의 새 기능을 검증 가능
    • 이벤트 버스에 게시되는 이벤트와 아카이브에 도착하는 이벤트 사이에 지연 있을 수 있음
      • 모든 이벤트가 리플레이 되도록 이벤트 리플레이를 10분 동안 지연하는 것이 좋음
  • 기능

Amazon Simple Notification Service (SNS)

  • 애플리케이션 간(A2A) 및 애플리케이션과 사용자 간(A2P) 통신 모두를 위한 완전 관리형 메시징 서비스
  • SNS topic을 사용하여 게시자 시스템은 병렬 처리를 위해 SQS, Lambda 등 많은 구독자 시스템으로 메시지를 팬아웃
    • Pub/Sub 말고도 SMS, 모바일 푸시 가능
  • 팬아웃 시나리오가 메인인 느낌.. 이는 실습과 함께 다뤄볼 예정!😁

'Cloud > AWS' 카테고리의 다른 글

[AWS] Lambda Warm Start / Cold Start  (0) 2022.10.28
[AWS] Fan-Out 시나리오 -1(easy)  (0) 2022.10.11
[AWS] Lambda Layer  (0) 2022.10.07
[AWS] Amazon Rekognition 개념  (1) 2022.10.05
[AWS] Amazon Rekognition 실습  (0) 2022.10.04

+ Recent posts