AWS Lambda는 대표적인 Serverless 서비스이다. 코드를 실행하려면 일반적으로 서버가 필요하고, 이 서버를 운영/관리해야 한다. Serverless는 서버의 운영/관리를 클라우드에서 해준다는 것이다. 우리는 우리의 코드만 신경 쓰면 된다.
언제 AWS Lambda를 사용해야 할까? 라는 질문에는 여러 가지 고려 사항이 있겠지만, AWS Lambda의 요금 정책이 어느 정도 답안을 줄 수 있다. Lambda의 요금은 CPU, 메모리 사양과 요청 수, 실행한 시간에 따라 결정된다. 따라서 많이 호출될수록, 오래 실행될수록 많은 요금이 많이 발생한다. 대신 드물게 요청되면서 짧은 시간 안에 처리되는 로직이라면 직접 서버를 프로비저닝하여 애플리케이션을 운영하는 것보다 노력과 비용을 절감할 수 있다.
AWS Lambda의 기능, 사용 방법을 더 잘 이해하기 위해 네트워크 비용 절감을 위해 업로드된 프로필 사진의 썸네일 제작
이라는 예제를 통해 살펴보자.
주의 사항
트리거로 사용한 버킷과 Lambda에서 생성하는 버킷을 다르게 설정하자. 동일하게 설정한다면, 트리거가 무한으로 작동하여 Lambda 요금이 정말 많이 나올 수 있다.
Lambda에서는 API Gateway, S3 Event, SQS 등 다양한 트리거를 설정할 수 있다.
gook-app-image
버킷의 모든 파일 생성 이벤트를 트리거로 등록하였다.
(아래에는 트리거를 발생시킬 S3 버킷과 코드에서 대상으로 하는 S3 버킷을 동일하게 설정하면 재귀적인 호출로 Lambda 사용량이 높아질 수 있다는 경고를 보여주고 있다.)
import boto3
import json
import os
from PIL import Image
from io import BytesIO
def lambda_handler(event, context):
print(json.dumps(event))
for record in event['Records']:
source_bucket = record['s3']['bucket']['name']
target_bucket = os.environ.get('THUMBNAIL_BUCKET')
key = record['s3']['object']['key']
file_extension = key.split('.')[-1].lower()
if file_extension in ['jpg', 'jpeg', 'png', 'gif']:
generate_thumbnail(source_bucket, target_bucket, key)
else:
print("Not supported image file")
def generate_thumbnail(source_bucket, target_bucket, path):
s3_client = boto3.client('s3')
response = s3_client.get_object(Bucket=source_bucket, Key=path)
image_stream = BytesIO(response['Body'].read())
thumbnail_stream = extract_thumbnail(image_stream)
s3_client.put_object(Body=thumbnail_stream, Bucket=target_bucket, Key=path)
print(f"Thumbnail generated and saved to {target_bucket}/{path}")
def extract_thumbnail(image_stream):
image = Image.open(image_stream)
image.thumbnail((100, 100))
thumbnail_bytes = BytesIO()
image.save(thumbnail_bytes, format='jpeg')
thumbnail_bytes.seek(0)
return thumbnail_bytes
예제 코드에서 대상 버킷에 대한 이름은 환경 변수를 통해 입력받는다. 환경 변수는 Lambda 함수 구성 > 환경 변수
에서 설정할 수 있다.
우리는 개발 환경이나 서버에서 Python 코드를 실행할 때 PIP를 이용해 필요한 라이브러리를 설치한다. Lambda에서 외부 라이브러리를 설치하는 방법을 알아보자.
boto3 라이브러리는 AWS Lambda에서 내장하고 있어 별도로 설치하지 않아도 된다.
pip install pillow -t ./python
예제 코드에서는 boto3를 제외하고 pillow라는 외부 라이브러리가 필요하다. Lambda에 추가하기 위해, 동일한 리눅스 환경에서 위 명령어를 통해 python 폴더 아래에 필요한 라이브러리를 설치하자.
그리고 필요한 라이브러리들이 설치된 이 python 폴더를 압축한다.
AWS Cloudshell
바로 접속할 수 있는 리눅스 서버가 없다면, AWS의 Cloudshell을 활용할 수 있다. Cloudshell은 무료로 사용할 수 있지만, AWS 네트워크 비용은 발생할 수 있다.
https://docs.aws.amazon.com/ko_kr/cloudshell/latest/userguide/shell-pricing.html
앞에서 압축한 zip 파일을 등록하여 Lambda 계층을 생성한다. 런타임을 라이브러리를 설치한 Python 버전과 맞춰주자.
Lambda 함수에서 Layers를 선택하고 [Add a layer]를 통해 앞에서 생성한 계층을 등록할 수 있다. 사용자 지정 계층
에서 선택하거나 ARN을 직접 지정하면 된다.
gook-app-image
버킷에 이미지를 업로드하여, 예제 코드를 실행하면, S3 버킷에서 파일을 읽는 권한이 없어 Access Denied 에러가 발생한 것을 확인할 수 있다. S3 버킷에 있는 파일을 읽고, 쓰기 위한 권한이 필요하다.
Lambda 함수의 구성 > 권한
으로 들어가면 할당된 역할을 확인할 수 있다.
Lambda 함수를 생성할 때
기본 Lambda 권한을 가진 새 역할 생성
옵션을 사용하였다면,${함수명}-xxx
라는 이름으로 역할이 생성되며, 기본적으로 함수의 로깅을 위한 권한 정책이 할당된다.
우리는 gook-app-image
버킷에서 파일을 가져오는 권한과 gook-app-thumbnail
버킷에 파일을 업로드하는 권한이 필요하므로 역할에 다음과 같은 정책을 추가해 주자.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::gook-app-image/**"
},
{
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::gook-app-thumbnail/**"
}
]
}
각 Statement는 제한된 리소스(Resource) 범위에 대해서 특정 작업(Action)을 허용/거부(Effect) 할지 나타낸다.
이제 권한을 추가했으니 gook-app-image
버킷에 이미지를 업로드하여 Lambda를 실행해 보자.
Lambda 함수가 에러 없이 잘 실행되었다.
gook-app-thumbnail
버킷에 이미지가 잘 생성된 것을 확인할 수 있다.
200 x 200 크기의 이미지에서 100 x 100 크기의 썸네일이 잘 생성되었다.
AWS Lambda가 무엇이고, 언제 어떻게 사용하는지 간단하게 알아보았다. 다음에는 트리거 유형의 차이와 Lambda 로그 확인 방법에 대해서 알아보자.