전통적으로 애플리케이션은 서버에서 실행된다. 서버는 물리적 서버 또는 물리적 서버에서 실행되는 가상 환경일 수 있지만 여전히 서버를 구입하고 프로비저닝해야 하고 용량을 관리해야 한다. 반면에 AWS Lambda는 서버를 미리 할당할 필요 없이 서버리스 코드를 실행할 수 있다. 코드를 제공하고 트리거를 정의하기만 하면 일주일에 한번 또는 초당 수백 번 등 필요할 때마다 함수가 실행될 수 있으며, 사용한만큼만 지불하면 된다. 이 실습에서는 파일을 Amazon S3에 업로드 할 때 AWS Lambda 함수를 트리거하는 방법을 설명한다. 파일은 Amazon DynamoDB 테이블에 로드되며, DynamoDB에서 데이터를 직접 가져오는 대시보드 페이지에서 데이터를 볼 수 있다. 이 솔루션은 완전한 서버리스이고 자동으로 확장 가능하며 비용이 매우 저렴하다. 시스템은 Amazon EC2를 사용하지 않는다. 시스템은 사용할 때는 자동으로 확장되고 사용하지 않을 때는 거의 비용이 발생하지 않는다. (데이터 스토리지에 불과 몇 센트) AWS Lambda는 서버를 프로비저닝하거나 관리하지 않고도 코드를 실행할 수 있게 해주는 서버리스 컴퓨팅 서비스이다. 서버리스를 사용하면 서버를 고려하지 않고 애플리케이션과 서비스를 구축하고 실행할 수 있다.
시나리오
현재 재고 추적 시스템을 만드는 중이다. 전 세계 매장이 재고 파일을 Amazon S3에 업로드하게 되는데, 팀은 재고 수준을 보고 재고 수준이 낮을 때 알림을 전송할 수 있기를 바란다. (실습에서 대시보드 앱과 Cognito는 미리 프로비저닝 되어 있다.)
이 시나리오의 워크플로우는 다음과 같다. 재고 파일을 Amazon S3 버킷에 업로드한다. 그러면 파일을 읽고 항목을 Amazon DynamoDB 테이블에 삽입할 AWS Lambda 함수가 트리거 된다. 서버리스 웹 기반 대시보드 애플리케이션이 Amazon Cognito를 사용하여 AWS에 인증한 다음 Dynamo DB테이블에 대한 엑세스 권한을 얻어 재고 수준을 표시한다. 다른 AWS Lambda 함수가 DynamoDB테이블에서 업데이트를 수신하고 재고품이 떨어지면 Amazon Simple Notification Services(SNS) topic(Amazon SNS 알림)에 메시지를 보낸다. 그런 다음 Amazon SNS가 사용자에게 SMS 또는 이메일 알림을 보내 추가 재고 요청을 한다.
Lambda 함수를 생성해 데이터를 로드하겠다. 재고 파일을 처리할 AWS Lambda 함수를 만든다. Lambda 함수는 파일을 읽어 Amazon DynamoDB 테이블에 정보를 삽입한다.
먼저 Lambda서비스를 클릭한다.
블루프린트는 Lambda 함수 작성을 위한 코드 템플릿이다. 실습에서는 사전 작성된 Lambda 함수를 제공하므로 처음부터 작성하게 된다.
이름을 Load-Inventory로하고 런타임은 Python3.7로 하고 역할은 기존 역할을 선택한 후 Lambda-Load-Inventory-Role로 설정한다. 이 역할은 Lambda 함수에 실행 권한을 부여하므로 Lambda함수가 Amazon S3 및 Amazon DynamoDB에 엑세스할 수 있다.
무사히 람다 함수가 생성되었다.
다음 코드를 복사해 Function code(함수 코드)섹션까지 붙여 넣는다.(기존의 것 삭제!)
# Load-Inventory Lambda 함수
#
# 이 함수는 Amazon S3 버킷에서 생성 중인 객체에 의해 트리거됩니다.
# 파일이 다운로드되고 각 행이 DynamoDB 테이블에 삽입됩니다.
import json, urllib, boto3, csv
# S3 및 DynamoDB에 연결
s3 = boto3.resource('s3')
dynamodb = boto3.resource('dynamodb')
# DynamoDB 테이블에 연결
inventoryTable = dynamodb.Table('Inventory');
# 이 핸들러는 Lambda 함수가 트리거될 때마다 실행됩니다.
def lambda_handler(event, context):
# 이벤트 디버그 로그에서 수신 이벤트 표시
print("Event received by Lambda function: " + json.dumps(event, indent=2))
# 이벤트에서 버킷과 객체 키를 가져옵니다
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'])
localFilename = '/tmp/inventory.txt'
# S3에서 로컬 파일 시스템으로 파일을 다운로드합니다
try:
s3.meta.client.download_file(bucket, key, localFilename)
except Exception as e:
print(e)
print('Error getting object {} from bucket {}. Make sure they exist and your bucket is in the same region as this function.'.format(key, bucket))
raise e
# 재고 CSV 파일을 읽습니다.
with open(localFilename) as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
# 파일의 각 행 읽기
rowCount = 0
for row in reader:
rowCount += 1
# 디버그 로그에서 행 표시
print(row['store'], row['item'], row['count'])
try:
# 매장, 품목, 개수를 재고 테이블에 삽입
inventoryTable.put_item(
Item={
'Store': row['store'],
'Item': row['item'],
'Count': int(row['count'])})
except Exception as e:
print(e)
print("Unable to insert data into DynamoDB table".format(e))
# 완료!
return "%d counts inserted" % rowCount
페이지 상단의 저장을 클릭한다. 다음으로 파일이 업로드될 때 람다 함수가 트리거하도록 Amazon S3를 구성한다.
Amazon S3 이벤트를 구성하겠다. 전 세계 매장이 재고 추적 시스템에 로드할 재고 파일을 제공한다. 매장은 FTP를 통하지 않고 Amazon S3에 파일을 직접 업로드할 수 있다. 이는 웹 페이지나 스크립트 또는 프로그램의 일부를 통해 가능하다. 파일이 수신되면 AWS Lambda함수가 트리거되어 DynamoDB테이블에 재고를 자동으로 로드한다.
S3 서비스로 이동한 후 버킷을 생성한다.
각 버킷의 이름은 고유해야 하므로 버킷 이름을 inventory-168이라고 설정하겠다.
버킷에 대한 public access는 기본적으로 차단된다. 재고 확인을 위한 웹 브라우저 접속을 위해 파일을 공개적으로 액세스할 수 있어야 하므로 액세스를 허용한다. (block 해제!)
나머지는 기본설정을 설정한 뒤 버킷을 생성한다.
Inventory-168버킷이 무사히 생성되었음을 확인할 수 있다.
버킷 이름을 클릭한 후 Properties(속성)탭을 클릭한다.
아래로 스크롤하여 고급설정으로 간 후 Events(이벤트)를 클릭한다. S3 버킷에서 객체가 생성될 때 트리거하도록 이벤트를 구성한다.
이름을 Load-Inventory으로 하고, 이벤트를 모든 객체 생성이벤트로 설정한다.
전송 대상을 람다 함수로 설정하고 생성한 람다 이름인 Load-Inventory를 클릭 후 저장!
알림 이벤트 하나가 무사히 Active되었다. 이렇게 하면 버킷에서 객체가 생성될 때마다 앞서 만든 Load-Inventory Lambda함수를 트리거하라고 Amazon S3에게 알린다. 버킷이 재고 파일을 수신할 준비가 되었다. (** S3의 트리거를 추가해줘야 하지만 여기서는 S3의 이벤트 기능을 사용하여서 구성하였다. **)
이제 로딩 프로세스를 테스트할 준비가 되었다. 재고 파일을 업로드한 다음 성공적으로 로드되었는지 확인한다. zip파일을 하나 다운로드하고 압축을 풀었다.
Inventory-berllin.csv파일을 하나 열어서 내용을 보았다.
S3 메뉴로 돌아와 버킷의 Overview(개요)를 선택한 후 업로드를 선택하여 CSV파일 중 하나를 버킷에 업로드하였다. (Inventory-berlin파일을 선택한 후 업로드!) Amazon S3가 자동으로 Lambda 함수를 트리거한다. 그러면 이 함수가 DynamoDB테이블에 데이터를 로드한다. 결과를 볼 수 있도록 서버리스 대시보드 애플리케이션이 제공되었다.
무사히 파일이 업로드되었다.
대시보드 URL을 복사한 후 새 웹 브라우저 탭을 열어 복사한 URL에 들어갔다.
대시보드 애플리케이션이 표시되어 버킷에 로드된 재고 데이터를 보여준다. 데이터가 DynamoDB에서 검색되고 있다. 그러므로 Lambda 함수가 성공적을 트리거 되었다. 이 대시보드 애플리케이션은 Amazon S3에서 정적 웹 페이지로 제공된다. 대시보드는 Amazon Cognito를 통해 anonymous user로 인증된다. 이는 대시보드가 DynamoDB에서 데이터를 검색할 수 있는 충분한 권한을 제공한다.
DynamoDB 테이블 내에서 데이터를 볼 수도 있다. 서비스에서 DynamoDB를 클릭한다.
탐색 창에서 Tables를 클릭한다.
Inventory(재고) 이름을 클릭한 후 items탭에 들어간다. 재고 파일의 데이터가 표시되어 매장, 항목 및 재고 수량을 보여준다.
매장에서 항목이 품절될 경우 재고 관리 직원에 알림을 보내려고 한다. 이 서버리스 알림 기능에 Amazon Simple Notification Services(SNS)를 사용한다.
Amazon SNS는 구독 엔드포인트와 및 클라이언트로 메시지를 전달하기 위한 유연한 완전 관리형 게시/구독 메시징 및 모바일 알림 서비스이다. SNS를 활용하면 분산 시스템 및 서비스와 모바일 디바이스를 비롯한 많은 구독자에게 메시지를 팬 아웃할 수 있다.
Simple Notification Service를 클릭한다.
주제 생성을 클릭한다.
주제 이름과 표시이름을 NoStock(재고 없음)으로 한 후 주제를 생성한다.
알림을 수신하려면 주제를 구독해야 한다. SMS 및 이메일과 같이 여러 방법을 통해 알림을 수신하도록 구독할 수 있다. 생성된 주제를 선택한 다음 Actions(작업)을 클릭해 주제를 구독할 수 있다. 구독 생성을 클릭한다.
프로토콜은 SMS, 이메일 등 여러가지가 있다 SMS로 하면 번호를 입력하면 된다. 번호는 국제 전화번호 형식으로 입력한다. (만약 010-1234-1234이면 821012341234로 입력한다.)
알림을 전송하는 Lambda함수를 생성해야 한다. 기존의 Load-Inventory Lambda 함수를 수정하여 파일이 로드되는 동안 재고 수준을 확인할 수 있지만, 이는 아키텍처 모범 사례가 아니다. Load-Inventory함수를 비즈니스 로직으로 과부화시키는 대신 DynamoDB 테이블에 데이터가 로드될 때마다 트리거 되는 다른 Lambda함수를 생성할 것이다. 이 함수는 DynamoDB Stream에 의해 트리거된다. 각 람다함수는 특정 기능 하나만 수행한다. 이렇게 되면 코드가 간단해지고 관리가 편해진다. 추가 람다 함수를 생성하여 비즈니스 로직을 추가할 수 있다. 각 함수는 독립적으로 작동하므로 기존 기능에 영향을 받지 않는다. 과제에서는 DynamoDB 테이블에 로드되는 재고를 모니터링하는 다른 Lambda 함수를 생성한다. 이 함수는 항목 품절을 확인할 경우 앞서 생성한 Amazon SNS 주제를 통해 알림을 전송한다.
Lambda 서비스를 들어간 후 함수 생성을 클릭하고 이름과 런타임을 설정한다.
기존 역할의 Lambda-Check-Stock-Role 선택! (Amazon SNS로 알림을 보낼 수 있는 권한!)
함수 코드 섹션에서 기존의 표시되는 코드를 삭제한 후 아래의 코드를 복사해서 넣어준다. 이 코드는 수신 레코드를 루핑하며 재고 수량이 0일경우 NoStock SNS 주제에 메시지를 전송한다.
# Stock Check Lambda function
#
# 이 함수는 재고 DynamoDB 테이블에 값이 삽입될 경우 트리거됩니다.
# 재고 수량을 확인한 후 항목이 품절일 경우 SNS 주제로 알림을 전송합니다.
import json, boto3
# 이 핸들러는 Lambda 함수가 트리거될 때마다 실행됩니다.!!!!
def lambda_handler(event, context):
# 이벤트 디버그 로그에서 수신 이벤트 표시
print("Event received by Lambda function: " + json.dumps(event, indent=2))
# 추가된 각 재고 항목에서 수량이 0인지 확인합니다.
for record in event['Records']:
newImage = record['dynamodb'].get('NewImage', None)
if newImage:
count = int(record['dynamodb']['NewImage']['Count']['N'])
if count == 0:
store = record['dynamodb']['NewImage']['Store']['S']
item = record['dynamodb']['NewImage']['Item']['S']
# Construct message to be sent
message = store + ' is out of stock of ' + item
print(message)
# Connect to SNS
sns = boto3.client('sns')
alertTopic = 'NoStock'
snsTopicArn = [t['TopicArn'] for t in sns.list_topics()['Topics']
if t['TopicArn'].lower().endswith(':' + alertTopic.lower())][0]
# Send message to SNS (보내기)
sns.publish(
TopicArn=snsTopicArn,
Message=message,
Subject='Inventory Alert!',
MessageStructure='raw'
)
# Finished!
return 'Successfully processed {} records.'.format(len(event['Records']))
이제 DynamoDB 테이블의 Inventory테이블에 데이터가 추가될 때마다 함수가 트리거 되로록 함수를 구성한다.
트리거를 추가하기 위해 Add trigger를 클릭한다. (DB와 오른쪽 람다 함수 연결)
트리거 추가 섹션에서 DynamoDB를 클릭한 후 트리거 구성 섹션에서 DB Table을 Inventory를 클릭하고 Enabled trigger을 체크한 후 추가한다.
페이지 상단의 저장을 클릭한다.
이제 시스템을 테스트를 해야한다. 이제 Amazon S3에 재고를 업로드한다. 그러면 원래의 Load-Inventory함수가 트리거된다. 이 함수는 DynamoDB에 데이터를 로드하고 그러면 새 Check-Stock Lambda함수가 트리거된다. 이 Lambda함수는 재고가 0인 항목을 감지하면 SMS 도는 이메일을 통해 Amazon SNS에 메시지를 전송한다.
서비스에서 S3를 클릭한다.
Inventory-168의 이름을 클릭한다.
다른 재고 파일을 업로드하겠다.
calcutta라는 파일을 업로드했다. 무사히 업로드가 되었다.
인벤토리 시스템 대시보드로 돌아가 페이지를 새로 고친다. 이제 Store(매장) 풀다운 메뉴를 사용해 양쪽 매장의 재고를 볼 수 있다.
또한 매장에서 한 항목이 품절임을 나타내는 알림을 SMS/이메일 통해 수신한다. 구독 생성할 떄 SMS로 설정하였고 번호는 나의 번호를 입력하였다. 그 결과 Calcutta Echo(2 nd Gen)이 재고가 부족하다고 핸드폰으로 문자가 옴을 확인할 수 있었다.
(핸드폰 화면)
'클라우드 > Public Cloud(Naver, Amazon)' 카테고리의 다른 글
nginx 컨테이너 이미지 생성 후 ECR로 push하기 (0) | 2019.10.25 |
---|---|
aws lambda (dev-1) (0) | 2019.10.09 |
aws CloudFormation을 사용한 인프라 배포 자동화 (Arch-5) (0) | 2019.09.22 |
aws 고가용성 환경 생성 (Arch-4) (0) | 2019.09.22 |
aws VPC 피어링 (Arch-3) (0) | 2019.09.22 |