Zero To One
S3, Lambda, SQS 본문
목표
- S3 이벤트가 SQS로 전송되게 만들고, SQS로부터 이벤트를 받아 람다가 실행시키게 한다
메세지큐를 쓰는 이유
- 소스에서 처리속도가 빠르고, 타켓에서 처리속도가 느린경우 타켓에서의 처리속도를 따라가게 하기 위해 큐를 둬서 이벤트 들을 하나씩 쌓아놓고 하나씩 처리하게 하기 위해서- 예시) 컴퓨터와 프린터
아키텍처
시작
1.
> sam init
You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.
Which template source would you like to use?
1 - AWS Quick Start Templates
2 - Custom Template Location
Choice: 1
Choose an AWS Quick Start application template
1 - Hello World Example
2 - Multi-step workflow
3 - Serverless API
4 - Scheduled task
5 - Standalone function
6 - Data processing
7 - Infrastructure event management
8 - Machine Learning
Template: 5
Which runtime would you like to use?
1 - dotnetcore3.1
2 - nodejs14.x
3 - nodejs12.x
Runtime: 2
Based on your selections, the only Package type available is Zip.
We will proceed to selecting the Package type as Zip.
Based on your selections, the only dependency manager available is npm.
We will proceed copying the template using npm.
Project name [sam-app]: image-resize-sqs
Cloning from https://github.com/aws/aws-sam-cli-app-templates (process may take a moment)
-----------------------
Generating application:
-----------------------
Name: image-resize-sqs
Runtime: nodejs14.x
Architectures: x86_64
Dependency Manager: npm
Application Template: quick-start-from-scratch
Output Directory: .
Next steps can be found in the README file at ./image-resize-sqs/README.md
Commands you can use next
=========================
[*] Create pipeline: cd image-resize-sqs && sam pipeline init --bootstrap
[*] Test Function in the Cloud: sam sync --stack-name {stack-name} --watch
2. src -> handlers -> hello-from-lambda.js
다음 코드의 출생은 https://mtou.tistory.com/111 에서 확인할 수 있다.
//기본 code
const AWS = require('aws-sdk');
AWS.config.update({region: 'ap-northeast-2'});
const s3 = new AWS.S3({apiVersion: '2006-03-01'});
const sharp = require('sharp')
exports.helloFromLambdaHandler = async (event, context) => {
console.log(JSON.stringify(event)) //JSON.stringify로 모든 로그가 찍힐 수 있게끔 한다
const sourceBucktName = event.Records[0].s3.bucket.name
const uploadFileName = event.Records[0].s3.object.key
const destinationBucktName = '타겟버킷'
console.log(sourceBucktName)
console.log(uploadFileName)
// 원본 버킷으로부터 파일 읽기
const s3Object = await s3.getObject({
Bucket: sourceBucktName,
Key: uploadFileName
}).promise()
// 이미지 리사이즈, sharp 라이브러리가 필요합니다.
const data = await sharp(s3Object.Body)
.resize(200)
.jpeg({ mozjpeg: true })
.toBuffer()
// 대상 버킷으로 파일 쓰기
const result = await s3.putObject({
Bucket: destinationBucktName,
Key: uploadFileName,
ContentType: 'image/jpeg',
Body: data,
ACL: 'public-read'
}).promise()
return 'Hello from Lambda!';
}
3.
sam build
sam deploy --guided
4. AWS SQS에 대기열 생성, 표시제한시간은 100초 이상으로 설정
5. S3에 버킷 생성 (ACL 활성화 및 모든 퍼블릭 엑세스 차단)
6. 만든 s3버켓 클릭 -> 이벤트알림 -> 이벤트알림생성
다음과 같이 생성하려면 오류가 뜬다.
6. 일단 다시 SQS로 들어와서 SQS와 람다를 연결한다
다음과 같은 오류가 뜬다....
함수의 실행권한이 없는 것이다.
7. 만든 람다 -> 구성 -> 권한 -> 실행역할 -> 역할이름(파란색 링크 클릭)
다음과 같이 추가해주자
그리고 다시 함수 트리거를 설정해주면 다음과 같이 잘 붙는다.
8. SQS -> 엑세스 정책 수정
정책을 다음과 같이 수정한다.
"Service" : "s3.amazonaws.com"만 추가해주면 된다.
{
"Version": "2008-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__owner_statement",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::485879423137:root",
"Service" : "s3.amazonaws.com"
},
"Action": "SQS:*",
"Resource": "arn:aws:sqs:ap-northeast-2:485879423137:s3-event-queue"
}
]
}
위 정책을 해석하면,
" s3-event-queue의 정책은 SQS의 모든 액션을 실행할 수 있다.
그 주체는 485879423137이다. s3.amazonaws.com 서비스가 SQS에서 실행할 수 있게 한다."
9. S3에 다시 돌아와서 변경사항 저장을 하면, 이벤트가 붙은 것을 볼 수 있다.
10. S3에 업로드를 하면, 타켓 버킷에 리사이즈 된 이미지가 안온다. 클라우드 워치를 이용해서 로그 기록을 확인해보자.
사진을 하나만 올렸는데 큐를 계속 보낸다. 오류를 해결하기 전에 잠깐 조취를 취해보자.
11. 대기열을 추가 생성 ->SQS에서 배달 못한 편지 대기열에서 편집을 눌러주자
활성화 후 s3-event-queue를 선택.
12. 소스S3에 다시 사진을 올려서 로그를 확인해보자
에러는 다음과 같이 2개가 나온다.
처음 나오는 에러
{
"Records": [
{
"messageId": "baea43ea-c55c-4030-8918-c9a35b3ae594",
"receiptHandle": "AQEBW/TlbE+Wpfpzc7i2slHqk1WKZ2KyZm0kfqxSZAJN94W7yHzRs/b/OLN6RwNjRCN+vSSuzpe7PFspEbWfTbxvSnHuKeyxVRgD0V5aDlmGLz5GR6biIghezRHwVcAKgUIuoBCus14wBNJ8Y7+FoaUt/sBg82HYfktWJtuHvTvbPquTmpNMzpat9wM6T0W3oMc9HGunbFAYxzknhV1GQSJkgz9oD+lU2E/jPiEnqkQZEhrgh7ZTd4RQD8BB+oxDd1ojwcg0HYjcAlEikGi/27UHGz5FJe5sfFU5rL5QyttLbagS5uXZpmsZB8j2PgkOyRm2O9jjD5WYjSKYhjg78bxhFH9g033FDp48Ek8aCkcEDehZ9AC4VDfGrvEn97nQ/S+Y56Hb1aOG9v4FNEhFaJnMqA==",
"body": "{\"Service\":\"Amazon S3\",\"Event\":\"s3:TestEvent\",\"Time\":\"2022-04-14T12:52:02.681Z\",\"Bucket\":\"s3-image-resize-sqs\",\"RequestId\":\"QAYPBCNNHF3QB2QQ\",\"HostId\":\"4t75X5MDNJ1rultDtRFxEDBF6ycBHZnhtgXcHLml8uLsSTDqKFNimcFLgKOGxWtopMWIVbi5ovk=\"}",
"attributes": {
"ApproximateReceiveCount": "12",
"SentTimestamp": "1649940722709",
"SenderId": "AIDAJCPXNKW2CHJMM46DC",
"ApproximateFirstReceiveTimestamp": "1649940722709"
},
"messageAttributes": {},
"md5OfBody": "2c1c9268490e1eab30c0fdd217c00af3",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:ap-northeast-2:485879423137:s3-event-queue",
"awsRegion": "ap-northeast-2"
}
]
}
두번째 나오는 에러
{
"errorType": "TypeError",
"errorMessage": "Cannot read property 'bucket' of undefined",
"stack": [
"TypeError: Cannot read property 'bucket' of undefined",
" at Runtime.exports.helloFromLambdaHandler [as handler] (/var/task/src/handlers/hello-from-lambda.js:9:49)",
" at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
]
}
세번째 나오는 에러
{
"Records": [
{
"messageId": "0b79d2bd-8db2-46fa-abaa-d47983b4d1ca",
"receiptHandle": "AQEBB+tDbKKc2XitdoDildFCXzWyEwNPurO1MV7o5z5nB94oAd01bo6n5LPtgTugZl0hqptuFeOtl+34fRpgVvsZFBhgh7o0LfapMAeWjN3HLoERhmSBiPUh/rdc2UwcGxFHcC1T5r8ZZh2+/4Y0qGoUhUMpLjvgJNLH7G+xuu/xzPywW0PE21IxHpzP27ogp6Uiy5MN+xTXTvL9fREnEr/5HHJPP4BdVncw82GdkzMG2dDVeg+op5E3udrXsR9nRIHhHmKroQ+sK0WT2GBP+/5kTuqXvMLQm21r4Fxekcn7qKQcjFF43QvSEa/Gb1x1ozJcYoGZQvB4/VQ3PKWPvYr2oENQg25rvgR4xnaNqJnkk+7iTMpmZUomdYvX9OJD2Q/4K/PkRIcVsLLrguSxVq3QPg==",
"body": "{\"Records\":[{\"eventVersion\":\"2.1\",\"eventSource\":\"aws:s3\",\"awsRegion\":\"ap-northeast-2\",\"eventTime\":\"2022-04-14T13:10:27.969Z\",\"eventName\":\"ObjectCreated:Put\",\"userIdentity\":{\"principalId\":\"AMAW5L9ADIVTO\"},\"requestParameters\":{\"sourceIPAddress\":\"116.125.64.98\"},\"responseElements\":{\"x-amz-request-id\":\"299J91MTX95D2Z6B\",\"x-amz-id-2\":\"C8QrQSEnjLAkQvIbGgistRxC0KRqxhfGDYsbcc56/z/hTBZWmYQgZ5cS5ISvHVmXZ8iMXuYs7IB5iRhFodmFxO8jn8eNjcwF\"},\"s3\":{\"s3SchemaVersion\":\"1.0\",\"configurationId\":\"image-event-sqs\",\"bucket\":{\"name\":\"s3-image-resize-sqs\",\"ownerIdentity\":{\"principalId\":\"AMAW5L9ADIVTO\"},\"arn\":\"arn:aws:s3:::s3-image-resize-sqs\"},\"object\":{\"key\":\"summer.jpg\",\"size\":2878872,\"eTag\":\"0fedc4356e5364746d677ae083b13299\",\"sequencer\":\"0062581D43D2E3929F\"}}}]}",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1649941829495",
"SenderId": "AIDAJCPXNKW2CHJMM46DC",
"ApproximateFirstReceiveTimestamp": "1649941829500"
},
"messageAttributes": {},
"md5OfBody": "57128a9d39f420543631bb9a49cf7f42",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:ap-northeast-2:485879423137:s3-event-queue",
"awsRegion": "ap-northeast-2"
}
]
}
처음 나오는 에러는 테스트이벤트 에러이고, 두번째 나오는 에러는 버켓을 찾을 수 없다고 뜨고, 세번째는 우리가 원하는 Event값이 이 들어가 있다.
개발자도구 -> 콘솔로그에서 테스트 이벤트 body값을 뽑아보자
테스트이벤트값은 필요없으므로 코드상으로 처리를 해주어야 한다.
Event : "s3:TestEvent"를 잡아주어야 한다.
// event는 sqs로부터 오는 이벤트
event = JSON.parse(event.Records[0].body)
console.log(JSON.stringify(event)) //JSON.stringify로 모든 로그가 찍힐 수 있게끔 한다
//테스트이벤트값 처리
if(event.Event === 's3:TestEvent'){
console.log('test event 입니다')
return;
}
그럼 완성된 코드는 다음과 같다.
Full Code
const AWS = require('aws-sdk');
AWS.config.update({region: 'ap-northeast-2'});
const s3 = new AWS.S3({apiVersion: '2006-03-01'});
const sharp = require('sharp')
exports.helloFromLambdaHandler = async (event, context) => {
// event는 sqs로부터 오는 이벤트
event = JSON.parse(event.Records[0].body)
console.log(JSON.stringify(event)) //JSON.stringify로 모든 로그가 찍힐 수 있게끔 한다
//테스트이벤트값 처리
if(event.Event === 's3:TestEvent'){
console.log('test event 입니다')
return;
}
//event는 s3로 오는 이벤트로 재설정
const sourceBucktName = event.Records[0].s3.bucket.name
const uploadFileName = event.Records[0].s3.object.key
const destinationBucktName = 'photo-destination-birdnine'
console.log(sourceBucktName)
console.log(uploadFileName)
// 원본 버킷으로부터 파일 읽기
const s3Object = await s3.getObject({
Bucket: sourceBucktName,
Key: uploadFileName
}).promise()
// 이미지 리사이즈, sharp 라이브러리가 필요합니다.
const data = await sharp(s3Object.Body)
.resize(200)
.jpeg({ mozjpeg: true })
.toBuffer()
// 대상 버킷으로 파일 쓰기
const result = await s3.putObject({
Bucket: destinationBucktName,
Key: uploadFileName,
ContentType: 'image/jpeg',
Body: data,
ACL: 'public-read'
}).promise()
return 'Hello from Lambda!';
}
로컬로 테스트를 해보자.
test-event.json 에는 테스트이벤트 로그를, real-event.json 에는 s3주소가 적혀있는 로그값을 넣는다.
sam build
sam local invoke -e ./test-event.json
테스트가 잘 됬다면 deploy를 하자
sam deploy
그리고 s3에 다시 사진을 넣어본다
그리고 다시 나는 에러
거의 다왔다...
구성 -> 권한 -> 실행역할 -> 역할이름(파란색 링크 클릭)
S3FullAccess권한을 넣는다
잘 들어가진다!
타겟 버킷에도 잘 들어가있다.
추가로....
람다가 잘 실행되면 SNS를 통해 이메일을 받아보자.
1. SNS -> 주제 생성
2. 표준으로 설정
3. 구독 -> 구독 생성
4. 이메일 선택
5. 이메일 인증
6. 람다로 돌아가서 대상추가
7. 성공하면 발송되게끔 설정해본다. (원래는 실패시 전송이 일반적이다)
8. 다시 소스 s3에 파일을 올리면 타겟s3에 resize된 사진이 올라오고, 메일에도 오는 것을 볼 수 있다.
'마이크로서비스' 카테고리의 다른 글
AWS 서버리스 사진첩 만들기 (0) | 2022.04.14 |
---|---|
AccessDenied: Access Denied 에러 해결 (0) | 2022.04.14 |
2) 마이크로서비스 정리 (0) | 2022.04.12 |
도메인 주도 설계 (코로나19 환자 관리 정보시스템 설계) (0) | 2022.04.08 |
마이크로서비스 정리 (0) | 2022.04.08 |