![article thumbnail image](https://blog.kakaocdn.net/dn/1mPfQ/btrDWiV7Vu8/pW4Ql6Zpr0AwaiDfsgibsk/img.png)
개요
미디어라이브를 AWS SDK를 이용해서 채널을 생성했을 때 발생하는 시점 문제 그 시점을 어떻게 다룰지 그리고 이벤트를 어떻게 핸들링 하는지에 대해 정리했습니다.
문제 상황
간략하게 워크플로우를 설명하면 다음과 같다.
동작 순서
- 사용자가 프론트 페이지에서 미디어라이브의 채널을 생성한다.
- 채널이 생성중이다가 생성이 완료되면 바로 시작한다.
- 채널이 러닝상태가되면 라이브가 송출되기 시작한다.
여기서 문제점은 채널을 생성하고 생성이 완료되는 시점까지에 있다.
AWS MediaLive를 이용해서 채널을 생성하면 최초 CREATING 상태에서 IDLE상태가 되는데 최소 1분에서 길면 2~3분까지도 걸린다.
이 말은 즉, Node.js에서 해당 채널을 생성하는데 동기적으로 기다리면 채널을 생성해달라는 요청 한 번으로 약 3분간 기다리고 있는 상황이 된다.
비동기가 강점인 Node.js에서 이벤트 루프를 멈추게 하는건 절대적으로 지양해야 하는 일이며 3분은 너무 긴 시간이여서 해결할 다른 방법이 필요했다.
구현해야 할 로직의 워크플로우는 다음과 같다.
워크 플로우
- 사용자가 프론트 페이지에서 미디어라이브의 채널을 생성한다.
- 미디어라이브에서는 채널이 생성되기 시작하고 CREATING상태가 된다.
- 약 1~3분이 걸린 후 생성이 완료되고 IDLE상태로 전환된다.
- 채널이 생성이 완료되고 IDLE 상태가 되었다는 이벤트를 캐치해서 채널을 시작한다.
아이디어
- aws sdk의 medialive class의 waitFor 함수 이용하기
- 채널이 특정 상태가 될 때까지 기다리는 함수
- 비동기로 동작시킬 방법을 찾으며 여러가지 실험을 했지만 동기적으로밖에 동작이 되지 않아서 사용할 수 없음
- aws lambda를 이용해 외부에서 이벤트 핸들링하기
- 빠른 실시간성이 요구될 경우 람다의 콜드 스타트 시간이 문제가 될 수 있음
1번의 경우 비동기로 동작시킬 방법이 없어서 후보에서 제외했고 2번 방법으로 설계를 해봤다.
자료를 조사해보니 AWS에는 약 200개가 넘는 서비스의 이벤트 및 사용자 정의 이벤트를 만들어서 수신이 가능한 AWS EventBridge라는 서비스가 있었다.
이벤트 브릿지에서 여러 대상 서비스로 트리거를 거는 것이 가능했고 그 서비스 중 람다도 있었다.
여기까지 도달한 결과 어떻게 해결할지 어느정도 윤곽이 잡혔다.
간단하게 정리하면 이벤트 브릿지에서 미디어라이브의 채널 이벤트를 수신할 규칙을 생성하고 해당 규칙에 트리거되는 대상을 람다 함수로 지정하면 원하는 부분이 모두 해결될 것으로 예상됐다.
설계가 어느정도 구상이 됐으니 이제 구현을 해보자.
AWS EventBridge 규칙 생성하기
이벤트 규칙의 이름과 간단한 설명을 적는다.
이벤트 패턴 작성
다음은 이벤트 패턴에 대한 정보를 입력한다.
이벤트브릿지는 두 가지 타입이 있다.
하나는 위에서 설명한대로 각 서비스에 대해서 특정 이벤트에 대한 규칙을 생성하는 것과 나머지 하나는 리눅스의 크론과 같은 형태로 스케줄러를 이용할 때 사용하는 일정 타입이 있다.
현재는 이벤트 패턴을 선택한다.
서비스 공급자는 AWS를 선택하고 서비스 이름은 MediaLive로 지정한다.
이벤트 유형에는 다양한 이벤트가 존재하지만 지금 상황에서는 채널의 상태가 CREATING에서 IDLE로 변경되는 시점을 캐치해야 하는 상황이여서 MediaLive Channel State Change
유형으로 선택했다.
대상 선택
다음은 위에서 정의한 이벤트가 감지되었을 때 어떤 대상을 호출할지를 정한다.
두 번째 이미지는 어떤 서비스를 대상으로 호출이 가능한지에 대한 부분이다.
AWS Lambda를 사용할것이므로 Lambda 함수를 선택한다.
Lambda함수를 선택하고 나면 하단의 리스트 메뉴가 새로 나오게되는데 현재 접속된 계정에 만들어진 Lambda 함수의 리스트가 나열되는데 거기서 어떤 함수에 지정할지를 고르면 된다.
AWS Lambda 함수 작성하기
위에서 이벤트 규칙을 생성했으니 규칙에서 대상을 지정해 호출할 람다 함수를 만들어야 한다.
일반적으로 람다 함수는 AWS 콘솔 상에서 직접 코드를 작성해서 만드는 것과 로컬에서 개발해서 압축해서 올리는 방법이 있다. 하지만 이 두 방법은 제한이 많고 유지보수가 굉장히 어렵고 불편하다.
람다 함수를 보다 편하고 효율적으로 관리하는 방법으로는 몇가지 도구를 이용하면 좋다.
- cloudia.js
- serverless framework
클라우디아는 람다와 API Gateway에 쉽게 배포하기 위한 도구로 과거에 람다를 처음 사용했을 때 써봤는데 cli기반으로 쉽게 만들어저있다. 하지만 기능이 제한적이고 cli로만 관리를 해야해서 프로덕션 레벨에서 사용하기엔 부적합하다.
두 번째로는 서버리스 프레임워크인데 대다수는 서버리스 프레임워크로 람다를 다룬다.
AWS Lambda만 지원하는게 아닌 각종 클라우드들의 서버리스 제품에 대해서 지원을 해준다. 지원되는 기능도 많고 훨씬 안정적이면 여러 사람이 협업할 때도 구조적이며 체계적으로 관리할 수 있게 해주는 프레임워크다.
필자는 서버리스 프레임워크를 이용해 진행해보려 한다.
서버리스 프레임워크 설치
서버리스 프로젝트 생성
커스텀하게 모두 직접 설정할 것이기때문에 Starter Project로 선택하고 만들었다.
필자는 이 상황에 이미 모두 진행한 부분이여서 Dashboard 선택 여부나 AWS Credentials 설정하는 부분은 No를 선택해서 스킵했다.
최초로 프로젝트를 생성하고 열어보면 다음과 같이 간단하게 샘플이 만들어저있다.
패키지 설치
serverless framework를 이용하면 npm에서 각종 패키지를 모두 설치해서 로직을 작성하고 배포할 때 node_modules도 같이 포함해서 압축되어 배포하는 것을 도와준다.
일단 현재 포스트에서는 aws-sdk만 있어도 충분하니 아래 패키지 하나만 설치한다.
npm init -y
npm i aws-sdk
handler 수정
먼저 핸들러 함수의 파일을 루트 경로에 두지 않고 폴더 구조를 먼저 잡는다.
다음 포스팅에서 썸네일쪽에서 사용할 함수를 추가할 것이기에 미리 구조를 잡아놓는다.
위에서의 문제점은 채널이 생성되는 시간이 오래걸리는 상황에서 시점을 어떻게 다루고 바뀐 상태에 대한 이벤트를 어떻게 캐치하냐의 문제였지 로직이 복잡한 문제는 아니였다.
단순하게 aws-sdk를 사용해서 채널을 시작하면 된다.
이벤트가 발생하면 아래의 샘플 데이터의 형태로 들어오게 된다.
아래 데이터에는 나와있지 않지만 샘플이 아닌 실제로 데이터를 받아서 확인을 해보면 detail
객체에 channel_arn
이란 값이 들어와서 해당 값을 이용해서 채널 아이디를 특정해낼 수 있다.
event sample data
{
"version": "0",
"id": "faff4b2f-4ec9-53b6-ecd0-a53370b1c088",
"detail-type": "MediaLive Channel Alert",
"source": "aws.medialive",
"account": "123456789012",
"time": "1970-01-01T00:00:00Z",
"region": "us-east-1",
"resources": ["arn:aws:medialive:us-east-1:123456789012:channel:123456"],
"detail": {
"alert_type": "Stopped Receiving UDP Input",
"alarm_state": "set",
"message": "Stopped receiving network data on [rtp://:5000]",
"pipeline": "1"
}
}
const AWS = require("aws-sdk");
const mediaLive = new AWS.MediaLive({ region: "ap-northeast-2" });
const handler = async (event) => {
try {
console.log(JSON.stringify(event));
const state = event.detail.state;
console.log(`state: ${state}`);
switch (state) {
case 'CREATING':
break;
case 'CREATED':
break;
case 'IDLE':
console.log("start create channel");
const channelId = event.detail.channel_arn.split("channel:").pop();
const channel = await mediaLive.startChannel({ ChannelId: channelId }).promise();
console.log(`channelId: ${channelId}`);
console.log(JSON.stringify(channel));
break;
case 'RUNNING':
break;
case 'DELETING':
break;
default:
break;
}
const response = {
statusCode: 200,
body: JSON.stringify(channel),
};
return response;
} catch (err) {
console.log(JSON.stringify(err));
const response = {
statusCode: 500,
body: JSON.stringify(err),
};
return response;
}
};
module.exports.handler = handler;
serverless.yml 수정하기
실제로는 환경 변수로 각종 수많은 값들을 넣어놨지만 다음은 예시일 뿐이기 때문에 모두 제거하고 심플한 형태로 진행한다.
serverless.yml
service: medialive-lambda
frameworkVersion: "3"
custom:
iam: "YOUR-IAM-HERE"
securityGroup: "YOUR-SECURITY-GROUP"
subnet: "YOUR-SUBNET"
provider:
name: aws
runtime: nodejs14.x
region: ap-northeast-2
iam:
role: ${self:custom.iam}
functions:
changeChannelStatus:
handler: src/channel/changeChannelState.handler
name: medialive-channel-change-handler
environment:
env: ${opt:stage}
timeout: 60
vpc:
securityGroupIds:
- ${self:custom.securityGroup}
subnetIds:
- ${self:custom.subnet}
events:
- eventBridge:
pattern:
source:
- aws.medialive
detail-type:
- MediaLive Channel State Change
위 설정에서 중요하게 봐야할 부분은 functions 하위에 changeChannelStatus 함수의 섹션이다.
파라미터 정리
- handler
- 어떤 경로의 파일의 핸들러를 지정할지에 대한 부분
- name
- 람다 함수의 이름
- environment
- 환경변수를 등록
- vpc
- 대부분 프로덕션 환경에서는 서버 자원들이 vpc 환경 내부에 있으므로 보안그룹과 서브넷 등 각종 설정을 해줌
- events
- 해당 항목이 이벤트브릿지와 연결하는 옵션으로 어떤 이벤트의 서비스를 지정할지 패턴과 디테일 타입을 입력한다.
- 추가적인 옵션을 원할 경우 공식 문서에서 확인하면 된다.
- Serverless Framework - AWS Lambda Events
배포하기
serverless.yml과 핸들러에 로직을 모두 작성했으면 이제 배포만 하면 된다.
다음 스크립트에서 stage
옵션은 인자로 전달하는 값을 serverless.yml
에서 ${opt.stage}
로 접근이 가능하다.
serverless deploy --stage develop
serverless deploy --stage production
결과 로그
> serverless deploy --stage develop
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Installing dependencies for custom CloudFormation resources...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service medialive-lambda.zip file to S3 (19.96 MB)...
Serverless: Uploading custom CloudFormation resources...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
..........................................................................................................................
Serverless: Stack update finished...
Service Information
service: medialive-lambda
stage: develop
region: ap-northeast-2
stack: medialive-lambda-develop
api keys:
None
endpoints:
POST - https://1234123123123123123.execute-api.ap-northeast-2.amazonaws.com/production/metadata
functions:
changeChannelStatus: changeChannelStatus
layers:
None
Serverless: Removing old service artifacts from S3...
Finished: SUCCESS
배포가 성공적으로 완료되면 채널을 생성하면서 이벤트를 발생시켜 클라우드왓치에서 결과 로그를 확인하면 된다.
'AWS' 카테고리의 다른 글
AWS S3를 파일 시스템에 마운트하기 (goofys) (0) | 2022.06.20 |
---|