마이크로 서비스 아키텍처(MSA)의 핵심 기술인 서버리스 환경 구성은 여러 장점도 존재하지만, 단점 역시 있습니다. 그중 Lambda의 고질적인 문제로 지적받는 콜드 스타트, 쓰로틀링을 제외하고서라도 복잡한 워크플로에 대한 처리가 있는데요. 병렬, 재시도, 동기화 등 다양한 플로우에 대해 Lambda만으로는 처리가 굉장히 복잡해지고 마이크로 서비스를 실천하기도 어려워집니다. 최근 프로젝트를 진행하면서 처음엔 비동기적이던 lambda 플로우를 처리하기 위해 다음과 같은 방법을 썼습니다.
S3 trigger로 Lambda 실행하기
S3의 버킷에 파일이 올라가면 lambda가 실행되고, 그 lambda가 실행이 끝날때 또 S3에 더미 파일을 올리면서 트리거가 되는 식으로 플로우를 짰습니다. 파일이 올라갈 때 lambda가 한 개만 실행되면 괜찮은 방법입니다. 하지만 보통 그렇게 간단하지 않죠. 1개 파일에 1개 lambda로만 구성되도록 아키텍처를 짜면 lambda가 여러 일을 수행하면서 과도하게 무거워지고, MSA의 의미도 사라집니다.
게다가 이 방법은 병렬처리가 불가능합니다. S3 event trigger의 원칙 중 하나는 event는 분리되어야 한다는 겁니다. 즉, 버킷의 new/ 폴더에 객체 생성 이벤트로 두 개의 lambda를 실행할 수 없습니다. new/a- , new/b-로 파일에 접두사를 붙여 다른 lambda를 실행하는 건 가능하지만 역시 워크플로가 복잡해지는 건 마찬가지입니다.
또한 S3 트리거를 실행시키기 위해 더미 데이터를 올리게 되면서 쓸데없는 데이터 누적이 됩니다. 파일 하나하나 다 비용인데,, 물론 객체 생성 후 기한이 지나면 Glacier에 옮기는 방법이 있지만 역시 불필요한 행동들입니다.
Step Function으로 lambda 워크플로 구성하기
S3 트리거로 구성하던 워크플로우를 Step Function으로 아주 간단하고 직관적이게 구현할 수 있습니다. Step Function은 lambda와 같은 주요 AWS 서비스를 모듈로 하는 블록을 끼워맞추듯이 아키텍처를 구성하면 자동으로 병렬 처리, 연속, 반복 등을 아키텍처에 맞게 구성해 주고, 입력과 출력을 각각의 lambda가 연결되도록 해주고 플로우 도중 오류 처리도 편리하게 시각화해주는 서비스입니다.
다음과 같이 최소한의 역할을 수행하는 lambda를 작성하고, 흐름에 맞게 아키텍처를 구성해주면 유지보수 측면에서 아주아주 큰 도움이 됩니다. 동기적으로 수행하려고 한개의 lambda에 여러 기능을 때려 넣으면 오류 디버깅도 힘들뿐더러 확장성도 낮아집니다.
확장성 측면에서 위와 같은 플로우차트를 확대시키는 경우 엄청난 효과를 볼 수 있습니다.
여러까지 까다로운 병렬 비동기처리를 깔끔하게 재구성할 수 있게 됩니다. 만약 lambda-stepfunction을 쓰지 않고 ec2에 nodejs백엔드 코드를 올리는 전통적인 방법을 썼으면.... 정말 대공사가 필요했겠죠.
더불어 '한 개의 lambda = 한 개의 역할' 공식을 더 잘지켜야 stepfunction 활용도가 높아집니다. 더욱더 잘게 쪼갤수록 잘 쓸 수 있게 되죠. 더불어 MSA의 철학과도 맞으니 쪼갤 수 있다면 더 쪼개면 좋습니다.
또 다음과 같은 아키텍처는 협업에도 매우 좋습니다. 백엔드를 여러명이 작업한다면 옛날에는 서로 다른 런타임 환경에서 작업을 하는 건 상상도 못 했지만 나는 nodeJS로 lambda를 작성하고 팀원은 python이나 java로 lambda를 쓰는 일 모두 가능합니다. 혹은 docker image로 구성한 작업을 lambda에 올리는 일도 가능해지죠.
S3 객체 생성 이벤트로 step function 실행 시키기
앞서 step function을 실행시키는 건 아주 간단하다고는 했지만 초반에 삽질을 많이 했습니다..🥲 S3 객체 생성 이벤트를 EventBridge로 Step Function을 호출하도록 구성하려 했는데 AWS 공식문서를 보고 다른 자료를 찾고, 아무리 테스트해도 Step Function이 실행되지 않아 다른 방법을 썼습니다.
다른 방법은 S3 객체 생성 이벤트로 lambda를 실행시키고 lambda에서 step function를 트리거하는 방법입니다. S3 트리거로 lambda를 실행시키는 건 해봤고, AWS sdk로 stepfunction을 execute 시키는 함수도 지원이 되니, step function을 실행시키는 lambda를 작성할 수 있었습니다. 그 코드는 다음과 같습니다.
var AWS = require('aws-sdk');
var stepfunctions = new AWS.StepFunctions({apiVersion: '2016-11-23'});
exports.handler = async (event) => {
try{
const bucket = event.Records[0].s3.bucket.name;
const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
const scoreID = decodeURIComponent(event.Records[0].s3.object.eTag.replace(/\+/g, ' '));
let input={};
input.key=key;input.scoreID=scoreID;input.bucket=bucket;
let params = {
stateMachineArn: 'arn:aws:states:ap-northeast-2:YOUR_AWS_ID:stateMachine:YOUR_STEPFUNC_NAME',
input: JSON.stringify(input),
};
const data = await stepfunctions.startExecution(params).promise();
console.log("data= " + data);
const response = {
statusCode: 200,
body: JSON.stringify(data),
};
return response;
}catch(err){
return {
statusCode : 500,
body : JSON.stringify(err)
};
}
};
startExecution함수는 stepfunction을 실행만 시키는 것이지, 해당 상태 머신이 끝날때까지 대기하는 실행이 아니므로 이 lambda도 1~2초 안에 종료되고 상태머신 실행시간 동안 lambda가 추가 지출되는 걱정도 필요 없습니다.
StepFunction 상태 머신의 실행과 오류 처리
상태 머신은 도중 에러가 나면 즉각 다른 실행 중인 lambda가 있다면 중지시킵니다. 에러 메시지도 바로 볼 수 있으며 이미 실패한 실행에서의 불필요한 비용을 줄여 도움을 줍니다. lambda와 연결된 cloudwatch 로그 그룹도 연결시켜줘서 해당 콘솔에서 내용을 바로 볼 수 있으니 디버깅도 간편해지죠.
'🐳AWS' 카테고리의 다른 글
Notion API 활용하기 - (3) AWS Lambda와 Notion API 연동하기 (0) | 2022.06.07 |
---|---|
[AWS] Lambda위에 자체 환경(Ubuntu)의 docker 컨테이너 올리기 (0) | 2021.11.23 |
[AWS] lambda에서 chrome-selenium 크롤링 환경 설정하기 (2) | 2021.08.26 |
[AWS][테라폼] 🐬 테라폼으로 AWS EC2 인스턴스 생성,삭제하기 (0) | 2021.06.19 |
[AWS] 나의 클라우드 아키텍처 입문기 (0) | 2021.06.16 |