단일 목적의 상태 없는 함수
작성자동 크기 조정, 기본 제공 고가용성 및 종량제 결제 모델을 제공하여 민첩성을 개선하고 비용을 최적화 ⇒ 용량 프로비저닝 및 패치 적용과 같은 인프라 관리 작업 필요 X
사용자를 위한 코드를 작성하는데 집중할 수 있다.
서버리스 애플리케이션은 AWS Lambda에서 사용 가능
AWS Lambda > 함수 > 함수 생성 버튼 클릭
코드 탭 > 소스 코드 수정 > Deploy 버튼 클릭
코드 탭 > 코드 소스 > Test 버튼 클릭
이벤트 설정 후 저장 버튼 클릭
모니터링 탭 > CloudWatch Logs 보기 버튼 클릭
Lambda > 함수 > 삭제할 함수 체크 > 작업 > 삭제
핸들러 함수란?
해당 람다 함수의 시작점을 말한다.
핸들러 정보
Lambda > 함수 > 런타임 설정 부분 확인
S3 put 확인해보기
{
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "us-east-1",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "EXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "EXAMPLE123456789",
"x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": { // 버킷 정보
"name": "example-bucket", // 버킷 이름
"ownerIdentity": {
"principalId": "EXAMPLE"
},
"arn": "arn:aws:s3:::example-bucket"
},
"object": { // 객체 정보
"key": "test%2Fkey", // 파일(객체) 이름
"size": 1024,
"eTag": "0123456789abcdef0123456789abcdef",
"sequencer": "0A1B2C3D4E5F678901"
}
}
}
]
}
VSCode에서 파이썬 파일 생성
소스 코드 작성 전 터미널 열어서 필요한 라이브러리 설치
# Python용 AWS SDK(boto3) 모듈 설치
$ pip install boto3
# 필로우 라이브러리 설치
$ pip install pillow
소스코드 작성 > 함수 소스 코드에 새로 작성한 내용을 붙여넣기
# Python 용 AWS SDK
# https://aws.amazon.com/ko/sdk-for-python/
import boto3
import os
import sys
import uuid
from urllib.parse import unquote_plus
# https://pypi.org/project/Pillow/
from PIL import Image
import PIL.Image
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html
s3_client = boto3.client('s3')
# image_path의 1/2 크기의 이미지를 만들어서 resized_path에 저장
def resize_image(image_path, resized_path):
with Image.open(image_path) as image:
image.thumbnail(tuple(n/2 for n in image.size))
image.save(resized_path)
# 람다 핸들러 함수
def lambda_handler(event, context):
for record in event['Records']:
bucket = record['s3']['bucket']['name'] # example-bucket
key = record['s3']['object']['key'] # test%2Fkey <= test/key 값을 URL 인코딩한 값
tmpkey = key.replace('/', '') # path/file 형식을 pathfile 형식으로 변경
# S3 버킷으로부터 파일을 가져와서 저장할 경로와 (S3 버킷으로 전달할) 썸네일 이미지를 저장할 경로를 지정
download_path = '/tmp/{}{}'.format(uuid.uuid1(), tmpkey) # /tmp/uuidpathfile
upload_path = '/tmp/resized-{}'.format(tmpkey) # /tmp/resized-pathfile
# S3 버킷으로부터 파일을 다운로드
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/download_file.html
s3_client.download_file(bucket, key, download_path)
# 다운로드한 파일의 크기를 반으로 축소 = 썸네일 생성
resize_image(download_path, upload_path)
# 썸네일 이미지를 S3 버킷에 업로드
# 다운로드버킷이름-resized 이름의 버킷에 원본 파일명과 동일한 파일명으로 업로드
# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3/client/upload_file.html
s3_client.upload_file(upload_path, '{}-resized'.format(bucket), key)
PIL 모듈이 존재하지 않기 때문에 오류가 발생
⇒ 파이썬 기본 라이브러리와 AWS SDK를 제외한 외부 라이브러리는 직접 추가해 줘야 함
참조
https://pillow.readthedocs.io/en/stable/installation.html
Lambda > 함수 클릭 > 코드 탭 > 런타임 설정 항목에서 편집 버튼 클릭 > 기존에 런타임 Python 3.7
→ Python 3.9
로 변경
EC2 > 인스턴스 > 인스턴스 생성
인스턴스 생성 확인
SSH 로그인
운영체제에 맞는 파이썬 모듈 만들기
Bitvise SSH Client > New terminal console
$ sudo yum update -y
$ sudo su
$ yum install gcc bzip2-devel ncurses-devel gdbm-devel xz-devel sqlite-devel openssl-devel tk-devel uuid-devel readline-devel zlib-devel libffi-devel
$ cd /home/ec2-user/
$ wget https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tar.xz
$ tar -xJf Python-3.9.0.tar.xz
$ ls -l
total 18428
drwxr-xr-x 17 ec2-user ec2-user 4096 Oct 5 2020 Python-3.9.0
-rw-r--r-- 1 root root 18866140 Oct 5 2020 Python-3.9.0.tar.xz
$ cd Python-3.9.0
$ ./configure --enable-optimizations
$ make altinstall
..(중략)..
Successfully installed pip-20.2.3 setuptools-49.2.1
# 환경변수 경로 설정
# `$PATH:/usr/local/bin` 경로를 `PATH` 라는 환경변수로 선언
$ export PATH=$PATH:/usr/local/bin
# pip 업그레이드
$ pip3.9 install --upgrade pip
# Piilow 설치
$ pip3.9 install pillow
Successfully installed pillow-9.5.0
# 내가 파이썬에 설치한 패키지 확인
$ cd /usr/local/lib/python3.9/
$ ls -l site-packages/
total 24
-rw-r--r-- 1 root root 126 Dec 7 04:26 easy_install.py
drwxr-xr-x 3 root root 4096 Dec 7 04:35 PIL # Pillow 패키지 설치 확인
drwxr-xr-x 2 root root 135 Dec 7 04:35 Pillow-10.1.0.dist-info
drwxr-xr-x 2 root root 4096 Dec 7 04:35 Pillow.libs
drwxr-xr-x 5 root root 136 Dec 7 04:27 pip
drwxr-xr-x 2 root root 166 Dec 7 04:27 pip-23.3.1.dist-info
drwxr-xr-x 5 root root 73 Dec 7 04:26 pkg_resources
drwxr-xr-x 2 root root 41 Dec 7 04:26 __pycache__
-rw-r--r-- 1 root root 119 Dec 7 04:26 README.txt
drwxr-xr-x 7 root root 4096 Dec 7 04:26 setuptools
drwxr-xr-x 2 root root 187 Dec 7 04:26 setuptools-49.2.1.dist-info
# /home/ec2-user/lambda_layers/python/lib/python3.9/site-packages 경로에 있는 모든 디렉토리가 존재하지 않을 경우,
# 필요한 모든 상위 디렉토리를 생성하고 마지막으로 site-packages 디렉토리를 생성
$ mkdir -p /home/ec2-user/lambda_layers/python/lib/python3.9/site-packages
# site-packages 디렉토리를 /home/ec2-user/lambda_layers/python/lib/python3.9/에 복사
$ cp -r site-packages/ /home/ec2-user/lambda_layers/python/lib/python3.9/
# lambda_layers 디렉토리로 이동
$ cd /home/ec2-user/lambda_layers/
# zip 파일 만들기
$ zip -r lambda_layers.zip *
# zip 파일 확인
$ ls -l
total 9444
-rw-r--r-- 1 root root 9670607 Dec 7 04:39 lambda_layers.zip
drwxr-xr-x 3 root root 17 Dec 7 04:37 python
Bitvise SSH Client > New terminal console
$ aws configure
AWS Access Key ID [None]: 사용자의 엑세스 키
AWS Secret Access Key [None]: 사용자의 비밀 엑세스 키
Default region name [None]: us-west-2
Default output format [None]: json
엑세스 키가 없거나 잊어버린 경우 IAM에서 기존 엑세스 키를 삭제하고 다시 만들자.
IAM > 사용자 > 계정 클릭 > 권한 탭 > 권한 정책
AmazonS3FullAccess
권한이 필요. 없는 경우 권한 추가해주기.
참조
https://docs.aws.amazon.com/cli/latest/reference/s3api/create-bucket.html
Bitvise SSH Client > New terminal console
$ aws s3api create-bucket --bucket lambda-layer-bucket-020 --region us-west-2 --create-bucket-configuration LocationConstraint=us-west-2
{
"Location": "http://lambda-layer-bucket-020.s3.amazonaws.com/"
}
Bitvise SSH Client > New SFTP window
lambda_layers.zip 로컬로 가져오기
S3 > 버킷 > lambda-layer-bucket-020 클릭 > 객체 탭 > lambda_layers.zip 파일 업로드
객체 URL 확인
S3 > 버킷 > lambda-layer-bucket-020 클릭 > 객체 탭 > lambda_layers.zip 클릭 > 속성
Lambda > 계층 > 계층생성
계층 확인
Lambda > 함수 > MakeThumbnailImage-020 > 코드 탭 > 계층 항목에 레이어 추가 버튼 클릭
생성해둔 계층 추가
계층 추가 확인
람다 함수 MakeThumbnailImage-020
를 실행하면 403 Forbidden 에러가 발생하는 것을 확인할 수 있다. 왜? 권한이 없으니까.
권한을 부여하기 전 람다 함수의 역할을 확인하자
역할 이름을 클릭해서 이 역할에 AmazonS3FullAccess
권한을 추가한다.
버킷에 다운받을 이미지 업로드 해놓기
람다 함수 MakeThumbnailImage-020
의 테스트 이벤트 수정
(업로드버킷이름-resized)
S3 > 버킷 > 버킷 만들기
람다 함수 MakeThumbnailImage-020
실행시키면 정상적으로 실행된 것을 확인할 수 있다.
썸네일 저장해주는 버킷인 lambda-layer-bucket-020-resized
확인
Lambda > 함수 > MakeThumbnailImage-020
클릭 > +트리거 추가 버튼 클릭
트리거 설정 후 추가
생성된 트리거 확인
S3 > 버킷 > lambda-layer-bucket-020
클릭 > 이미지 파일 업로드
람다 함수 MakeThumbnailImage-020
의 로그 확인
CloudWatch > 로그 그룹 > /aws/lambda/MakeThumbnailImage-020
클릭 > 로그 스트림 탭
S3 > 버킷 > lambda-layer-bucket-020-resized
버킷 확인
이미지 파일을 업로드 시키자 썸네일을 만들어주는 함수가 자동으로 실행되었다는 것을 확인할 수 있다.
MyEC2ForPILBuild-020
인스턴스 삭제$ cd /aws
$ mkdir hello-lambda
$ cd hello-lambda
# 현재 디렉토리에 pymysql 설치
$ pip install pymysql --target .
$ code .
lambda_function.py 파일 생성
import pymysql
def lambda_handler(event, context):
ret = []
db = pymysql.connect(host='database-020.cgswf9z3mjij.us-west-2.rds.amazonaws.com', user='root', db='sampledb', password='password', charset='utf8')
curs = db.cursor()
sql = "select * from emp";
curs.execute(sql)
rows = curs.fetchall()
for e in rows:
temp = {'empno':e[0],'name':e[1],'department':e[2],'phone':e[3] }
ret.append(temp)
db.commit()
db.close()
return ret
cmd창
$ tar -acf ..\lambda_function.zip .
$ dir ..\lambda_function.zip
C 드라이브의 볼륨에는 이름이 없습니다.
볼륨 일련 번호: 7CA3-675B
C:\aws 디렉터리
2023-12-07 오후 04:20 133,815 lambda_function.zip
1개 파일 133,815 바이트
0개 디렉터리 97,860,091,904 바이트 남음
Lambda > 함수 > 함수 생성
람다 함수에 lambda_function.zip 파일 업로드
lambda_function.zip 파일 업로드 확인
이대로 Test를 하면 에러가 난다. 왜? 연결이 3초 동안 이뤄지지 않으면 타임아웃되도록 설정되어있기 때문에
RDS와 연결하는데 시간이 걸리는 것으로 추정되므로 타임아웃 시간을 적절하게 늘려주도록 한다.
람다 함수 > 구성 탭 > 일반 구성 > 제한 시간 > 60초로 수정
제한 시간을 60초로 수정했지만 RDS에 연결하는데 문제가 있는 듯 에러 메세지가 발생된다.
RDS와 람다 함수를 연결하기 위해서는 동일한 VPC에 있어야 한다.
람다 함수에 VPC에 연결하려면 AWSLambdaVPCAccessExecutionRole
권한이 필요하다.
람다 함수를 RDS가 연결되어 있는 VPC에 연결한다.
여기서 보안 그룹은 크게 중요치 않다. Default로 설정해도 된다.
이후 RDS에서 람다 함수 연결에서 추가되는 보안 그룹이 중요하다.
RDS를 람다 함수에 연결한다.
RDS와 람다 함수가 같은 VPC에 존재하기 때문에 선택이 가능하다.
람다 함수에서 코드 소스 > Test 시 에러없이 실행되는 것을 확인할 수 있다.
람다 함수를 외부에서 호출, 실행할 수 있도록 설정하려면 REST API를 구축해야한다.
REST API 생성
API Gateway > API > API 생성
리소스 생성
메서드 생성
메서드 테스트 실행
메서드 테스트 실행 결과 확인
로그 분석
로그
Execution log for request 64aa2df1-c767-4f4b-ad5b-1643ea84d449
Thu Dec 07 08:02:11 UTC 2023 : Starting execution for request: 64aa2df1-c767-4f4b-ad5b-1643ea84d449
Thu Dec 07 08:02:11 UTC 2023 : HTTP Method: GET, Resource Path: /emp
Thu Dec 07 08:02:11 UTC 2023 : Method request path: {}
Thu Dec 07 08:02:11 UTC 2023 : Method request query string: {}
Thu Dec 07 08:02:11 UTC 2023 : Method request headers: {}
Thu Dec 07 08:02:11 UTC 2023 : Method request body before transformations:
Thu Dec 07 08:02:11 UTC 2023 : Endpoint request URI: https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:750300976708:function:LambdaFunctionWithRDS-020/invocations
Thu Dec 07 08:02:11 UTC 2023 : Endpoint request headers: {x-amzn-lambda-integration-tag=64aa2df1-c767-4f4b-ad5b-1643ea84d449, Authorization=***********************************************************************************************************************************************************************************************************************************************************************************************************a2c419, X-Amz-Date=20231207T080211Z, x-amzn-apigateway-api-id=3y5x36n6j5, X-Amz-Source-Arn=arn:aws:execute-api:us-west-2:750300976708:3y5x36n6j5/test-invoke-stage/GET/emp, Accept=application/json, User-Agent=AmazonAPIGateway_3y5x36n6j5, X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDgaCXVzLXdlc3QtMiJHMEUCIAMpuU6Js52ORJi0GlIfXZ1IXG/a6DAtlrgBGe0lzp/+AiEA/WexO5a4VochCv/njtOX8NXvrJKzP/ajvHR2SGdYKGcqugUIof//////////ARAFGgwxMDkzNTEzMDk0MDciDJMHFrTcDpJvHCfuXCqOBYbSqqgdNEQ5CLWN3UwvckjF2es0LQCWrVRrxHlVc6fD6T/7uSaSL/i+O+WxzwXrPNwzjfjer4XolouGvenGMLvukblOd2pZM8Lbv5O29Wz2gv0vnzx5e0xspTfxtdZfX1blh9v5GaWb4QBIKdCGQyp9oqcLkXQjSAjDa1 [TRUNCATED]
Thu Dec 07 08:02:11 UTC 2023 : Endpoint request body after transformations:
Thu Dec 07 08:02:11 UTC 2023 : Sending request to https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:750300976708:function:LambdaFunctionWithRDS-020/invocations
Thu Dec 07 08:02:11 UTC 2023 : Received response. Status: 200, Integration latency: 450 ms
Thu Dec 07 08:02:11 UTC 2023 : Endpoint response headers: {Date=Thu, 07 Dec 2023 08:02:11 GMT, Content-Type=application/json, Content-Length=312, Connection=keep-alive, x-amzn-RequestId=5ca225c8-5bc0-4334-9d8b-ceb4fe2f602a, x-amzn-Remapped-Content-Length=0, X-Amz-Executed-Version=$LATEST, X-Amzn-Trace-Id=root=1-65717c03-7b734d0b6e98a7b32a317aad;sampled=0;lineage=2954aa31:0}
Thu Dec 07 08:02:11 UTC 2023 : Endpoint response body before transformations: [{"empno": "1", "name": "\uccab\ubc88\uc9f8", "department": "\ubcf8\uc0ac", "phone": "010-0000-0001"}, {"empno": "2", "name": "\ub450\ubc88\uc9f8", "department": "\uc9c0\uc0ac", "phone": "010-0000-0002"}, {"empno": "3", "name": "\uc138\ubc88\uc9f8", "department": "\ud611\ub825\uc0ac", "phone": "010-0000-0003"}]
Thu Dec 07 08:02:11 UTC 2023 : Method response body after transformations: [{"empno": "1", "name": "\uccab\ubc88\uc9f8", "department": "\ubcf8\uc0ac", "phone": "010-0000-0001"}, {"empno": "2", "name": "\ub450\ubc88\uc9f8", "department": "\uc9c0\uc0ac", "phone": "010-0000-0002"}, {"empno": "3", "name": "\uc138\ubc88\uc9f8", "department": "\ud611\ub825\uc0ac", "phone": "010-0000-0003"}]
Thu Dec 07 08:02:11 UTC 2023 : Method response headers: {X-Amzn-Trace-Id=Root=1-65717c03-7b734d0b6e98a7b32a317aad;Sampled=0;lineage=2954aa31:0, Content-Type=application/json}
Thu Dec 07 08:02:11 UTC 2023 : Successfully completed execution
Thu Dec 07 08:02:11 UTC 2023 : Method completed with status: 200
API 배포
API Gateway의 Endpint로 접근
웹 브라우저 주소창에 URL(Endpoint) 입력 + /emp(리소스)
API Gateweay 주소를 호출하도록 리액트 수정
import { useEffect, useState } from 'react';
import './App.css';
import axios from 'axios';
function App() {
// 상태변수를 정의
const [emps, setEmp] = useState([]);
// 화면이 최초로 출력되었을 때(=마운트될 때) 동작을 정의
useEffect(() => {
// axios.get('http://localhost:80/emp01')
// 플라스크 서버가 동작하고 있는 EC2 인스턴스의 퍼블릭 주소로 변경
// axios.get('http://3.38.179.183:80/emp01')
// API Gateway의 엔드포인트로 변경
axios.get('https://3y5x36n6j5.execute-api.us-west-2.amazonaws.com/prod/emp')
.then(res => {
console.log(res); // 응답 내용을 콘솔에 출력
setEmp(res.data); // 응답 내용 중 data 항목을 상태변수 emps에 설정
})
.catch(err => console.log(err));
}, []);
return (
<table>
<thead>
<tr>
<th>사번</th>
<th>이름</th>
<th>부서</th>
<th>전화번호</th>
</tr>
</thead>
<tbody>
{ // 상태변수의 내용을 화면에 출력
emps.map((emp, index) => (
<tr key={index}>
<td>{emp.empno}</td>
<td>{emp.name}</td>
<td>{emp.department}</td>
<td>{emp.phone}</td>
</tr>
))
}
</tbody>
</table>
);
}
export default App;
로컬에서 리액트 서버 실행 후 결과 확인
$ npm start
CORS 오류가 발생했다는 것을 확인할 수 있다.
API Gateway에서 CORS 활성화
API Gateway > API > 리소스 > /emp 선택 > CORS 활성화
API 재배포
로컬에서 리액트 페이지 확인
데이터가 정상적으로 나타나는 것을 확인했다면 리액트 코드를 빌드한다.
$ npm run build
빌드 결과를 S3 버킷에 업로드
리액트 서버 버킷의 엔드포인트로 접근
개발자 도구 > Network 확인 시 S3 버킷으로 리액트 코드를 요청 → 리액트 코드에서 axios를 이용해 API Gateway로 emp 리소스를 요청하는 것을 확인할 수 있다.
인스턴스 삭제 시 시스템 스냅샷 및 특정 시점으로 복구를 포함한 자동화된 백업을 더 이상 사용할 수 없다는 점을 인정합니다.
항목만 체크 후 삭제